Know/Java

유니코드(Unicode)와 유니코드 인코딩

Marine™ 2005. 8. 8. 09:47
반응형
[[출처: http://jinsuk.pe.kr/Unicode/Unicode_intro-kr.html ]]


유니코드(Unicode)와 유니코드 인코딩
원래 유니코드에 관한 개괄적인 내용을 담은 문서는 만들지 않을 작정이었습니다. 그런데, 유니코드를 처리하는 부분에 대한 작업을 진행하다보니 용어나 인코딩 방식 등에 대해 혼동이 되는 부분이 너무 많군요. 제 자신을 위해서도 유니코드에 대한 정리를 할 필요가 있을 듯하여 이 문서를 작성합니다. 이 문서에서는 유니코드의 기본 구성 및 인코딩 방식에 대해 비교적 자세하게 설명할 것입니다. 성격 급하신 분이라면 Unicode Consortium이 제공하는 코드간 변환 소스(C 언어)를 바로 보시는 게 더 도움이 될 수도 있겠군요 :-)

차례

유니코드 야사 (아마도 이랬을 것입니다)
유니코드의 구조
유니코드 3.1에 정의된 언어 영역
유니코드 용어의 이해
유니코드 인코딩
UTF-8 인코딩 (8 비트)
UTF-16 인코딩 (16 비트)
UTF-32 인코딩 (32 비트)
보충언어판에 대한 UTF-32와 UTF-16의 인코딩
UTF-16과 UTF-8의 대행코드 인코딩에 관한 오해
기타
참고문헌
유니코드 야사 (아마도 이랬을 것입니다)

거의 모든 프로그래밍 언어에서 사용하는 데이타 기본형으로 char가 있습니다. 이것이 8개의 비트, 즉 1바이트로 이루어져 있다는 것은 모두 알고 계시죠? 왜 하필 8비트였을까요? 당시의 메모리 사용환경이라던지(지금에 비하면 무지무지하게 열악했을 겁니다), CPU 연산속도 때문이라던지(애니악의 경우 폰노이만보다 계산속도가 느렸다는 이야기가 있습니다) 이런 이유들 때문이었을 까요? 가장 궁극적인 원인은 컴퓨터를 만든 사람들에게 있어서 8비트(실제로는 7비트)면 자신들의 언어(영어)를 모두 표현할 수 있었다는 데 있을 것입니다. 즉 7비트이면 27(128)개의 코드를 표현할 수 있는데, 26x2(알파벳 대소문자) + 10(숫자) + 특수문자 + 통제문자 등등을 모두 합쳐 128개이하로 표현이 가능합니다.

세상 모든 사람들이 영어만 쓴다면 Unicode를 만들겠다는 생각을 아무도 하지는 않을 것입니다(훗날 외계인들의 언어를 수용한다면 모를까...). 1바이트로 모든 문자를 표현할 수 없는 언어를 사용하는 국가에서는 각각 자신들만의 인코딩 방법으로 이용하여 문자를 표현해 왔습니다. 한국의 경우 KSC5601 표준(완성형)이 대표적이죠. 그러다 보니 한글, 한자, 일본어 등을 섞어쓸 수 있는 방법이 묘연했었나 봅니다(제한적이나마 가능하긴 합니다만). 그래서 소프트웨어 업체들이 생각한 것이 "그러면 세계의 모든 문자를 표현할 수 있는 코드 체계를 만들자!"라고 해서 만들어지기 시작한 게 UniCode입니다.

세상에 존재하는 모든 문자의 수가 216(65,536)개 이하라면 어떻게 될까요? char를 16비트로 증가시키면 간단하게 해결되겠죠? 처음에는 다들 그렇게 생각했나 봅니다. 유럽과 같이 영어 알파벳 쓰는 자잘한 언어들의 경우 몇백자면 될테고, 한글도 1만자 조금 넘고, 한자도 2만자 조금 넘게 주면 될 것 같고... 모두 합쳐도 6만자도 안되겠네... 그랬나 봅니다. 그래서 유니코드는 16비트의 공간에 모든 문자를 집어넣으려고 했습니다. 이 16비트 영역을 기본다중언어판(BMP, Basic Multilingual Plane)이라고 부릅니다. Unicode 3.0버전까지는 여기에만 문자 코드가 부여되어 있었습니다.

세상일이란 그렇게 만만치 않죠... 한중일에서 고문서를 다루는 사람들도 컴퓨터를 사용해야 되지 않겠습니까? 우리말 고어는 어떻게 표현하고, 지금은 사용되지 않는 옛 한자는 또 어떻게 하란 말입니까? 새로운 인코딩 방식을 별도로 만들어서 사용할까요? 이렇게 되면 유니코드라는 게 의미가 없어질 것입니다. 그래서 유니코드 3.0부터 보충언어판(Supplementary Planes)을 정의하였습니다. 이를 위해 BMP의 2,048자를 대행코드 영역(Surrogates)으로 할당하고 이중 1,024자를 상위대행(high surrogates), 1,024자를 하위대행(low surrogates)으로 정의하여 이 둘의 조합으로 다시 1백만여자의 - 1024x1024=1048576 - 문자를 추가로 정의할 수 있도록 하였습니다. 유니코드 3.1부터는 실제로 이 영역에 문자를 정의했습니다. 가장 크게 할당 받은 것은 역시 한자로 4만여자가 추가로 정의되었습니다.

이러한 세계표준코드를 만들려는 움직임은 소프트웨어 업체들의 연합인 유니코드 컨소시엄외에도 있었습니다. 세계표준기구(ISO)가 바로 이 움직임의 주체였는데, 다행스럽게도 두 단체가 서로 합의하여 1991년이후로 동일한 표준을 만들고 있습니다. 유니코드 표준과 동일한 세계표준기구의 표준안은 ISO/IEC 10646으로 명명되었습니다. 두 표준이 내용은 동일하지만 사용하는 용어나 논리적인 설명이 약간 다르기 때문에 혼동스러울 때도 있습니다. 이러한 이유때문에 제가 이 문서를 작성한 것입니다 :-)


