요즘 웹 애플리케이션 개발에 있어서 가장 뜨거운 주제는 국제화(internationalization)와 지역화(localization), 그리고 유니코드이다. 애플리케이션을 개발하고 서비스할 때 다양한 국가의 언어를 지원하지 않는다면 많은 잠재적 사용자를 잃게 될 것이다. 최근 조사에 의하면 전세계에서 영어를 사용하는 인구는 약 오억 천만 명 정도로 알려져 있다. 만일 영어권 사용자만을 위해 애플리케이션을 만든다면 그것은 곧 92퍼센트나 되는 잠재적인 글로벌 고객을 차단하게 되는 것이다. 사실 이러한 통계는 부정확한 경향이 있고 대개 위험을 과장하기 위한 수단으로 이용될 때도 있기 때문에 실제로 전세계 60억 명의 인구 중에서 얼마나 많은 사람이 인터넷을 사용하는지부터 고려해야 할 것이다. 이런 점을 감안하더라도 통계 조사 자료^1 에 따르면 인터넷 사용자의 64%인 약 6억 8천 만 명은 영어를 사용하지 못하며 따라서 비영어권에 대한 지원을 하지 않는다면 엄청난 규모의 잠재적 고객을 놓치게 될 것이다.
예전부터 이 문제를 다루는 것은 매우 큰 문제였다. 개발자들은 문자 집합(character set)과 텍스트 처리에 대한 고급 지식이 있어야 하고 언어에 종속적인 데이터를 별도로 저장해야 했으며 또한 서로 다른 그룹의 사용자 간에 데이터를 공유할 수도 없었다. 전 세계적으로 인터넷이 보급되면서 이러한 문제에 대한 해결책이 필요해졌고 마침내 도출된 해결책은 개발자를 힘들게 했던 많은 일들을 없애 주고 몇 가지 간단한 지식만으로도 다양한 언어를 지원하는 애플리케이션을 쉽게 만들 수 있게 해 주었다.
이 장에서는 국제화와 지역화를 둘러싼 문제점들을 살펴보고 손쉽게 해결할 수 있는 방법들을 소개할 것이다. 또한 유니코드가 무엇이며 어떻게 동작하는지에 대해 자세히 알아보고 쉽고 빠르게 유니코드를 지원하는 애플리케이션을 구현하는 방법에 대해서도 알아볼 것이다. 아울러 웹 애플리케이션의 데이터 조작과 관련하여 유니코드와 연관된 주요 사항들을 살펴보고 잠재적인 함정들을 찾아보도록 할 것이다.
국제화와 지역화
국제화와 지역화는 웹 애플리케이션 분야에서 유행하고 있는 용어이다. 사람들이 솔깃해 할 만큼 멋져 보이는 단어들이자 최근에 점차 중요성이 높아지고 있기 때문이다. 국제화와 지역화는 보통 하나로 묶어서 말하는 경우가 많지만 뚜렷히 다른 의미를 가지고 있으며 다음과 같이 그 차이점을 이해하는 것이 중요하다.
- 국제화는 글로벌한 텍스트의 입력ㆍ출력ㆍ처리를 수행하는 능력을 애플리케이션에 추가하는 것이다.
- 지역화는 애플리케이션을 특정 로캘(locale)에 적합하게 만드는 작업이다.
국제화는 종종 i18n으로 줄여서 표기하고 지역화는 L10n으로 표기하는 경우가 많다.[^2] 대부분의 다른 주목을 받는 주제들과 마찬가지로 i18n과 L10n도 많은 관련 용어들이 있다. 오용을 막기 위해 여기서 몇 가지 소개하자면 세계화(globalization) 또는 g11n은 i18n과 L10n을 모두 합친 의미를 가진 용어이며 개인화(personalization) 또는 p13n과 범위(reach) 또는 r3h는 L10n과 같은 의미를 지닌다.
웹 애플리케이션의 국제화
아주 먼 옛날에는 (또는 90년대라고 불리던 시절에서는) 국제화에 대한 지원은 애플리케이션이 데이터를 입력ㆍ저장ㆍ출력할 때 다양한 문자 집합과 인코딩을 지원하는 것을 의미했다. 예를 들어, 영어 사용자는 Latin-1을 사용하고, 러시아어 사용자는 KOI8-R을 사용하며, 일본어 사용자는 Shi_JIS를 사용하는 것과 같이 말이다. 이러한 방식은 서로 다른 언어권 사용자의 데이터를 한 페이지에 표시해야 하기 전까지는 문제가 되지 않았다. 각 문자 집합과 인코딩은 대개 100개에서 250개 내외의 정의된 문자들만을 인코딩하고 출력할 수 있었다. 때로는 사용하는 문자들이 중복되기 때문에 예를 들어 유니코드 문자로 Ю이자 키릴 문자 “Ю”를 KOI8-Ukrainian에서는 0xE0으로, Apple Cyrillic에서는 0x9E으로 각각 저장할 수 있다. 하지만 대개 한 문자 집합에서 사용하는 문자를 다른 문자 집합에서 표기할 수는 없다. 따라서 IBM-971-Korean에서는 ね 또는 “ね”를 표현할 수 있고 IBM-914-Baltic에서 Ų 또는 “Ų”를 표현할 수 있지만 인코딩을 서로 바꿔서 표현할 수는 없다.
문자 집합의 다양성도 좋지만 서로 다른 문자 집합의 문자 간의 차이점 외에도 다른 문제들이 있다. 모든 저장된 데이터는 사용된 문자 집합이 무엇인지 알 수 있도록 꼬리표를 붙여야 하고 문자열을 처리할 때 사용하고 있는 문자 집합의 종류를 미리 알고 있어야 한다. 예를 들어, Shi_JIS 문자열에서 문자를 바이트 단위로 잘라낼 수 없듯이 말이다. 페이지를 표시할 때도 HTML과 컨텐츠가 올바른 문자 집합을 사용하도록 처리해야 한다.
그러다 누군가가 더 이상 이렇게 처리해서는 안 되겠다고 말했다. 해결 방법은 간단해 보였다. 하나의 문자 집합과 인코딩을 사용해 저장하고 표기할 문자를 모두 이 안에 담는 것이었다. 따라서 문자열에 문자 집합에 대한 꼬리표를 달지 않아도 되고 다양한 문자를 다루는 함수도 쓸 필요가 없어지며 페이지들을 단일 형식으로 출력할 수 있게 될 것이었다. 아주 산뜻하고 좋은 생각 같아 보였다.
그리하여 1991년에 유니코드가 탄생하였다. 세상에 알려진 모든 표기 가능한 문자들을 포함한 문자 집합과 현존하는 모든 문자 집합의 분음 표기 및 기호들을 가진 유니코드는 새롭고 훌륭한 일련의 인코딩들과 함께 혁명을 가져올 것이 분명해 보였으며 이후 10년간의 암흑기를 지나 마침내 그 희망을 이루게 되었다.
이 장에서는 국제화와 관련하여 유니코드 문자 집합과 UTF-8 인코딩만을 다룰 것이다. 다른 유니코드 인코딩을 이용하거나 다양한 문자 집합을 이용하는 등 다른 방식으로도 국제화를 할 수 있지만 UTF-8을 사용하는 것이 영문 데이터 중심의 애플리케이션에서는 올바른 선택일 것이다. 많은 양의 CJKV(중국어, 일본어, 한국어, 베트남어) 데이터를 사용하거나 큰 값의 코드 포인트(code point)가 많은 경우에는 UTF-16이 좋은 선택일 수 있다. 가변적인 길이의 코드 포인트 형식을 제외하고는 이 장의 모든 내용은 UTF-16에서도 UTF-8과 마찬가지로 적용된다.
웹 애플리케이션의 지역화
웹 애플리케이션의 국제화는 지역화의 필요한 선행 조건이지만 이 둘은 서로 매우 다른 의미를 가지고 있다. 웹 애플리케이션을 지역화한다는 것은 사용자가 선호하는 로캘에 따라 (대개 문자적인 의미에서) 다른 인터페이스를 제공하는 것을 의미한다.
도대체… 로캘이 뭐야?
로캘(locale)은 지역화와 관련된 일련의 설정값들을 나타낸다. 보통 언어와 지역을 포함하며 대개 시간대, 시간 및 날짜 표기 형식, 숫자 표기 형식 그리고 통화 기호를 포함하기도 한다. 보통 한 명의 사용자에 대해 하나의 로캘이 저장되어 사용자의 시간대에 맞는 시간 표시, 사용자의 언어로 표현된 텍스트, 사용자가 원하는 숫자 단위 기호 (예를 들어 숫자의 천 단위마다 콤마의 사용) 등 애플리케이션의 다양한 부분이 해당 사용자에게 맞추어지도록 사용될 수 있다.
웹사이트를 지역화하는 방법은 몇 가지 있지만 간단한 것은 하나도 없다. 이 장에서는 국제화에 대해 중점적으로 다룰 것이며 지역화에 대해서는 많이 다루지 않을 것이다. 국제화에 대해 살펴보기에 앞서 먼저 지역화에 대한 세 가지 접근법에 대해 간단히 살펴보자.
문자열 치환
가장 저수준의 방법으로는 GNU의 gettext^3와 같이 문자열 수준에서 언어를 교체해주는 라이브러리를 이용할 수 있다. 예를 들어, 다음과 같이 환영 인사를 출력하는 간단한 PHP를 보자.
printf(“Hello %s!”, $username);
다음과 같이 gettext 래퍼(wrapper) 함수를 이용하면 어떠한 언어로도 변환이 가능하다.
printf(_(“Hello %s!”), $username);
기존의 코드에서 바뀐 것은 _()라는 gettext 함수를 호출하여 영문 문자열을 전달한 것뿐이다. 그리고 예제 4.1과 예제 4.2에 나와 있듯 gettext의 설정 파일은 해당 구절에 대한 다양한 언어의 표현 값을 담는다.
예제 4.1 my_app.fr.po
msgid “Hello %s!”
msgstr “Bonjour %s!”
예제 4.2 my_app.ko.po
msgid “Hello %s!”
msgstr “%s님 안녕하세요!”
따라서 gettext 함수가 호출되면 사용자의 로캘에 맞는 올바른 문자열이 반환되고 애플리케이션은 이를 출력하게 된다.
문자열 치환의 문제점은 순서나 흐름의 변경이나 설명의 추가와 같은 애플리케이션의 시각적인 구조가 바뀔 때마다 즉시 모든 번역 내용을 업데이트해야 한다는 점이다. 번역팀이 항시 대기중이라면 괜찮을 수도 있겠지만 그렇지 않다면 매번 변경할 때마다 번역되길 기다려야 하며 신속한 웹 애플리케이션 개발과 잘 맞지 않을 것이다.
다중 템플릿 집합
페이지 로직에서 마크업이 완전히 분리된 애플리케이션에서는 템플릿이 (조건문이나 반복문을 제외하고서는) 별도의 처리가 필요 없는 문서처럼 동작한다. 지원하고자 하는 로캘별로 하나씩 템플릿 세트를 구축한다면 하나의 템플릿 세트를 다른 세트와 동시에 개발할 필요는 없다. 주 로캘에 다수의 변경을 계속해서 수행하면서 주기적으로 이러한 변경 사항을 한 번에 몰아서 다른 로캘에 적용할 수 있다.
하지만 이런 방법은 다음과 같은 몇 가지 문제점을 안고 있다. 마크업의 변경은 페이지 로직의 변경과 독립적으로 수행할 수 있지만 페이지 로직의 기능적인 변경은 마크업 전체에 영향을 주게 된다. 예를 들어, 기존의 페이지에서 다섯 개의 최신 뉴스를 보여주던 것을 임의의 기사들을 보여주는 것으로 바꾼다면 모든 언어별로 이러한 변경 사항이 동시에 적용되어야 한다. 이것의 대안으로 페이지 로직 계층에서 다양한 기능을 동시에 지원하게 할 수 있다. 템플릿이 원하는 기능을 선택하게 함으로써 특정 기능의 변경이 모든 로캘에 영향을 주지 않게 만드는 것이다. 하지만 이 같은 방법은 페이지 로직 계층에 상충하는 여러 개의 논리적인 흐름들을 만들어서 복잡성을 증대시킨다.
다중 앞단
여러 개의 논리적 흐름들로 페이지 로직 계층을 복잡하게 만들지 않고 대신에 여러 개의 페이지 로직 계층을 (각자 상위의 마크업과 표현 계층을 포함하여) 만드는 방법을 택할 수도 있다. 이를 통해 효과적으로 하나의 동일한 저장소 및 비지니스 로직 계층을 기반으로 다수의 사이트를 만들어낼 수 있다.
계층 모델을 기반으로 애플리케이션의 아키텍처를 설계하고 비지니스 로직의 기능들을 API로(11장 참조) 제공함으로써 초기에는 하나의 로캘만을 지원하다가 추후 다른 로캘들을 지원하도록 만들 수 있다. 이렇게 국제화된 비지니스 로직 및 저장소 계층을 통해 로캘 간 데이터를 공유할 수 있게 된다. 예를 들어, 일본어 로캘을 지원하는 앞단(Frontends)을 통해 추가된 데이터를 스페인어로캘을 지원하는 앞단에서 볼 수 있게 된다.
W3C의 i18n 사이트^4를 방문하면 웹 관련 i18n과 L10n에 대해 좀 더 전반적인 정보를 찾아볼 수 있다.
[^2]: 국제화의 영문 표기인 “internationalization”에서 첫 글자와 마지막 글자인 ‘i’와 ‘n’을 쓰고 두 글자 사이의 문자들의 개수를 조합하여 표현한 것이다. 지역화도 마찬가지 방식으로 줄여서 표기하지만 첫 글자인 ‘L’의 소문자가 ‘i’와 혼동되기 쉽기 때문에 대문자를 사용한다.
- 이미지 출처: Greg Grossmeier / CC BY-SA 2.0