유니코드 지원
프로그래밍 언어에서 유니코드 (Unicode) 지원은 크게 두가지로 나누어볼 수 있다. 바로 소스 코드(source code)에서의 유니코드 지원과 문자열(string)에서의 유니코드 지원이다. 소스 코드에서의 유니코드 지원은 문자열 리터럴과 클래스, 변수 이름과 같은 심볼들의 이름에서 유니코드 문자를 표현할 수 있음을 의미한다. 문자열에서의 유니코드 지원은 소스 코드에서의 유니코드 지원을 포함한다. 문자열에서의 유니코드 지원은 문자열 변수, 리터럴이 유니코드 문자를 표현할 수 있는가 뿐만 아니라, 문자열과 문자를 다루는 방법, 문자열과 바이너리의 구분, 여러가지 다른 인코딩으로의 변환 등을 포함한다.
이 글에서는 ‘문자열에서의 유니코드 지원’을 중심으로 얘기를 해보자.
C 문자열
C/C++, PHP, Python (< 2.0), Ruby와 같은 언어들에서는 다음과 같은 문자열을 가지고 있다.
- 문자열은 외부에서 주어진 인코딩으로 인코딩된 바이너리 형태며, 문자열 자체만으로 어떤 인코딩인지는 구분할 수 없는 방식.
- 문자열과 문자를 다루는 함수는 시스템 로케일 또는 컴파일러에게 주어진 설정에 따른 인코딩을 가정함.
- 이를 다른 인코딩의 문자열로 변환하는 방법을 문자열에 대한 언어적인 지원이나 라이브러리로 제공.
이러한 문자열 방식의 문제점은 그동안 수많은 프로그래머들을 괴롭혀온 것이라고 볼 수 있다.
- 유니코드 문자를 사용할 수 없다. 동시에 여러 문자집합을 사용하기 어렵다.
- 특정 문자집합 인코딩(charset encoding)을 가정하고 프로그래밍하는 경우가 잦아서, I18N이 매우 어렵다.
C와 함께하는 프로그래밍 역사에는 ‘a byte is a character’라는 생각이 깃들여있다. 하나의 문자가 실제로 ‘문자’에 해당하는 문자집합 인코딩을 가진 영어 또는 서유럽 언어에서는 크게 문제가 안되지만, 이른바 멀티바이트(multibyte) 문자열을 사용하는 한국, 중국, 일본과 같은 국가에서는 인코딩 지옥(encoding hell)이라고 부를만큼 사용자나 프로그래머들이 어려움을 겪어왔다고 볼 수 있다.
Java 문자열
Java는 유니코드 지원과 함께 탄생했다. 국제 표준에 해당하는 유니코드에 대한 개념이 그리 보편화되지 않았던 1996년에 Unicode 1.1을 반영하는 Java 1.0이 발표되었다.
- 타입을 사용한 문자열과 바이너리의 구분: 문자는 char형, 바이트는 byte형을 이용하고, 문자열과 바이너리는 각각의 배열로 표현한다. 문자와 이를 인코딩한 형태를 타입을 이용해 구분했다는 것은 당시로서는 획기적인 것이었다.
- 유니코드 문자집합 (UCS-2)의 지원: 내부적으로 2byte로 표현되는 char형은 유니코드에서 정의한 UCS-2 문자집합의 코드포인트(codepoint)에 해당했다. Java 5에서 Unicode 4.0을 반영하면서 char형은 UTF-16의 코드유닛(codeunit)을 나타내게 되었다.
- 문자열과 문자를 다루는 함수는 UTF-16 이외에 더이상 인코딩을 신경쓸 필요가 없게 되었음. (UCS-2에 속하는 문자만 사용한다면 아예 신경 쓸 필요가 없음.)
- 문자열을 특정 인코딩으로 인코딩하는 방법을 언어적인 지원과 라이브러리로 제공.
Java 초기에는 유니코드 지원으로 인한 성능 문제에 대한 논란이 많았던 걸로 기억한다. 지금 돌이켜보면, 유니코드 문자열의 도입으로 인한 성능 하락은 그리 큰 문제가 아닌 듯하다.
Java의 유니코드 지원 방식은 다른 언어들의 모범이 될만큼 깔끔하고 성공적으로 적용되었다고 생각한다. 개인적으로 가장 맘에 드는 Java의 특징 중 하나가 바로 유니코드 지원이다.
To Be Continued…
다음에는 C/C++의 wchar_t, Perl의 유니코드 지원, Python 2.0의 유니코드 문자열, Python 3000과 PHP 6의 유니코드 문자열, Ruby의 유니코드 지원에 대해서 다루어보겠다.