유니코드 3.1에 정의된 언어 영역 (List of block names for Unicode Standard 3.1)


범위 언어 설명
0000..007F Basic Latin 기본 라틴(영어 알파벳)
0080..00FF Latin-1 Supplement
0100..017F Latin Extended-A
0180..024F Latin Extended-B
0250..02AF IPA Extensions
02B0..02FF Spacing Modifier Letters
0300..036F Combining Diacritical Marks
0370..03FF Greek 그리스어
0400..04FF Cyrillic 키릴어
0530..058F Armenian 아르메니아어
0590..05FF Hebrew 히브리어
0600..06FF Arabic 아랍어
0700..074F Syriac 시리아어
0780..07BF Thaana
0900..097F Devanagari
0980..09FF Bengali 벵골어(인도)
0A00..0A7F Gurmukhi
0A80..0AFF Gujarati
0B00..0B7F Oriya
0B80..0BFF Tamil
0C00..0C7F Telugu
0C80..0CFF Kannada
0D00..0D7F Malayalam
0D80..0DFF Sinhala
0E00..0E7F Thai
0E80..0EFF Lao
0F00..0FFF Tibetan
1000..109F Myanmar
10A0..10FF Georgian
1100..11FF Hangul Jamo 한글 자모
1200..137F Ethiopic
13A0..13FF Cherokee
1400..167F Unified Canadian Aboriginal Syllabics
1680..169F Ogham
16A0..16FF Runic
1780..17FF Khmer
1800..18AF Mongolian 몽고어
1E00..1EFF Latin Extended Additional
1F00..1FFF Greek Extended
2000..206F General Punctuation
2070..209F Superscripts and Subscripts
20A0..20CF Currency Symbols
20D0..20FF Combining Marks for Symbols
2100..214F Letterlike Symbols
2150..218F Number Forms
2190..21FF Arrows
2200..22FF Mathematical Operators
2300..23FF Miscellaneous Technical
2400..243F Control Pictures
2440..245F Optical Character Recognition
2460..24FF Enclosed Alphanumerics
2500..257F Box Drawing
2580..259F Block Elements
25A0..25FF Geometric Shapes
2600..26FF Miscellaneous Symbols
2700..27BF Dingbats
2800..28FF Braille Patterns
2E80..2EFF CJK Radicals Supplement 한자 부수
2F00..2FDF Kangxi Radicals 강희자전 부수
2FF0..2FFF Ideographic Description Characters
3000..303F CJK Symbols and Punctuation
3040..309F Hiragana 일본어 히라가나
30A0..30FF Katakana 일본어 카타카나
3100..312F Bopomofo
3130..318F Hangul Compatibility Jamo 한글 호환 자모
3190..319F Kanbun
31A0..31BF Bopomofo Extended
3200..32FF Enclosed CJK Letters and Months 원문자
3300..33FF CJK Compatibility 한중일 호환
3400..4DB5 CJK Unified Ideographs Extension A 한자 확장 A
4E00..9FFF CJK Unified Ideographs 한중일 공통 한자
A000..A48F Yi Syllables
A490..A4CF Yi Radicals
AC00..D7A3 Hangul Syllables 한글
D800..DB7F High Surrogates 상위대행코드
DB80..DBFF High Private Use Surrogates Private Use용의 15,16 Plane 지정을 위한 상위대행코드
DC00..DFFF Low Surrogates 하위대행코드
E000..F8FF Private Use
F900..FAFF CJK Compatibility Ideographs 한중일 호환
FB00..FB4F Alphabetic Presentation Forms
FB50..FDFF Arabic Presentation Forms-A
FE20..FE2F Combining Half Marks
FE30..FE4F CJK Compatibility Forms
FE50..FE6F Small Form Variants
FE70..FEFE Arabic Presentation Forms-B
FEFF..FEFF Specials
FF00..FFEF Halfwidth and Fullwidth Forms
FFF0..FFFD Specials
10300..1032F Old Italic
10330..1034F Gothic
10400..1044F Deseret
1D000..1D0FF Byzantine Musical Symbols
1D100..1D1FF Musical Symbols 음표
1D400..1D7FF Mathematical Alphanumeric Symbols
20000..2A6D6 CJK Unified Ideographs Extension B 한자 확장 B
2F800..2FA1F CJK Compatibility Ideographs Supplement
E0000..E007F Tags
F0000..FFFFD Private Use 개인 사용 목적(언어판 15)
100000..10FFFD Private Use 개인 사용 목적(언어판 16)

