1 분 소요


◆ 문제 상황

다국어를 지원하는 검색 기능을 유지보수하던 중, 인도계 언어(벵갈어, 힌디어 등)의 검색 결과에서 글자가 깨지는 현상을 발견했다.

검색 결과 미리보기의 시작 부분에 모음기호만 단독으로 표시되는 것이었다.

...(모음기호) 나머지 텍스트...

처음에는 인코딩 문제를 의심했지만, 원인은 문자열을 잘라내는 방식에 있었다.




◆ 원인

검색 결과 미리보기는 일반적으로 검색어 주변의 일정 길이만 잘라서 생성한다.

예를 들어 다음과 같은 방식이다.

const preview = text.substring(start, end);

문제는 substring()문자의 의미를 고려하지 않고 단순히 인덱스만 기준으로 문자열을 자른다는 점이다.

인도계 언어는 하나의 글자가

자음 + 모음기호

처럼 여러 유니코드 문자로 구성되는 경우가 많다.

만약 잘리는 위치가 가운데라면

자음 ❌

모음기호만 남음 ⭕

이 되어 화면에는 모음기호만 덩그러니 표시된다.

즉, 브라우저의 렌더링 문제가 아니라 문자 자체가 잘못 잘린 것이었다.




\p{M}이란?

해결 방법을 찾던 중 JavaScript의 Unicode Property Escape를 알게 되었다.

/\p{M}/u

여기서

  • \p : 유니코드 속성(Property)으로 검색
  • M : Mark(결합 기호) 속성
  • u : Unicode 모드

를 의미한다.

쉽게 말하면,

“이 문자가 결합기호(Combining Mark)인가?” 를 판별하는 정규표현식이다.

대표적으로 다음과 같은 문자들이 모두 Mark에 속한다.

  • 악센트(◌́)
  • 힌디어 모음기호
  • 벵갈어 모음기호
  • 펀자비어(Gurmukhi) 모음기호
  • 아랍어 모음기호
  • 기타 결합 문자를 사용하는 언어

즉, 특정 언어를 하나씩 판별하는 것이 아니라 유니코드 표준을 기준으로 모든 결합기호를 공통 처리할 수 있다는 장점이 있다.




◆ 해결 방법

잘라야 하는 위치가 결합기호라면, 결합기호가 끝날 때까지 이동시킨다.

예를 들어

A + ◌́ + B
        ▲
      잘리는 위치

처럼 결합기호에서 문자열이 시작된다면

A + ◌́ + B
          ▲
     여기까지 이동

하도록 보정하는 것이다.

이를 위해 먼저 현재 문자가 결합기호인지 확인한다.

function isCombiningMark(ch) {
    return /\p{M}/u.test(ch);
}

그리고 잘리는 위치가 결합기호라면 다음 일반 문자까지 이동하도록 처리했다.

(실제 프로젝트에서는 이 로직을 문자열 시작과 끝 경계에 적용하였다.)




◆ 결과

보정 로직을 적용한 이후에는

  • 모음기호만 단독으로 출력되는 현상이 사라졌고,
  • 벵갈어, 힌디어, 펀자비어 등 여러 언어를 하나의 로직으로 처리할 수 있었다.

별도의 언어별 예외 처리를 추가하지 않아도 되어 유지보수성도 크게 향상되었다.




◆ 마무리

이번 경험을 통해 단순한 문자열 처리도 유니코드 문자 구조를 이해하는 것이 중요하다는 점을 배웠다.

처음에는 특정 언어만 예외 처리하려고 했지만, 유니코드 표준(\p{M}) 을 활용하면서 훨씬 일반적이고 확장 가능한 방법으로 문제를 해결할 수 있었다.

다국어를 지원하는 애플리케이션에서는 “문자 개수”뿐 아니라 문자가 어떻게 구성되는지까지 고려해야 한다는 점을 다시 한번 느낀 디버깅 경험이었다.

댓글남기기