다국어 솔루션 개발에서 반드시 고려해야 할 것들

2008년에 전세계적으로 제한 없이 접근 가능한 앱 스토어와 플레이 스토어가 경쟁적으로 오픈한 이래, 진정으로 특정 국가나 로컬에서만 사용되는 것을 목적으로 개발되는 솔루션의 수는 점점 줄어들어 이제는 극히 소수에 불과하다고 할 수 있습니다. 아니, 심지어 로컬에서 사용되는 것을 전제로 개발된 솔루션일지라도 다국어 기반의 솔루션인 것이 당연한 상황에까지 이르렀다고 할 수 있을 것 같습니다.

하지만 1990년대 중반부터 다국어 프레임워크 기반의 솔루션에 목말라하고 이러한 프레임워크를 직접 개발해 왔던 저는, 지금 개발 현장에서는 다국어 솔루션 개발에 대한 이해도가 의외로 매우 떨어져 있다는 사실을 실감하고 있습니다. 이것은 우리가 특정 제품이나 사상이 발전해 오다 보면 어느 순간 그 원래 목적이나 의도가 희석되고 관성적으로 사용하게 되는 것과 같다고 할 수 있습니다. 이 글은 강좌가 아니기 때문에 다국어 개발을 어떻게 해야 하는지 일일이 설명하지는 않겠지만, 지금까지 개발 도구나 운영 체제, 프레임워크의 개발을 주도해 온 서양의 입장에서 다국어를 바라보는 자세가 어떻게 변화해 왔는지에 대해서 살펴 봄으로써 다국어 개발에 대한 이해를 높여 보고자 합니다.

2바이트 (실제로는 4바이트) 기반의 유니코드(Unicode)가 세상을 지배하기 전에는, 서양인들의 관점에서는 알파벳의 26글자를 대소문자로 구분하고, 숫자와 일부 기호를 모두 담아도 128개를 넘지 않았기 때문에 모든 글자를 7비트 내에 담을 수 있다고 생각했습니다. 왜 8비트인 1바이트가 아니냐 하면 제일 앞의 1비트는 특수한 용도로 쓰였기 때문이지요. 추후 여러 가지 이유로 나머지 1비트도 문자 체계에 포함하여 우리가 흔히 ASCII 문자 체계라고 일컫는 SBCS(Single Byte Character System, 1바이트 문자 체계)를 기본 문자 체계로 사용했습니다.

그리고 당시 일본은 어떤 선택을 했느냐 하면, 일본어의 특성 상 불편해도 카타카나(カタカナ) 50음도가 있으면 어떻게든 표현은 할 수 있었기 때문에 8비트 시스템에서는 SBCS에서 확장 문자 영역에 해당하는 영역에 카타카나를 박아넣는 것부터 시작했습니다. 그리고 당시의 모습은 유명한 영화 《매트릭스(Matrix, 1999)》의 화면에서 찾아 볼 수 있습니다. 하지만 당연하게도 이는 매우 불편한 방식이었기 때문에, 두 개의 바이트를 하나로 묶어서 글자 수를 256개가 아닌 65,536개로 늘릴 수 있는 방법을 고안해 냈고 이는 추후 JIS(Japan International Standard) 문자 체계를 거쳐, 일부 문자의 비트를 이동시켜 SBCS의 일부 제어 문자를 원활하게 사용할 수 있게 해 주는 Shift-JIS 체계로 정착하게 됩니다. 이 문자 체계는 지금도 DBCS(Double Byte Character System, 2바이트 문자 체계)기반의 시스템에서 EUC-JP(Extended UNIX Code Packed Format for Japanese)로 지정되어 사용되고 있습니다.