유니코드의 구조

이제는 유니코드의 구조에 대해 대강 감을 잡으셨을 줄 압니다. 유니코드의 구조는 크게 17개(1개의 기본언어판(BMP)와 16개의 보충언어판)의 언어판으로 구성되어 있습니다. 각 언어판은 216, 즉 65,536개의 문자를 지정할 수 있으니까 유니코드에서 지정할 수 있는 문자의 수는 17x65,536=114112개입니다. 하지만 대행코드영역(Surrogates) 2048개를 제외해야 하니까 실제로는 1112064개의 문자를 지정할 수 있겠군요. Unicode 3.1 표준에서 각 언어판은 다음과 같이 정의되어 있습니다.

표. Unicode 3.1의 각 언어판 정의 언어판 UCS4 영역 이름 문자수
0 (0x00) 0x00000000 - 0x0000FFFF 기본언어판(BMP, Basic Multilingual Plane) 49,196
1 (0x01) 0x00010000 - 0x0001FFFF 보충언어판(SMP, Supplementary Multilingual
Plane for scripts and symbols) 1,594
2 (0x02) 0x00020000 - 0x0002FFFF 보충표의문자판(SIP, Supplementary
Ideographic Plane) 43,253
3 ~ 13
(0x03~D) 0x00030000 - 0x000DFFFF 아직 사용하지 않음 0
14 (0x0D) 0x000E0000 - 0x000EFFFF 특별보충판(SPP, Supplementary
Special-purpose Plane) 97
15 ~ 16
(0x0F~0x10) 0x000F0000 - 0x0010FFFF 개인사용목적으로 지정(Private Use) 0
합계 94,140


유니코드 표준 3.0에서는 49,194 문자가 정의되었고 이들은 모두 BMP에 한정되어 정의되었으나, 3.1에서는 BMP에 2개의 문자를 추가하고, 보충언어판에 44,944개의 문자를 추가하였습니다. 새로 추가된 문자들은 음표, 고대문자, 한자(CJK Ideographic Extension B) 등입니다.

제 15, 16 언어판에 대한 설명이 조금 필요할 듯 합니다. 이 두개의 언어판은 개인사용(Private Use)로 지정되었습니다. 개발자들에게는 일종의 보너스라고 말하는 사람들도 있군요. 어쨌던 이 두 언어판은 상위대행코드중 마지막 128개 코드와 하위대행코드의 조합으로 지정됩니다(UTF-16 인코딩의 경우). 이 때문인지 유니코드 3.1에서는 U+DB80 ~ U+DBFF 영역을 별도로 분리하여 High Private Use Surrogates라고 이름을 붙였더군요.

2002년 1월에 Unicode Standart 3.2 베타판이 발표되었습니다. 3.2버전이 고정되는 대로 이 문서를 갱신할 예정입니다.

유니코드 용어의 이해

위의 표에서 뜬금없이 UCS4라는 용어를 사용했습니다. 유니코드 관련 문서를 읽다보면 가장 많이 마주치는 용어들이 UCS2, UCS4, UTF8, UTF16, UTF32 등과 같은 단어들입니다. 제가 아주 많이 헷갈렸던 관계로 용어들에 대한 정리를 간단하게 할 까 합니다.

