컴퓨터 속의 한글

컴퓨터 속에서 맨날 한글을 봐오고 있으면서도 막상 한글 코드에 대해서 관심 있어 하는 프로그래머는 그리 많지 않은 듯 싶다. 그리고 관심을 가진다고 해도 잘 정리된 문서가 없는 듯 싶어서 내가 아는 내용을 살짝 정리해 봅니다.
우선 한글은 조합형, 완성형, 확장 완성형, iso2022-kr, unicode 등으로 표현이 가능하며… 조합형, 완성형, 확장 완성형, iso2022-kr 등은 symbol table 과 한자 테이블 또한 가지고 있지만 여기서 관련된 내용은 거의 얘기하지 않을 겁니다.

  1. 조합형 (Johab)

    ASC II 범위에 있는 글자는 그대로 프린트하고 한글인 경우 MSB 를 1로 세팅하고 나머지 비트를 초성(5bit) / 중성(5bit) / 종성(5bit) 을 표현하는데 사용한다. 이론 상으로 한글 11172 자를 모두 표현해낼 수 있으며 한글 입력을 처리하기가 아주 쉽다. 다만 Microsoft 에서 완성형을 선택함에 따라 잊혀진 인코딩이 되어가고 있다.

  2. 완성형 (EucKR)

    ksx1001 에 정의되어 있으며 역시나 ASC II 범위의 글자는 그대로 프린트 한다. 한글의 경우 연속된 두 개의 바이트를 이용해서 표현하게 되며 첫번째 바이트와 두번째 바이트 모두 0xA1~0xFE 사이의 값을 가진다.
    한글은 2350 자 밖에 지원하지 않지만 MS windows 에서 선택함에 따라 널리 사용되고 있다.

  3. 확장완성형 (Unified Hangul Code)

    Microsoft 에서 EucKR 에 몇 가지 글자를 더 추가한 인코딩으로 UHC, cp949 등으로도 불린다. 빈 공간에 글자를 추가해 넣었기 때문에 바이너리 값 그대로 정렬을 시도할 경우 한글의 가나다라 순서대로 정렬되지 않는 문제가 있다.
    EucKR 과 마찬가지로 한글을 표현하는데는 2바이트를 사용하며 첫번째 바이트는 0x81~FE 사이 두번째 바이트는 0x41~5A, 0x61~7A, 0x81~FE 영역을 차지한다.

  4. iso2022-kr

    EucKR 을 7bit 만 사용하며 표현하는 방식으로 RFC1557 에 정의되어 있다. Designator Sequence (0x1B 24 29 43), SO (0x0E), SI (0x0F) 등을 이용해 EucKR 을 7bit 로 변환시킨다.
    Designator sequence 는 non ASC character 를 만나기 전에 아무 때나 한 번 출력하면 된다. non asc character들을 출력할 때는 ‘SO char&0x7F char&0x7F char&0x7F SI’와 같은 식으로 SO를 먼저 하나 출력하고, msb가 제거된 non sac character 값들을 모두 출력한 뒤 SI를 출력하면 된다. 이런 과정을 반복하면 EucKR 은 iso2022-KR 로 변환된다.
    그 과정을 pseudo code 로 표현하자면 (위에 설명이 좀 복잡하게 보이는데 실제로 보면 간단하다.)

    (위의 pseudo code 에서는 Designator Sequence 를 맨 앞에 출력을 했지만 iconv 등에서는 처음으로 non asc character 를 만났을 때 출력하고 있다.)