우리나라 역시 일본의 문자 체계를 모방하는 것에서 시작해서, 두 개의 바이트를 하나로 묶고, 첫 번째 글자의 MSB(Most Significant Bit)가 1로 설정되면 다음 바이트까지 묶어 한글로 표현하고, 0으로 설정되면 뒤의 7비트를 SBCS의 ASCII 문자로 인식하는 방식을 사용했습니다. 문제는 첫 번째 비트가 무조건 1로 설정되어야 하기 때문에 실제로는 32,768개의 글자만 사용할 수 있었고, 한글의 특성 상 현대 한글의 11,172자와 한자를 모두 다 담다 보면 어떤 문제가 발생할지 알 수 없다는 문제가 있었습니다. 그래서 한글의 문자 체계는 초기의 7비트 한글 체계를 고려하지 않더라도 한글의 자모를 비트별로 쪼개 조합하는 방식의 조합형과 한글의 글자 자체를 그대로 담는 완성형의 두 개의 흐름으로 꽤 오랜 시간 유지되어 왔고, 마이크로소프트의 Windows 3.0이 완성형 한글을 채택하면서 완성형 한글이 한동안 기본 문자 체계였던 시기가 있었습니다. 그리고 이 문자 체계는 일본의 EUC-JP와 마찬가지로 EUC-KR로 지정되어 지금도 레거시 시스템과 데이터베이스에서 사용되는 경우가 존재합니다.

중국의 경우에는 7,445개의 간체자 문자를 담을 수 있는 GB2312 문자 체계를 1980년에 발표한 이래 GBK 1.0을 거쳐 GB18030까지 발전하면서 EUC-CN으로 사용해 오다가, 유니코드로 넘어가기 전까지는 사실 상 유유자적한 상황이었습니다. 해외 인터넷을 볼 일도 해외 앱을 사용할 일도 사실 거의 없을 정도로 거의 모든 솔루션이 중국어 버전을 제공했었거든요. 그리고 대만에서는 이와 별도로 Big5(大五碼) (→ EUC-TW) 문자 체계를 사용했었습니다.

왜 이렇게 장황하게 문자 체계 이야기를 하고 있는지 궁금한 분들이 계실 겁니다. 이러한 문자 체계는 매우 큰 문제를 가지고 있었는데, 예를 들면 한국의 완성형 한글을 쓰는 시스템에서는 중국어나 일본어를 사용할 수 없었고, 일본과 중국도 마찬가지였습니다. 그리고 서양의 개발자들은 이러한 글자 체계에 대해 애초에 관심조차 없었습니다. 그렇기 때문에 미국에서 개발된 대다수의 소프트웨어는 동아시아에서 사용하게 될 경우 화면이 깨지는 것은 기본이었습니다.

한글 DOS 환경에서 박스 문자가 깨지는 현상의 예 (Borland C++ 3.0)

이와 같은 문제를 해결하기 위해 90년대 중반부터 비트맵 폰트를 활용한 그래픽 모드 기반의 한글 솔루션이 대거 등장하기 시작했고, 저를 포함한 많은 개발자들이 한라 프로, 허르미, 한과 같은 한글 라이브러리를 개발해 배포하기도 했습니다. 당시 PC 통신을 위해 사용되었던 Intalk가 자체 한글 솔루션의 서막을 알린 이래, 경북대학교의 컴퓨터 동아리였던 하늘소에서 개발한 이야기가 국내에서 가장 많이 사용된 소프트웨어였던 적도 있으니까요.

하지만 이는 소프트웨어 개발 시 시스템 BIOS Call이 기본적으로 제공해 주는 기능들을 대다수 포기하고, 모든 것을 다 직접 구현해야 한다는 것과 마찬가지 의미였습니다. 만약 여러분이 지금 React.JS와 React Native, Flutter, Vue.js, Django REST Framework, Fast API, Java Spring과 같은 프레임워크의 도움 없이 사용 중인 언어의 기반 라이브러리만으로 모든 것을 직접 구현하여 웹이나 앱 솔루션을 개발해야 한다고 생각한다면 그 어려움이 조금 느껴질지 모르겠네요.

그럼 그래픽 모드 기반의 환경이었던 Windows에서는 괜찮았느냐 하면 그렇지 않았습니다. 혹시 현재 개발을 하고 계신 분들 중에 Windows의 UI 기반 메트릭(Metrics)이 무엇을 기반으로 하는지 알고 계신 분이 계실까요? 글꼴이나 폰트라고 답하신 분이라면 아마 이것 때문에 무척 고생을 했던 저 같은 분들일 겁니다. 이게 무슨 뜻이냐면 특정 창이나 대화 상자의 기반 글꼴을 변경할 경우, 대화 상자의 내부 UI 구성 요소인 버튼이나 텍스트의 크기, 위치가 전부 이에 맞춰 자동으로 변경된다는 뜻입니다. 이게 왜 문제였는지 궁금한 분들이 계실테니 간단하게 설명드릴게요.