기본언어판, BMP
BMP는 Basic Mulitilingual Plane의 약자입니다. 유니코드의 첫 65,536개의 코드를 의미합니다.
언어판, Plane
256x256 즉 65,536 개씩의 코드 묶음을 이릅니다. 유니코드에서는 현재 17개의 언어판을 사용할 수 있습니다. 모두 그룹 00에 포함됩니다.
언어판 그룹, Group
256개씩의 언어판을 묶어 하나의 그룹으로 명명합니다. 유니코드의 17개 언어판은 모두 Group 00에 있습니다. 유니코드는 17개의 언어판에 한정되어 정의됩니다. 반면 ISO 표준(UCS-4)에서는 모두 128개의 언어판 그룹이 정의될 수 있습니다.
1 Plane = 65,536 code points
1 Group = 256 planes = 256x65,536 = 16,777,216 code points
UCS-4 = 128 groups = 128x16,777,216 = 2,147,483,648 code points
인코딩, Encoding
문자집합을 표현하는 방식을 말합니다. 유니코드는 코드체계 또는 문자집합을 명명하는 것이며 이를 표현하기 위해서는 UTF-8, UTF-16, UTF-32 등과 같은 인코딩이 필요합니다.
UCS-2: Universal Character Set 2(octets)
좀더 정확하게는 Universal Multipe-Octet Coded Character Set 2입니다. ISO/IEC 10646의 용어로 BMP의 65,536 코드를 정의하며, 2바이트로 표현됩니다. 1개의 언어판, 즉 BMP만이 이에 해당합니다. UCS-2는 인코딩 방법이 아니며 문자코드 자체입니다. 인코딩으로 봐도 무방하겠군요. 여기서 octet이라는 용어를 사용했는데 이 용어는 ISO쪽에서 사용하는 용어로, 유니코드 진영에서 사용하는 바이트와 같은 뜻입니다
UCS-4: Universal Character Set 4(octets)
ISO/IEC 10646의 용어로 4바이트로 표현됩니다. 모두 128개의 언어판 그룹, 즉 128*256 언어판 = 32,768 언어판을 정의합니다. 이는 대략 231 = 2,147,483,648개의 코드에 해당합니다. UCS-4는 인코딩 방법이 아니며 문자코드 자체입니다.
UTF-8: UCS Transformation Format, 8-bit form
Unicode 표준의 인코딩 방식중의 하나입니다. 표준에서는 17개 언어판의 문자만을 표현할 수 있으나 기술적으로는 UCS-4 전영역의 문자를 표현할 수 있습니다. 문자에 따라 1 ~ 4(또는 6) 바이트로 표현됩니다.
UTF-16: UCS Transformation Format, 16-bit form
유니코드 3.0에서는 16을 16비트로 해석한 것이 아니라, 그룹 00의 16개 언어판이라고 써 놓았군요. UTF-32의 32가 32비트를 지칭하므로 통일성을 위해 16비트로 이해하시는 게 좋습니다. 16비트로 표현한다는 점에서는 UCS-2와 흡사하지만 대행문자영역(Surrogates)을 이용하여 16개의 보충 언어판 코드를 표현할 수 있는 인코딩입니다. 대행문자영역 2개로 16개의 보충 언어판을 표현할 수 있습니다. UCS-2에서는 65536개의 코드만을 정의할 수 있으나 UTF-16에서는 1백만여자를 더 표현할 수 있습니다.
UTF-32: UCS Transformation Format, 32-bit form
32비트 즉 4바이트로 각 문자를 표현합니다. 이점에서 UCS-4와 동일하지만 17개의 언어판만을 정의한다는 점에서는 UCS-4의 부분집합으로 간주하면 됩니다. UCS-4와 동일하나 0x00000000 ~ 0x0010FFFF 범위만을 문자코드로 간주한다고 이해하시면 됩니다.
표. 각 인코딩별 표현가능한 문자 수 인코딩 그룹 언어판 문자수(표준) 문자수(이론적)
UCS-2 0 0(BMP) 216=65,536 216=65,536
UCS-4 0 ~ 127 0 ~ 32,767 231=2,147,483,648 231=2,147,483,648
UTF-8 0 0 ~ 16 17*216-211=1,112,064 231=2,147,483,648
UTF-16 0 0 ~ 16 17*216-211=1,112,064 17*216-211=1,112,064
UTF-32 0 0 ~ 16 17*216-211=1,112,064 231=2,147,483,648