위의 네가지 인코딩 이 그동안 많이 쓰여왔던 인코딩 들인데 국제화 시대인 지금은 오로지 한글만을 표현할 수 있는 저런 인코딩으로는 뭔가 부족하다. 그렇기 때문에 유니코드 콘소시움에서는 여러가지 언어를 함께 표현할 수 있도록 unicode 라는 글자집합(character set)을 만들어 냈으며, 유니코드를 표현하는 5가지의 encoding 을 제공하고 있다. (ucs2, ucs4, utf-7, utf-8, utf-16)

  1. ucs2

    2바이트 고정형 인코딩, unicode 와 4.0 버젼과 동일하다. Endian 관련해서 Big Endian 과 Little Endian 으로 표현이 가능하다.

  2. ucs4

    4바이트 고정형 인코딩. 길이를 제외하곤 ucs2 와 거의 동일하다.

  3. utf-7

    unicode 의 mail safe version. RFC1642 에 정의되어 있다. BASE64 와 비슷한 방식을 통해 unicode 를 7bit 로 표현하게 된다.

  4. utf-8

    가변형 인코딩으로 1바이트영역은 Asc II 와 100% 호환된다. 0x00 이 사용되지 않으므로 전통적으로 C 언어에서 사용해온 null terminated 방식을 사용하는데 문제가 없고 2바이트 이상을 사용하는 경우 특수한 규칙을 가지고 있기 때문에 validation 이 가능하다.

  5. utf-16

    ucs2 와 ucs4 를 적절하게 혼합해서 사용하는 방식으로 BMP (Basic Multilingual Plane) 에 들어있는 글자는 2 바이트로 표현하게 되고 그 외의 글자들은 4 바이트를 이용해서 표현하게 된다.

utf-7 과 utf-8 을 제외하고는 byte order 가 중요시 되기 때문에 BOM(Byte Order Mark) 를 문서 맨 앞에 삽입해야 한다. 그렇기 때문에 Unicode 로 작성된 텍스트 파일의 경우 BOM 을 이용해 어떤 인코딩을 사용하는 지지 알아내는게 가능하다. (utf-8 을 위한 BOM 도 존재하지만 utf-8 의 경우 Byte Order 가 정해져 있기 때문에 BOM 을 의무적으로 삽입할 필요는 없다.)

Encoding UTF-8 UTF-16BE UTF-16LE UTF-32BE UTF-32LE
‘가’ EA B0 80 AC 00 00 AC 00 00 AC 00 00 AC 00 00
Smallest code point 0000 0000 0000 0000 0000
Largest code point 10FFFF 10FFFF 10FFFF 10FFFF 10FFFF
Code unit size 8 bits 16 bits 16 bits 32 bits 32 bits
Byte order N/A big-endian little-endian big-endian little-endian
Byte order mark EF BB BF FE FF FF FE 00 00 FE FF FF FE 00 00
Minimal bytes 1 2 2 4 4
Maximal bytes 4 4 4 4 4

대강 ucs2, ucs4, utf-8, utf-16, utf-32 의 특징을 정리하면 위의 표와 같다. UTF-8 의 경우엔 ASC II 와 호환이 되기 때문에 영어권 언어들에서 별다른 조작 없이도 문제를 일으키지 않게 되므로 널리 쓰이고 있다. UTF-8 과 UCS2 사이의 변환은 아래와 간단한 규칙을 통해 이루어지게된다.

  1. 0x00~0x7F 까지는 그냥 표시한다.
  2. 0x80~07FF 까지는 B110xxxxx 10xxxxxx (2바이트)
  3. 0x0800-FFFF 까지는 B1110xxxx 10xxxxxx 10xxxxxx (3바이트)
  4. 이후는 UCS4 영역이고 위와 같은 방식으로 4바이트까지 확장됩니다.

보다시피 ASC II 는 그대로 표현하고 있습니다. 그리고 2바이트 이상을 사용하는 문자에서는 2진수 기준으로 앞에 붙어있는 1의 개수가 그 글자를 표시하는데 사용된 바이트 수를 나타내며, 2번째 바이트부터는 맨 앞에 10 을 붙여줌으로 이건 글자의 시작이 아니라는 걸 표시하게 됩니다. 위의 두 규칙을 지키면서 unicode 를 2진수로 바꾸어 xx 로 표시된 영역에 차례로 배치하게되면 ucs -> utf-8 변환은 끝이 납니다. 한글은 UTF-8 로 표현하게 될 경우 한 글자 당 3 바이트 씩을 차지하게 됩니다.
그리고 이 유니코드에서 한글이 차지하는 영역은 아래와 같습니다.

  1. U+1100: 조합형 영역

    첫가끝 코드라는 이름으로도 불리고 있으며 초성(첫), 중성(가운데), 종성(끝)이 각기 다른 영역에 배치되어 있다. 이 영역에 있는 글자들을 이용하면 한글 고어까지도 표현이 가능해진다.
    한글 한 글자를 표현하는데 많은 저장공간이 필요하다는 점과, iconv 에서 첫가끝 영역 -> Euc-KR, iso2022-kr, UHC 로의 변환이 불가능한 점 등이 약간의 문제라고 생각됨.

  2. U+3130: 한글 자모 영역

    ㄱ,ㄴ,ㄷ,ㄹ, … , ㅏ,ㅑ,ㅓ,ㅕ, … 등의 자음과 모음이 배치되어 있는 영역

  3. U+AC00: 완성형 영역

    가각… 같이 이미 완성되어 있는 한글 케릭터가 11172 자 배치되어 있다.