혹시 아주 예전에 한글 Windows 95, 98, 2000, Me, XP를 사용했던 분들 중에 ACDSee 같은 영문 기반의 프로그램을 실행하고 설정 대화 상자 등을 열면 내용의 일부가 잘려서 보이지 않아, OK, Cancel 버튼을 누를 수 없었던 황당한 문제를 겪었던 분들이 계실 거라고 생각합니다. 이 문제가 발생했던 이유는 동아시아 문자는 그 복잡성으로 인해 시스템 폰트의 기본 크기가 9pt였던데 반해, 영문 시스템 폰트는 8pt였기 때문입니다. (영문 Windows: Tahoma 8pt, 한글 Windows: 굴림 9pt, 일본어 Windows: MS UI Gothic 9pt, 간체 중국어 Windows: SimSun 9pt)

영문 기본 글꼴이 더 작으니까 오히려 다 나와야 하는 거 아니냐라고 생각하시기 쉽지만 앞에서 말씀드린 것처럼 UI의 기본 메트릭이 글꼴 크기 기반이기 때문에, 동아시아 시스템에서 해당 UI를 호출하면 8pt 기반으로 디자인된 UI가 자동으로 9pt 기반으로 변경이 되는 문제가 발생합니다. 다시 말해 창의 크기는 8pt 기반의 UI에 맞춰져 있는데 글꼴과 내부 UI 구성 요소가 9pt 기반으로 커지다 보니 창이 모든 내용을 담지 못하고 잘리는 문제가 발생하는 것입니다.

그런데 만약에 우리가 다국어 기반의 솔루션을 개발하면서 단순하게 텍스트 번역을 통해 다국어를 구현한다고 가정해 봅시다. 아마 영어로 메시지는 표현되겠지만, 9pt 기반의 한글 Windows에서는 예쁘게 나오던 화면이 8pt 기반의 영문 Windows에서는 대화 상자의 우하단이 비어 보이는 문제가 발생하겠지요. 따라서 이런 경우에는 각 나라별 리소스를 따로 분리해서 대화 ㅂ상자 디자인부터 텍스트, 그리고 심지어 때로는 이미지까지도 분리해서 관리해 주어야 합니다.

아래 코드는 제가 예전에 개발했던 모바일 솔루션의 다국어 리소스 코드의 일부분으로서 먼저 영문 리소스 데이터입니다.

IDD_FIND DIALOG 0, 0, 160, 76
STYLE DS_SETFONT | WS_POPUP | WS_CAPTION
CAPTION "Find"
FONT 8, "Tahoma"
BEGIN
    LTEXT "Find:", IDC_FIND_FILTER_H, 4, 20, 34, 8
    EDITTEXT IDC_FIND_FILTER, 40, 18, 116, 12, ES_AUTOHSCROLL
    LTEXT "From:", IDC_FIND_FROM_H, 4, 34, 34, 8
    EDITTEXT IDC_FIND_FROM, 40, 32, 98, 12, ES_AUTOHSCROLL
    PUSHBUTTON "…", IDC_FIND_BROWSE, 140, 32, 16, 12
    CONTROL "Search Sub Folders", IDC_FIND_SUBFOLDERS, "Button",
        BS_AUTOCHECKBOX | WS_TABSTOP, 40, 46, 116, 10
    PUSHBUTTON "Date…", IDC_FIND_DATE, 74, 60, 40, 12
    PUSHBUTTON "Size…", IDC_FIND_SIZE, 116, 60, 40, 12
    CONTROL "", -1, "SIPPREF", NOT WS_VISIBLE, -10, -10, 6, 6
END

이어서 아래 코드는 한글 리소스 데이터입니다. 두 개의 데이터는 별도의 프로젝트로 관리되며, 각각 별도의 DLL 파일로 빌드되게 됩니다.