위의 표에서 표준이라 함은 유니코드 표준에서 정의하는 것을 의미하며, 이론적이라는 것은 표준의 정의를 무시할 때 표현할 수 있는 문자의 수를 의미합니다. 211을 뺀 부분이 있는 데, 이것은 상위대행코드(high surrogates) 1,024개와 하위대행코드(low surrogates) 1,024개의 합입니다. UCS-2에서는 이들이 하나의 문자로 취급되지만, UTF-8, UTF-16, UTF-32의 인코딩에서는 보충언어판의 코드를 지정하는데 사용되므로 코드로 취급할 수 없습니다.

유니코드 인코딩(Unicode Encodings)

유니코드에서 지원하는 인코딩 방식은 UTF-8, UTF-16, UTF-32의 세가지 방식입니다. UTF는 UCS Transformation Format의 약자이며, 뒤에 붙은 숫자는 인코딩에 사용되는 단위의 비트수를 의미합니다. 즉 UTF8은 8비트 단위, UTF16은 16비트 단위, UTF32는 32비트 단위로 문자를 표현합니다. 세가지 방식의 공통점이라면 16개의 보충언어판에 위치한 1,048,576개의 코드를 표현할 때는 4바이트를 사용한다는 점입니다. 하지만 그 방식은 모두 다릅니다. UTF8은 4개의 바이트로, UTF16은 2개의 16비트로, UTF32는 1개의 32비트 단위로 표현합니다. 이제 각 인코딩 방식에 대해 좀더 상세하게 설명하겠습니다.


UTF-8
가장 완벽하게 유니코드 표준을 표현하는 인코딩 방식은 UTF-16이라고 할 수 있습니다. 그런데 이 인코딩에서는 16비트 단위로 하나의 문자가 표현되기 때문에 전통적인 char 형과는 맞지 않는 부분이 있습니다. UTF-16으로 문자열을 표현했을 때 전통적인 char 형으로 이 문자열 취급하게 되면 중간에 널(null, 0)값이 들어가게 되어 문제가 발생합니다. 즉 유니코드를 지원하려면, 지금까지 개발된 모든 프로그램을 재개발해야 한다는 부담이 발생합니다. 현실적으로 이것은 거의 불가능한 일이라고 할 수 있습니다.

이의 대안으로 제시된 인코딩이 바로 UTF8입니다. UTF-8은 문자열의 중간 바이트에서 0이 나타나지 않도록 고안되었습니다. 이를 위해 각 유니코드 문자는 1바이트에서 4바이트까지 가변적으로 인코딩되도록 하고 있습니다. UTF-8에서는 U+0000 ~ U+007F까지의 128자는 1바이트로 표현되는데 이는 ASCII와 동일합니다. 또 U+0080 ~ U+07FF까지는 두 바이트, U+0800 ~ U+FFFF까지는 세 바이트로 표현됩니다. 즉 BMP내의 모든 문자는 1 ~ 3바이트로 표현됩니다. 그리고 나머지 16개의 보충언어판에 위치하는 1,048,576개의 코드는 네 바이트로 인코딩됩니다. 다음 표에서 UCS-4와 UTF-8간의 변환 방법을 나타내고 있습니다.

표. UTF-8과 UCS-4간의 변환 규칙 UCS-4 UTF-8
0x00000000 - 0x0000007F 0xxxxxxx
0x00000080 - 0x000007FF 110xxxxx 10xxxxxx
0x00000800 - 0x0000FFFF 1110xxxx 10xxxxxx 10xxxxxx
0x00010000 - 0x001FFFFF 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
0x00200000 - 0x03FFFFFF 111110xx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx
0x04000000 - 0x7FFFFFFF 1111110x 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx


위의 표에서는 UCS-4와 UTF-8간의 변환 규칙을 보여주고 있습니다만, UCS-4의 모든 영역에 대해서 보여주고 있습니다. 그러나 빨간색으로 칠한 부분을 0x0010FFFF로 변경하고 나머지는 삭제해야 정확한 유니코드의 UTF-8 인코딩이라고 할 수 있습니다. 이를 모두 표현한 것은 6바이트까지 확장하면 UTF-8로도 UCS-4의 전역을 인코딩할 수 있음을 보여주기 위한 것입니다. 아마도 1백만자의 코드이면 지구상에 존재했거나 존재하는 모든 문자를 표현하기에 충분할 것으로 예상됩니다. 우주의 모든 언어코드를 표현해야 할 때가 오면 0x00110000 이후의 영역도 사용할 가능성이 조금이나마 있을까요?

어쨌든, 현재 유니코드 표준에서는 UTF-8이 4바이트까지로 인코딩됩니다. 유니코드에 정의된 각 코드는 반드시 다음과 같은 범위에서 인코딩되어야 합니다. 다음 표는 UTF-8 인코딩에서 각 바이트에 올 수 있는 값을 보여주고 있습니다.