Link:

  • http://mytears.org/resources/doc/Hangul_Code/
  • http://www.jinsuk.pe.kr/Unicode/Unicode_intro-kr.html
  • http://ko.wikipedia.org/wiki/UTF-8
  • http://ko.wikipedia.org/wiki/UTF-16
  • http://unicode.org
  • http://www.unicode.org/charts/PDF/U1100.pdf
  • http://www.unicode.org/charts/PDF/U3100.pdf
  • http://www.unicode.org/charts/PDF/UAC00.pdf

Similar Posts:

Facebooktwitterlinkedinmail

27 thoughts on “컴퓨터 속의 한글”

  1. 음….
    같이 한 번 대규모 정리와 가이드를 써보실 생각은 없으십니까…!!
    -_-;; 케빌 님이랑 몇몇 분 모아서 뭔가 하고픈데.. 의지가 약하군요.

  2. 조합형은 과정이 어땠든간에 완성형과의 싸움에서 졌고… 요 근래 조합형을 쓰는 경우는 찾아보기가 힘들지 않나 싶습니다…
    예전에 대강 써놓고… 다듬는다는게… 귀차니즘으로 인해 -_-;; 이렇게 방치되고 있군요…
    하튼 제가 다시 생각해본다고 조합형이 현재 그다지 쓰이고 있지 않다는 사실이 뒤집히지는 않을거 같군요… 담 번엔 익명이 아니라 이름정도는 남겨주셨음 하는군요…

  3. UCS-4를 가리키는 MIME 이름은 UTF-32입니다.
    UTF-8은 최대 6바이트가 아니라 최대 4바이트입니다. ISO 10646에서 처음에 2^16짜리 multilingual plane을 2^15개 만들어서 2^31자를 담을 수 있도록 할 계획이었지만, Unicode와 통합 과정에서 plane 수를 17개로 줄여서 담을 수 있는 글자의 총 수가 2^20 + 2^16자로 줄었습니다. (그래서, 유니코드 코드 포인트를 나타낼 때에 “20.1 비트”가 필요하다고 합니다.) 따라서, UTF-8 역시 U+10FFFF 이후의 부분을 나타낼 필요가 없기 때문에 최대 길이가 4바이트로 줄었습니다.
    EUC-KR=완성형이란 등식도 약간 어폐가 있습니다. EUC-KR이나 ISO-2022-KR은 모두 ISO 2022 체계에서 나온 KS X 1001과 US-ASCII/ISO 646:KR/KS X 1003을 묶어서 표현하기 위한 인코딩 방법입니다. EUC-KR이 널리 쓰이게 된 것은 MS DOS에서 채택한 탓도 없다고 할 수 없지만, 한국 표준 당국이 ISO 2022 체계에 부합하지 않아서 Unix에서 사용하기에 문제가 많은 조합형을 꺼린 탓도 매우 큽니다. 참, 아직 조합형을 그리워하시는 분도 계실지 모르지만, 유니코드가 널리 쓰이는 지금에 와서 과거의 조합형을 쓸 이유는 눈꼽만치도 없습니다. (단, 조합형은 KS X 1001:1998 – KS C 5601-1992-에 보조 인코딩으로 들어 있습니다.)
    또, UTF-16을 UCS-2와 UCS-4를 혼합해서 쓰는 방식이라고 하는 것은 오해의 소지가 너무 많습니다. BMP의 영역 가운데 1024개씩을 high surrogate(0xD800 – 0xDBFF)와 low surrogate(0xDC00 – 0xDFFF) 영역으로 따로 할당한 후 , 이 두 영역에서 각각 뽑은 16비트 코드 유니트 2개(각각이 16bit니까 둘을 더하면 32bit=4바이트겠지요)를 조합해서(이 조합을 surrogate pair라고 합니다) BMP 영역 밖의총 2^20개의 글자(즉, U+10000에서 U+10FFFF)를 표현합니다. 각 surrogate 영역이 1024=2^10이니까 둘을 조합하면 2^20자를 표현할 수 있겠지요. UCV(Unicode Code Value)에서 UTF-16으로 변환하는 식은 다음과 같습니다.
    high surrogate = (ucv – 0x10000) >> 10 + 0xD800
    low surrogate = (ucv – 0x10000) & 0x03ff + 0xDC00
    (위 식은 좀더 optimize할 수 있습니다. 예를 들어,
    http://lxr.mozilla.org/seamonkey/source/xpcom/string/public/nsCharTraits.h
    를 보세요.)
    예를 들어, U+10400 (Desert Alphabet의 첫번째 글자)는 UTF-32BE에서는 “00 01 04 00″이지만, UTF-16BE에서는 ‘D8 01 DC 00″입니다.
    유니코드 관련 용어에 대해 좀더 명확하게 알고 싶으시면 Unicode.org의 glossary를 읽어 보시기 바랍니다.

  4. UTF-7은 Unicode 표준에 들어 있지 않습니다. 앞으로 쓰지 않는 게 더 좋은 방법입니다. 유일하게 쓰이는 곳은 IMAP 서버와 클라이언트 사이의 폴더 이름 교환입니다. 이 경우에도 원래 UTF-7이 아니라 IMAP을 위해 변형된 UTF-7이 쓰입니다.

  5. 코멘트 하나가 모더레이트 된 상태였네요. 그나저나 cp949 가 한글음절 11172 자를 전부 지원하지 못한다는건 제가 잘못 알고 있었던 사실인가보군요. 긴 글 감사합니다.
    p.s) utf-8 바이트 수 관련해선 cjkv information processing 에는 6바이트 라고 적혀 있고, 인터넷에서 찾았던 다른 자료에는 또 다르게 적혀 있어서 그냥 책에 있는 내용대로 적었던 것인데, 그 책이 출판된 이후로 바뀐 것이었나보네요. 🙂

  6. 난데없이 옛 글에 커멘트를 장황하게 달았는데, 잘 읽어 주셔서 감사합니다.
    CJKV I.P가 출판된 게 2000년 초인 것으로 기억합니다. ISO/IEC에서 ISO 10646을 개정하면서 U+10FFFF 다음에 글자를 배정하지 않기로 선언한 것은 아마 더 뒤였습니다. 그렇게 해야 Unicode와 ISO 10646이 포함하는 글자나 글자 위치에서 현재에나 미래에도 완전히 동기될 수 있으니까요. 그에 따라 UTF-8을 규정한 IETF RFC도 새롭게 나왔습니다. (UTF-8은 물론 Unicode 표준에서도 규정합니다.)
    x-Windows-949/UHC/통합 완성형이 현대 한글 완성 음절 전부를 모두 다 포함한다는 사실은 CJKV I.P.에도 나왔고, 그 책이 아니라도 꽤 알려진 사실인데 어째서 착각을 하셨을까요? 🙂 http://ftp.unicode.org에서 MS codepage와 Unicode의 대응표를 보아도 알 수 있고요. 그 파일을 받아다가 ‘grep -v “^#’ | grep -i SYLLABLE | wc -l’ … 11172라고 나오지요 🙂

  7. 앗… 조합형 설명에서도 nit이 있네요. nit picking을 너무 많이 한다고 미워하시지 마시길…. 🙂
    KS X 1001:1998(KS C 5601-1992)에 보조 인코딩으로 들어 있는 조합형은 지원하는 한자와 심볼이 KS X 1001:1998의 ‘기본 문자 집합’에 들어 있는 것과 같습니다. 물론, 1980년대 중후반에 쓰던 여러 가지 ‘조합형의 변형’들 가운데에는 한자와 심볼의 수가 더 적은 것도 있었을 수도 있습니다.
    참, ISO 2022 체계에 부합하는 EUC-KR이 그렇지 않은 조합이나 UHC보다 더 나은 점은 하나의 옥텟으로 나타내지는 글자와 2개의 옥텟으로 나타내지는 글자가 겹치는 옥텟을 쓰지 않아서 문자열 중에 어느 옥텟을 하나 주었을 때 그로부터 글자 경계를 알아내기가 비교적 쉽습니다. 조합이나 UHC는 2번째 옥텟이 하나의 옥텟으로 나타내지는 글자와 같은 값을 지니는 경우가 있어서 좀더 복잡한 과정을 거쳐야 합니다. 이런 EUC-KR의 장점은 UTF-8의 장점 중 일부와 같습니다. 물론, UTF-8은 EUC-KR이 지니지 않은 또다른 장점도 지녔습니다. (순전히 코드 배치 관점에서만 본다고 해도)

  8. 그렇군요. 🙂 아 그나저나 glibc 의 iconv (bsd 의 iconv 에도 마찬가지 문제가 있는 걸로 압니다.) 에서 첫가끝 코드를 euc-kr, iso2022-kr, uhc 로 변환하지 못하는 문제가 있는데… 저건 어쩔 수 없는 문제인가요?

  9. iconv에 넘기기 전에 normalization을 해서 NFC로 변환한 후에 넘기면 되겠지요. ISO 10646의 관점에서 보면 U+1100 U+1161은 U+AC00과 같지 않습니다. Unicode의 관점에서는 전자가 후자의 NFD form이고 후자는 전자의 NFC form으로 같은 글자의 다른 표현이지만요. 현재 GNU iconv는 ISO 10646의 입장을 취하고 있습니다. GNU option(POSIX에는 없지만)으로 Unicode/ISO 10646 기반 인코딩이 소스 혹은 타겟 인코딩인 경우 normalization 여부나 종류를 결정할 수 있도록 해 볼 수 있겠네요. 하지만, 그것은 명령행에서나 쓸 수 있지, iconv(3)에서는 쓸 수 없겠지요. iconv(3)에서도 그게 가능하도록 하려면 인코딩 이름에 NFC, NFD 등 꼬리표를 붙여야겠네요. 꼬리표가 안 붙었으면 normalization(정규화)에 신경 쓰지 않고, 붙은 경우엔 적절한 처리를 한다. … GNU libc의 iconv 개발자인 Bruno Haible(sp?)에게 한번 얘기를 해 보겠습니다.
    표준 C lib.에 있는 API만으로는 사실 국제화 프로그램을 짜기가 쉽지 않지요. (손수 많은 일을 해야 하니까요.) 그래서, ICU도 있고, glib나 Windows, Mac OS X에서 제공하는 다른 라이브러리 에 여러 API들이 더해졌고요.

  10. nit 이란 건 처음 들어봤는데, 문서 오류 같은걸 nit 이라고 표현하나보네요. 덕분에 이것저것 많이 배우네요. 감사드립니다. 😀

  11. Pingback: hyangchun's me2DAY
  12. 컴퓨터 속의 한글이라….
    두벌식, 세벌식 얘기도 한 번 해 볼 만 하겠죠?
    혹시 모르신다면? 세벌사랑넷 들러 보셔요.
    아니면 인터넷에서 세벌식 이렇게 쳐서 검색해 보셔도 되고요…

    1. 세벌식에 대해 모르는 건 아니지만 오랫동안 익숙해져 있는 자판을 바꾸는게 쉬운 선택은 아니더라구요. 😉 두벌식, 세벌식 이야기는 물론 재밌는 이야기겠지만 저처럼 두벌식만 사용하고 있는 사람 보다는 두벌/세벌 자판을 모두 사용해본 사람이 쓰는게 훨씬 재밌는 이야기가 되지 않을까 싶네요.

Leave a Reply