IDD_FIND DIALOG 0, 0, 138, 76
STYLE DS_SETFONT | WS_POPUP | WS_CAPTION
CAPTION "찾기"
FONT 9, "Tahoma"
BEGIN
    LTEXT "찾기:", IDC_FIND_FILTER_H, 4, 20, 20, 8
    EDITTEXT IDC_FIND_FILTER, 26, 18, 108, 12, ES_AUTOHSCROLL
    LTEXT "위치:", IDC_FIND_FROM_H, 4, 34, 20, 8
    EDITTEXT IDC_FIND_FROM, 26, 32, 90, 12, ES_AUTOHSCROLL
    PUSHBUTTON "…", IDC_FIND_BROWSE, 118, 32, 16, 12
    CONTROL "하위 폴더 검색", IDC_FIND_SUBFOLDERS, "Button",
        BS_AUTOCHECKBOX | WS_TABSTOP, 26, 46, 108, 10
    PUSHBUTTON "일시…", IDC_FIND_DATE, 52, 60, 40, 12
    PUSHBUTTON "크기…", IDC_FIND_SIZE, 94, 60, 40, 12
    CONTROL "", -1, "SIPPREF", NOT WS_VISIBLE, -10, -10, 6, 6
END

두 개의 코드에서 굵게 표시된 부분은 각각의 UI 구성 요소의 위치와 크기를 지정하는 부분과 폰트를 지정하는 부분입니다. 영문의 경우 8pt로 지정되어 있고, 한글의 경우 9pt로 지정되어 있는 것을 알 수 있습니다. 문제는 이렇게 개발하다 보니 마치 한글 표시의 문제를 해결하기 위해 아예 그래픽 기반으로 자체적인 한글 출력 시스템을 만든 것처럼, 너무나 많은 개발 리소스가 투입되는 문제가 생기게 됩니다. 하지만 2010년대 이후, Windows를 개발하던 Microsoft 내에서 점차 외국인 개발자의 비중이 늘어나게 되고, 이에 대해 문제 의식이 생기게 되면서, Windows의 다국어 개발에 있어 큰 개벽이라 부를 만한 사건이 일어납니다. 바로 Windows 8부터 기본 폰트가 언어 버전과 상관없이 9pt로 고정된 것입니다. 기존에 8pt에 맞춰서 디자인되어 있던 Segoe UI 폰트를 다듬고 폰트 크기 레벨을 재조정하여 9pt가 기존의 8pt 크기에 맞도록 변경되었던 것이죠. 그 결과 앞에서 고민하면 UI 구성 요소가 뒤틀리던 문제가 30년만에 해결되게 됩니다.

어떤 분들은 그러게 Mac OS를 쓰지 그랬어라고 하실 것 같은데… Mac OS도 NextSTEP 기반의 OS X가 나오기 전의 System 6, 7, 8, 9는 훨씬 더 많은 문제를 가지고 있었기 때문에 따로 언급하지 않겠습니다.

그리고 난립하던 문자 체계 역시, Windows 7과 Mac OS X 이후로 사실 상 유니코드로 대동단결하면서 많은 부분이 해결되기에 이릅니다. 그럼에도 불구하고, 시스템의 용량 문제로 인해 여전히 대부분의 동아시아 글꼴은 기본 설치 항목에서 제외되어 있으며, 언어 팩을 설치해야 입력기와 글꼴이 설치됩니다. 심지어 언어 팩의 리뷰에는 도대체 이 큰 용량은 뭐냐고 불만을 터트리는 경우가 아직도 대다수일 정도로, 삭제 문의가 쏟아지고 있습니다. 이러한 환경에서 우리가 개발하는 다국어 솔루션이 아무런 설명이나 추가 설치 요구 없이 기본적인 설치만으로 정상 동작하게 하려면 많은 고민과 노력이 필요합니다.

시스템적인 제약은 이렇게 해결되어 가고 있지만, 사실 그 보다 더 큰 문제는 다국어 리소스를 대하는 우리의 자세입니다. 예를 통해 설명하기 위해 아래와 같은 메시지를 출력해야 한다고 가정해 보겠습니다. 그리고 우리는 복사할 파일의 전체 갯수와 복사 중인 파일의 갯수, 그리고 복사한 데이터의 크기와 전체 데이터 크기를 바이트 단위로 알고 있다고 가정해 보겠습니다.