표. 올바른 UTF-8 바이트 배열 Code Points 1st Byte 2nd Byte 3rd Byte 4th Byte
U+0000 ~ U+007F 00 ~ 7F
U+0080 ~ U+07FF C2 ~ DF 80 ~ BF
U+0800 ~ U+0FFF E0 A0 ~ BF 80 ~ BF
U+1000 ~ U+CFFF E1 ~ EC 80 ~ BF 80 ~ BF
U+D000 ~ U+D7FF ED 80 ~ 9F 80 ~ BF
U+D800 ~ U+DFFF ill-formed (surrogate 부분이므로 인코딩되어서는 안됨)
U+E000 ~ U+FFFF EE ~ EF 80 ~ BF 80 ~ BF
U+10000 ~ U+3FFFF F0 90 ~ BF 80 ~ BF 80 ~ BF
U+40000 ~ U+FFFFF F1 ~ F3 80 ~ BF 80 ~ BF 80 ~ BF
U+100000 ~ U+10FFFF F4 80 ~ 8F 80 ~ BF 80 ~ BF



UTF-16
UCS-2와 거의 동일합니다. 이 인코딩의 기본 단위는 16비트, 즉 2바이트입니다. 대행문자 영역 2,048개를 제외한 63,488개의 코드를 문자로 사용할 수 있으며(BMP), 대행문자 영역 2개의 쌍을 이용하여 16개의 보충언어판에 위치한 1,048,576개의 코드를 표현할 수 있습니다.

대행 문자 2개의 쌍으로 1개의 문자를 인코딩한다는 점에서 UTF-16의 인코딩은 가변적인 인코딩이라고 할 수 있겠군요. 즉 기본언어판의 문자는 2바이트로 보충언어판의 문자는 4바이트로 인코딩됩니다. 이러한 인코딩 방식때문에 UTF-16에서는 유니코드 표준에서 지원하는 17개의 언어판 코드만 표현이 가능합니다. 가장 유니코드다운 인코딩이라고 할 수 있겠군요 :-)

이제 대행코드 및 이를 이용한 보충언어판 코드의 인코딩 방식을 알아보겠습니다. 유니코드 3.0에서 BMP의 U+D800부터 U+DFFF까지의 2,048 코드가 대행코드 영역(surrogates)으로 정의되었습니다. 이중 U+D800 ~ U+DBFF의 1,024개를 상위대행코드(high surrogates), U+DC00 ~ U+DFFF의 하위대행코드(low surrogates)라고 합니다. 상위대행코드 1개와 하위대행코드 1개의 조합으로, 즉, 16비트 단위 2개로 보충언어판의 1백만여개의 코드를 지정할 수 있습니다.

두개의 대행코드로 조합된 코드를 UTF-32 코드로 대응시키는 공식은 다음과 같습니다.

CodeValue = (HighSurrogate - 0xD800)*(LowSurrogate - 0xDC00) + 0x10000
여기서 CodeValue는 UTF-32 코드값입니다. 0xD800의 상위대행코드 영역의 시작점이며 0xDC00은 하위대행코드 영역의 첫번째 값입니다. 보충언어판은 0x10000부터 시작하므로 마지막에 이 값을 더해줍니다. 이 공식에 의해 CodeValue는 U+10000 ~ U+10FFFF까지 1048576개의 값을 가질 수 있게 됩니다.


UTF-16에서는 상위대행코드가 나타나면 반드시 뒤이어 하위대행코드가 따라와야 합니다. UCS-2에서는 그렇지 아니한 점이 UTF-16과 UCS-2의 차이점입니다.


UTF-32
UTF-32는 32비트, 즉 4바이트로 모든 유니코드 문자를 표현합니다. UTF-8과 UTF-16에 대비해서 고정길이 인코딩이라고 할 수 있겠군요. 4바이트 단위로 표현한다해서 UCS-4와 동일하게 볼 수는 없습니다. 17개의 언어판만을 대상으로 하는 UCS-4의 부분집합이라고 보시면 됩니다(표준상 그렇습니다). 즉 UTF-32의 인코딩 영역은 0x00000000에서 0x0010FFFF로 제한되어야 합니다(이 제한을 무시하면 UCS-4와 동일합니다).


보충언어판에 대한 UTF-32와 UTF-16의 인코딩
위의 세 인코딩간의 변환은 알고리즘의 문제라고 할 수 있습니다. 유니코드 컨소시엄에서 제공하는 C 언어로 된 변환 코드를 보면 아주 간단하게 세 인코딩간의 변환이 이루어지는 것을 알 수 있습니다.

다음 표에서는 보충언어판(Supplementary Planes)의 코드 인코딩을 UTF-32와 UTF-16 인코딩에 대해 보여줍니다.

표. UTF-32와 UTF-16의 보충언어판 코드 인코딩 언어판(plane) UTF-32 Encoding UTF-16 Encoding
1 0x00010000-0x0001FFFF 0xD800DC00-0xD83FDFFF
2 0x00020000-0x0002FFFF 0xD840DC00-0xD87FDFFF
3 0x00030000-0x0003FFFF 0xD880DC00-0xD8BFDFFF
4 0x00040000-0x0004FFFF 0xD8C0DC00-0xD8FFDFFF
5 0x00050000-0x0005FFFF 0xD900DC00-0xD93FDFFF
6 0x00060000-0x0006FFFF 0xD940DC00-0xD97FDFFF
7 0x00070000-0x0007FFFF 0xD980DC00-0xD9BFDFFF
8 0x00080000-0x0008FFFF 0xD9C0DC00-0xD9FFDFFF
9 0x00090000-0x0009FFFF 0xDA00DC00-0xDA3FDFFF
10 0x000A0000-0x000AFFFF 0xDA40DC00-0xDA7FDFFF
11 0x000B0000-0x000BFFFF 0xDA80DC00-0xDABFDFFF
12 0x000C0000-0x000CFFFF 0xDAC0DC00-0xDAFFDFFF
13 0x000D0000-0x000DFFFF 0xDB00DC00-0xDB3FDFFF
14 0x000E0000-0x000EFFFF 0xDB40DC00-0xDB7FDFFF
15 0x000F0000-0x000FFFFF 0xDB80DC00-0xDBBFDFFF
16 0x00100000-0x0010FFFF 0xDBC0DC00-0xDBFFDFFF



UTF-16과 UTF-8에서의 대행코드 인코딩에 관한 오해
본 절에서는 대행코드(Surrogates)를 이용하여 보충언어판 코드영역을 인코딩할 때 사람들이 종종 오해하는 부분에 대해서 설명하고자 합니다. 앞서 설명했듯이 UTF-16에서는 U+10000 ~ U+10FFFF까지의 코드를 인코딩하기 위하여 상위대행코드(high surrogate) 1개와 하위대행코드(low surrogate) 1개의 쌍으로 표현한다고 하였습니다. 그렇다면 UTF-8에서도 두개의 대행코드를 각각 3바이트씩으로 인코딩할 수 있을 것입니다(대행코드가 제안되었을 초기에 개발된 많은 프로그램들이 실제로 이렇게 구현했었다고 합니다).

이 방식이 제대로 동작할 수는 있지만 세가지 문제점이 있습니다. 첫째 문제점은 보충언어판의 모든 코드가 6바이트로 인코딩된다는 것입니다 - 정상적인 방법으로는 4바이트로 인코딩됩니다. 두번째는 인코딩된 문자열의 해석입니다. 보충언어판의 실제 코드 값은 U+10000 ~ U+10FFFF인데 대행코드를 UTF-8 인코딩에 이용하게 되면, 두개의 대행코드로부터 실제 코드값으로 다시 계산해주어야 하는 번거로움이 있습니다. 세째, 텍스트 인코딩의 안정성이 떨어진다는 것입니다. 세 바이트로 인코딩된 상위대행코드가 나타나면 그 다음에는 반드시 하위대행코드가 따라와야 하므로, 정상적인 방법에 비해 오류가 발생할 위험성이 그만큼 더 커진다는 것이죠.

이러한 문제점들 때문에 UTF-8에서는 대행코드 자체를 "절대로" 인코딩하지 않습니다. 그래서 대행코드영역의 byte sequence를 보면 ill-formed라고 나타내고 있습니다. 또한 대행코드를 인코딩한 것을 사용하게 되면 17개의 언어판 밖에는 표현할 수 없다는 단점도 가지게 될 것입니다. 이러저러한 이유로 UTF-8은 규칙에 따라 순서대로 코드값을 인코딩합니다. 따라서 대행코드영역(Surrogates)은 UTF-16 인코딩에서만 됩니다.

기타

제가 프로그램해야 할 유니코드 처리부분은 UTF-8 인코딩을 사용합니다. 이 인코딩은 바이트단위이기 때문에 컴퓨터 기종에 따라 발생하는 바이트 배열(byte ordering) 순서를 무시할 수 있습니다. 하지만 UTF-16이나 UTF-32와 같은 다바이트 인코딩을 이용하려면 이 문제를 신경써야 한다고 합니다. -- little-endian, big-endian 문제... 이 때문에 바이트순서표식(BOM, Byte Oder Mark)을 사용하기를 권장합니다. 즉 UTF-16이나 UTF-32 문자열의 처음에 BOM을 삽입하면 됩니다. 각 인코딩의 BOM은 다음 표와 같습니다.