Copying files... 10 of 150 (20KB of 460GB)

이를 고민 없이 한글 메시지로 바꾸면 어떻게 될까요? 아마 어떤 개발자들은 아래와 같이 바꾸려 할지도 모르겠습니다.

파일 복사 중... 10개 / 전체 150개 (20KB / 전체 460GB)

그리고 의외로 많은 솔루션이 고민없이 이렇게 바꾸고 있습니다. 하지만 제대로 바꾼다면 아래와 같이 바꾸어야 할 겁니다.

150개 중 10개의 파일 복사 중... (460GB 중 20KB 완료)

앞의 영문 메시지를 바로 위의 한글 메시지로 바꾸려면 어떻게 해야 할까요? 초보 개발자라면 각각의 요소를 전부 쪼개서 코드를 통해 재조합하려고 할지도 모르겠습니다. 하지만 그게 제대로 된 방법이 아니라는 것은 자명한 일이겠죠. 왜냐하면 언어별로 조합하는 코드를 별도로 작성해야 하기 때문이며, 각각의 요소를 전부 쪼개개 될 경우, 문자열 리소스 관리에도 예기치 않은 버그가 생길 가능성이 있습니다. 따라서 가급적이면 하나의 문자열 리소스로 저 메시지를 처리해야 합니다.

대부분의 프레임워크는 이러한 문제를 해결하기 위해 특별한 솔루션을 제공합니다. 예를 들면 Windows의 Visual C++에서 사용되는 MFC(Microsoft Foundation Classes)는 AfxFormatStringn()이라는 함수를 제공하며, 이 함수에 던져줄 문자열은 아래와 같이 작성할 수 있습니다. 물론 이런 솔루션이 기본적으로 제공되지 않는다면 sprintf()와 같은 기능을 하는 메서드를 이용해 직접 비슷한 기능을 구현할 수도 있겠지요.

Copying files... %1 of %2 (%3%4 of %5%6)
%2 개 중 %1 개의 파일 복사 중... (%5%6 중 %3%4 완료)

이렇게 작업할 경우, 언어에 따라 다른 코드를 작성할 필요없이 문자열 리소스 자체에서 어느 위치에 어떤 정보가 들어가는지를 명확하게 지정할 수 있으므로, 오류가 발생할 가능성도 줄어듭니다.

또한 이외에도 많이 하는 실수가 있습니다. 바로 텍스트는 동일하지만 용도가 다른 문자열을 하나의 리소스 ID로 묶어서 이곳저곳에서 사용하는 것입니다. 문제는 다른 언어일 경우에는 용도에 따라 전혀 다른 문자열을 사용할 수도 있다는 점이며, 아무 생각없이 문자열을 1대1로 번역할 경우 심각한 오류를 내포하게 될 수 있게 됩니다.

따라서 모든 문자열 리소스는 반드시 전부 화면별로 문장별로 철저하게 분리해야 하며, 리소스 ID를 지정할 때도 반드시 어떤 화면의 어떤 기능을 위한 문자열인지를 명시하도록 구성해야 합니다. 아래는 리소스 ID를 잘못 지정한 예입니다.

IDS_EDIT "수정"
IDS_OPEN "열기"

리소스 ID는 아래와 같이 반드시 어디에서 사용되는지에 따라 분리하고 명시해 줄 필요가 있습니다.

IDS_IMAGE_EDIT "수정"
IDS_TEXT_EDIT "수정"
IDS_FILE_OPEN "열기"
IDS_OPTION_OPEN "열기"

글을 쓰다 보니 너무 길어졌네요. 하지만 최근에 팀 내 개발에서 이슈가 있었고, 지금 정리하지 않으면 앞으로도 계속 발생할 이슈이다 보니 한 번 짚고 넘어갈 수 있도록 제 자신이 한 번 더 정리할 필요를 느꼈습니다. 여러분께서도 이미 알고 계시는 내용이었다 하더라도 항상 주의깊게 한 번 더 살펴 보실 수 있는 기회가 되시기를 바랍니다.

Related Posts

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.