표. Little-endian, Big-endian 바이트순서에 따른 BOM 구분 Little-endian Big-endian
UTF-16 0xFFFE 0xFEFF
UTF-32 0x0000FFFE 0x0000FEFF
플랫폼 Intel 프로세서, DEC/Alpha 프로세서 RISC 프로세서, Motorola의 Microprocessor
운영체제 MS Windows, 리눅스/인텔 Unix, MacOS


요즘 생산되는 RISC CPU(MIPS나 ARM 같은)는 big-endian과 little-endian을 모두 지원한다고 하는군요. 나의 생각: 개발자들에게는 무지하게 골치아프겠군요 - 프로그램할 때 두가지 경우 모두에 대해 고려해야 할 테니... 어쨌든 제가 타겟으로 삼고 있는 인코딩은 UTF-8로 정보검색시스템에서 사용할 예정이기 때문에 바이트순서에는 별로 상관없을 것 같아서 다행이군요 :-)

참고문헌[Ken Kunde]에 엔디안문제에 따른 좋은 예제가 있어서 이를 설명할까 합니다. UTF-16에서 0x4E와 0x00 두 바이트로 예를 들겠습니다. little- 또는 big-endian에 따라 둘의 조합은 0x4E00 또는 0x004E가 될 것입니다. Endian 방식에 따라 두 글자는 서로 다른 글자를 나타내게 됩니다. 결과는 다음과 같습니다.

0x4E00/Big-endian : '一' (한자 1)
0x4E00/Little-endian : 'N' (대문자 N)
0x004E/Big-endian : 'N' (대문자 N)
0x004E/Little-endian : '一' (한자 1)
마지막으로 유니코드 컨소시엄이 제공하는 UTF8, UTF16, UTF32 인코딩간의 변환 소스를 소개하겠습니다. 이 소스는 C 언어로 되어 있으며 http://www.unicode.org/Public/PROGRAMS/CVTUTF/에서 다운로드받을 수 있습니다. 이 소스를 분석해보면 각 인코딩 방법에 대해 명확하게 이해할 수 있을 것입니다.

유니코드 관련 문서들을 접하면서 제가 받은 느낌은 유니코드는 유니코드 컨소시움 답게, ISO/IEC 10646은 ISO답게 코드를 정의했다는 것입니다. 그것도 동일한 내용을 정의하면서 느낌이 다르니 참 존경스럽더군요. 무슨 말인고 하니, 유니코드는 업체가 제안한 코드이고 10646은 표준기구에서 제안하는 코드라는 것입니다. 따라서 유니코드 쪽은 구현이 중요한 쟁점이 되고 표준기구에서는 표준의 범용성, 확장성 등 구현 외의 관점들이 더 중요했을 것이라는 것입니다. 이런 이유로 유니코드는 코드의 범위를 1백만자 정도로 한정함으로써 구현이 쉬울 뿐만니라 코드자체로 인한 오류 발생 가능성을 최소화 했다고 볼수 있습니다. 하지만 ISO의 입장에서는 그것이 먼 미래의 일일지라도 새로운 문자의 수용 등을 고려할 수 있는 의미론적인 접근이 더 중요했을 것 같습니다. 그래서 4바이트 정수형 전체를 코드로 사용할 수 있는 가능성을 열어 놓은 것으로 보입니다. 같은 표준으로 참 절묘한 차이를 보여주는군요.

긴 글을 다 읽어주셔서 감사합니다. 그리고 많은 도움이 되었기를 바랍니다. -- 김진숙

참고문헌

Unicode 홈페이지, http://www.unicode.org
Unicode Online Data, http://www.unicode.org/Public
Unicode Public Programs, http://www.unicode.org/Public/PROGRAMS
Ken Lunde, 2001, "Unicode encodings: How to interoperate between UTF-8, UTF-16, and UTF-32" Adobe Systems.
The Unicode Consortium, 2000, "The Unicode Standard 3.0", Addison-Wesley.
반응형

'Know > Java' 카테고리의 다른 글

캐릭터셋 관련  (0) 2006.01.05
불필요한 JSP 재컴파일을 피하는 방법  (0) 2005.12.20
올바른 프레임워크의 선택과 사용법  (0) 2005.07.15
리팩토링 냄새표  (0) 2005.06.26
Triple DES Encryption (DESede)  (2) 2005.03.25