인문주의 사피엔스

유니코드를 제대로 이해하는 방법 본문

지식정보/과학기술

유니코드를 제대로 이해하는 방법

인문주 2021. 11. 8. 23:23
반응형

유니코드(Unicode)는 컴퓨터에서 체계적이고 통일된 방식으로 문자를 처리하기 위해 만든 국제 기술 표준입니다. 유니코드가 널리 사용되면서 관련 업종 종사자 이외의 일반 사용자들도 유니코드, UTF-8, UTF-16 등의 용어를 자주 접하게 되었습니다. 하지만 자주 접하게 되었음에도 불구하고 정작 그 의미에 대해서는 제대로 이해하지 못하는 경우가 많습니다. 더구나 인터넷에서 관련 내용을 검색해서 읽은 후에도 여전히 그 의미를 혼동하거나 오해하기도 합니다.

 

유니코드를 검색하면 보통 ‘문자 인코딩 표준’이라고 설명합니다. 그러면서 U+1 F603처럼 생긴 숫자 부호가 포함된 문자 목록을 보여줍니다. 그런데 검색 결과에는 ‘가변 길이의 문자 인코딩’으로 소개되는 UTF-8 또는 UTF-16 같은 용어도 함께 등장합니다. 그리고 UTF-8을 사용하면 문자가 F0 9F 98 83처럼 인코딩 된다고 설명합니다. 그런 설명을 처음 본 사람들은 이렇게 생각합니다.

 

“그래서 문자 인코딩을 하면 U+1F603이라는 거야 F0 9F 98 83이라는 거야?”

“그냥 U+1F603을 사용하면 되는데 거기에 UTF-8이 왜 나와?”

 

유니코드에 관한 대부분의 설명은 이해하고 보면 틀린 내용이 없지만 모르고 보면 혼동과 오해의 도가니입니다. 혼동과 오해의 이유는 주로 부호(code)와 부호화(encoding)라는 용어가 애매하게 사용되기 때문입니다. 유니코드를 제대로 이해하려면 먼저 용어의 의미를 파악해야 합니다.

 

문자-부호 변환과 부호-부호 변환

부호화는 어떤 정보를 다른 형태의 정보, 즉 부호로 변환하는 것을 말합니다. 문자 정보를 숫자 부호로 변환하는 것이 문자 부호화이고 그 대표적인 예가 유니코드입니다. 결론부터 말하면 유니코드에는 두 개의 변환이 있습니다. 첫째는 문자-부호 변환이고 둘째는 부호-부호 변환입니다.

 

첫째 변환은 문자를 숫자 부호로 변환합니다. 입력은 문자이고 출력은 숫자입니다. 예를 들어 영어 알파벳 A는 65라는 숫자 부호로 변환됩니다. 이 변환의 결과물을 부호화된 문자 집합이라고도 부릅니다. 아스키(ASCII) 코드표는 컴퓨터 역사의 초기부터 사용된 부호화된 문자 집합입니다. 아스키 코드표의 문자 수는 알파벳, 숫자, 기호 등을 다 합쳐 100여 개에 불과했습니다. 이후 동서고금의 수많은 문자와 기호를 포함할 필요성이 자연스럽게 생겼고 그 결과로 만들어진 표준의 하나가 유니코드입니다. 유니코드 버전 14.0에는 총 144,697개의 문자가 등록되어 있고 각 문자에는 고유한 숫자 부호가 할당되어 있습니다. 숫자 부호는 보통 U+1 F603처럼 16진수로 표시됩니다. 

 

둘째 변환은 숫자 부호를 또 다른 형태의 숫자 부호로 변환합니다. 입력은 숫자이고 출력은 또 다른 숫자입니다. 보통 이 변환을 문자 부호화라고 부릅니다. 그리고 바로 그 부분이 사람들을 혼동시키는 지점입니다. 숫자 부호와 함께 또 다른 숫자 부호가 등장하기 때문입니다. 혼동을 피하려면 이 변환의 목적을 이해해야 합니다.

 

둘째 변환의 목적은 데이터의 형태를 바꾸는 것입니다. 형태를 바꾸는 이유는 숫자 부호를 다양한 환경에서 제약 없이 사용하기 위해서입니다. 다양한 환경은 예를 들어 숫자 부호를 저장 장치에 보관하거나 네트워크로 전송하는 상황을 포함합니다. 구체적으로 말하면 유니코드를 16비트 컴퓨터에 전송하거나 저장하는 상황을 생각해볼 수 있습니다. 16비트 컴퓨터는 모든 데이터를 2바이트 단위로 처리합니다. 문제는 유니코드 숫자 부호의 최대 길이가 21비트로서 2바이트를 넘어선다는 점입니다. 숫자 부호를 16비트 컴퓨터에 전송하거나 저장하려면 문제를 해결할 방법을 찾아야 합니다.

 

사람들이 찾아낸 해결 방법은 숫자 부호를 다른 형태로 변환하는 것입니다. 대표적인 방법이 바로 UTF-8 또는 UTF-16 등입니다. 그 방법의 기본 아이디어는 숫자 부호의 이진 데이터를 일정 크기의 단위로 잘라서 옮겨 담아 처리하는 것입니다. 예를 들어 17비트 크기의 U+1F603은 UTF-8을 통해 F0 9F 98 83 같은 1바이트 부호의 배열로 변환됩니다. 즉 둘째 변환은 부호에서 부호로의 변환입니다. UTF-8의 전체 이름이 ‘Unicode Transformation Format - 8bits’라는 사실을 생각하면 둘째 변환의 의미가 더 분명해집니다.

 

다음의 공식 웹사이트에서 유니코드에 관한 다양한 정보를 확인할 수 있습니다.

 

Unicode – The World Standard for Text and Emoji

 

Home

 

home.unicode.org

 

문자에서 부호점으로

첫째 변환의 결과물은 문자-부호 변환표입니다. 다른 이름은 부호화된 문자 집합입니다. 문자와 부호가 양방향으로 변환됩니다. 다음은 변환표의 일부 내용을 표시한 것입니다.

 

문자 10진수 부호점 16진수 부호점 2진수 부호점
% 37 U+25 0010 0101
1 49 U+31 0011 0001
A 65 U+41 0100 0001
a 97 U+61 0110 0001
44032 U+AC00 1010 1100 0000 0000
😃 128515 U+1F603 0001 1111 0110 0000 0011

 

유니코드에서는 부호의 값을 부호점(code point)으로 부릅니다. 위 표의 알파벳 대문자 A에 할당된 부호점은 65입니다. 10진수 65를 16진수 U+41 또는 2진수 100 0001로 나타낼 수도 있습니다. 부호점을 16진수로 표시할 때는 보통 맨 앞에 U+를 붙입니다. 시간이 흐르면서 알파벳 이외의 다양한 문자가 변환표에 추가되었습니다. 예를 들어 한글 음절 ‘가’와 U+AC00, 이모티콘 ‘😃’과 U+1F603이 포함되었습니다.

 

부호점을 256x256, 즉 65536개씩 묶어 부호평면(code plane)으로 정의합니다. 65536은 2바이트, 2의 16 제곱 또는 U+FFFF 크기의 숫자입니다. 부호평면은 0번부터 16번까지 총 17개가 있습니다. 그리고 모든 부호평면을 묶어 부호공간(code space)으로 부릅니다. 각각의 부호평면은 용도와 목적에 따라 다음과 같이 이름이 부여되어 있습니다.

 

시작 부호점 끝 부호점 부호평면 번호 부호평면 이름
U+0000 U+FFFF 0 BMP
U+10000 U+1FFFF 1 SMP
U+20000 U+2FFFF 2 SIP
U+30000 U+3FFFF 3 TIP
U+40000 U+DFFFF 4 ~ 13 unassigned
U+E0000 U+EFFFF 14 SSP
U+F0000 U+FFFFF 15 SPUA-A
U+100000 U+10FFFF 16 SPUA-B

 

부호평면의 갯수가 17개가 된 이유는 뒤에서 설명될 UTF-16 때문입니다. UTF-16은 그 원리상 부호평면 17개 분량의 부호점까지만 처리할 수 있습니다. 따라서 부호점의 총수는 65536x17 또는 U+10FFFF로부터 1,114,112개로 계산됩니다. 하지만 여기서 UTF-16의 도구로 사용되는 U+D800 ~ U+DFFF 범위의 2048개를 제외하면 유효 부호점의 갯수는 1,112,064개가 됩니다. 그 가운데 실제 문자가 등록된 부호점의 수는 유니코드 버전 14.0을 기준으로 144,697개입니다. 다음은 각 부호평면의 전체 이름과 내용입니다.

 

이름 내용
Basic Multilingual Plane (BMP) 기본 다국어 평면으로서 대부분의 현대 문자와 부호를 포함하고 있습니다. 한글의 경우 한글 자모가 U+1100 ~ U+11FF 범위에, 한글 음절이 U+AC00 ~ U+D7A3 범위에 정의되어 있습니다.
Supplementary Multilingual Plane (SMP) 추가 다국어 평면으로서 이집트 상형문자 등의 고대 문자, 현대 음악 기호, 수학 부호, 이모지 등을 포함하고 있습니다.
Supplementary Ideographic Plane (SIP) 추가 표의문자 평면으로서 한중일 통합 한자가 포함되어 있습니다.
Tertiary Ideographic Plane (TIP) 3차 표의문자 평면으로서 한중일 통합 한자가 포함되어 있습니다.
부호평면 4부터 13까지 아직 어떤 문자도 할당되지 않은 영역입니다.
Supplementary Special-purpose Plane (SSP) 추가 특수목적 평면으로서 원래 태그를 표시하는 특수 용도로 만들어졌지만 지금은 목적이 바뀌어 국기를 표시하는 데 사용되고 있습니다.
Supplementary Private Use Area A (SPUA-A) 추가 개인 용도 영역 평면 A입니다.
Supplementary Private Use Area B (SPUA-B) 추가 개인 용도 영역 평면 B입니다.

 

반응형

 

부호점에서 부호단위로

둘째 부호화는 부호점(code point)을 부호단위(code unit)로 바꾸는 변환입니다. 부호점의 변환이 필요한 이유를 문자-부호 변환표에서 찾을 수 있습니다.

 

문자 10진수 부호점 16진수 부호점 2진수 부호점
% 37 U+25 0010 0101
1 49 U+31 0011 0001
A 65 U+41 0100 0001
a 97 U+61 0110 0001
44032 U+AC00 1010 1100 0000 0000
😃 128515 U+1F603 0001 1111 0110 0000 0011

 

여기서 부호점을 저장하거나 전송하는 상황을 사실적으로 보여주는 것은 2진수 부호점입니다. 부호점을 16비트 컴퓨터에 저장하거나 전송하는 상황을 생각하면 마지막 줄에 있는 이모티콘의 경우에 문제가 생긴다는 사실을 알게 됩니다. 이모티콘의 2진수 부호점의 크기가 최소 17비트 또는 3바이트이기 때문입니다. 해결 방법은 부호점의 이진 데이터를 일정 크기의 단위로 잘라서 옮겨 담아 처리하는 것입니다.

 

UTF-32 (Unicode Transformation Format - 32bits)

UTF-32 또는 UCS-4는 가장 쉽고 단순한 해결 방법입니다. 4바이트의 부호단위에 유니코드의 가장 큰 부호점인 U+10FFFF의 21비트가 그대로 담기기 때문입니다. 부호점의 데이터가 변경되지 않고 그대로 담기므로 처리 과정이 간단하고 빠릅니다. 하지만 32비트에서 21비트를 뺀 나머지 11비트가 사용되지 않고 낭비되는 단점이 있습니다.

 

UCS-2 (Universal Coded Character Set - 2bytes)

낭비를 줄이려면 부호단위의 크기를 줄여야 합니다. UCS-2의 부호단위는 2바이트이고 부호점의 데이터를 변경하지 않습니다. 따라서 U+0000 ~ U+FFFF 범위의 기본 다국어 평면(BMP)의 부호점들만 처리할 수 있습니다. 그 이상의 부호평면을 처리할 수 없기 때문에 지금은 사용하지 않는 방법입니다.

 

UTF-16 (Unicode Transformation Format - 16bits)

UTF-16는 부호단위가 2바이트임에도 불구하고 모든 부호평면을 처리할 수 있습니다. UCS-2와 달리 부호점의 데이터를 변경하기 때문입니다. UTF-16의 핵심 아이디어는 부호점을 16비트 이하인 경우와 초과하는 경우로 분리하는 것입니다. 16비트 이하인 부호점은 그대로 사용합니다. 16비트보다 큰 부호점은 10비트씩 나누어 각각을 16비트에 담습니다. 결과적으로 UTF-16의 출력은 2바이트 또는 4바이트의 크기가 됩니다. 다음은 앞에 나온 예제를 UTF-16으로 변환한 결과입니다.

 

16진수 부호점 2진수 부호점 2진수 UTF-16 16진수 UTF-16
U+25 0010 0101 0000 0000 0010 0101 00 25
U+31 0011 0001 0000 0000 0011 0001 00 31
U+41 0100 0001 0000 0000 0100 0001 00 41
U+61 0110 0001 0000 0000 0110 0001 00 61
U+AC00 1010 1100 0000 0000 1010 1100 0000 0000 AC 00
U+1F603 0001 1111 0110 0000 0011 1101 1000 0011 1101 1101 1110 0000 0011 D8 3D DE 03

 

위에서부터 다섯 개의 부호점들은 모두 크기가 16비트 이하입니다. 따라서 UTF-16의 출력값이 입력값과 동일합니다. 마지막 줄의 U+1F603은 2진 데이터의 크기가 17비트입니다. 크기가 17비트에서 21비트인 U+10000 ~ U+10FFFF 범위의 부호점들은 UTF-16에 의해 다음과 같이 변환됩니다.

 

(1) 0x1F603에서 0x10000을 빼서 0x0F603으로 만듭니다. (0x10000은 복호화 또는 역변환을 할 때 다시 더해집니다)

 

0001 1111 0110 0000 0011 (0x1F603)
-0001 0000 0000 0000 0000 (0x10000)
=0000 1111 0110 0000 0011 (0x0F603)

 

(2) 0x0F603을 상하위로 10비트씩 분리합니다.

 

00 0011 1101 (0x003D, 상위 10비트)
10 0000 0011 (0x0203, 하위 10비트)

 

(3) 상하위 각각에 0xD800, 0xDC00을 더해서 16비트로 변환합니다.

 

0000 0000 0011 1101 (0x003D)

+1101 1000 0000 0000 (0xD800)

=1101 1000 0011 1101 (0xD83D)

 

0000 0010 0000 0011 (0x0203)

+1101 1100 0000 0000 (0xDC00)

=1101 1110 0000 0011 (0xDE03)

 

(4) 변환된 상하위 16비트를 이어붙여 최종 결과를 만듭니다.

 

1101 1000 0011 1101 1101 1110 0000 0011 (D83D DE03; big endian)

 

0x1F603이 UTF-16을 통해 D83D DE03으로 변환되었습니다. UTF-16은 마이크로소프트 윈도나 자바 프로그래밍 언어 등에서 사용되는 유니코드 변환 형식입니다.

 

UTF-8 (Unicode Transformation Format - 8bits)

부호단위의 크기를 1바이트까지 줄이면 데이터의 낭비는 더 줄어들고 변환 과정은 더 복잡해집니다. UTF-8의 부호단위는 1바이트입니다. 변환된 결과는 1바이트부터 4바이트까지 가변 길이입니다. UTF-8은 다음과 같이 부호점의 범위별로 각각 다른 형식을 적용합니다.

 

16진수 부호점 2진수 UTF-8
U+0000~U+007F 0xxx xxxx
U+0080~U+07FF 110x xxxx 10xx xxxx
U+0800~U+FFFF 1110 xxxx 10xx xxxx 10xx xxxx
U+10000~U+10FFFF 1111 0xxx 10xx xxxx 10xx xxxx 10xx xxxx

 

(1) U+0000 ~ U+007F 범위의 7비트 값은 그대로 1바이트에 담깁니다.

 

(2) U+0080 ~ U+07FF 범위의 8비트부터 11비트 범위의 값은 5비트와 6비트로 분리되어 각각 110x xxxx 10xx xxxx에 담깁니다.

 

(3) U+0800 ~ U+FFFF 범위의 12비트부터 16비트 범위의 값은 4비트, 6비트, 6비트로 분리되어 각각 1110 xxxx 10xx xxxx 10xx xxxx에 담깁니다.

 

(4) U+10000 ~ U+10FFFF 범위의 17비트부터 21비트 범위의 값은 3비트, 6비트, 6비트, 6비트로 분리되어 각각 1111 0xxx 10xx xxxx 10xx xxxx 10xx xxxx에 담깁니다.

 

다음은 앞에 나온 예제를 UTF-8로 변환한 결과입니다.

 

16진수 부호점 2진수 부호점 2진수 UTF-8 16진수 UTF-8
U+25 0010 0101 0010 0101 25
U+31 0011 0001 0011 0001 31
U+41 0100 0001 0100 0001 41
U+61 0110 0001 0110 0001 61
U+AC00 1010 1100 0000 0000 1010 1100 0000 0000 EA B0 80
U+1F603 0001 1111 0110 0000 0011 1111 0000 1001 1111
1001 1000 1000 0011
F0 9F 98 83

 

길이가 7비트 이하인 위에서부터 네 개의 부호점들은 UTF-8의 결과와 동일합니다. 길이가 16비트인 U+AC00은 다음과 같이 3바이트로 변환됩니다.

 

1010 1100 0000 0000 (U+AC00)

1010 110000 000000 (4비트, 6비트, 6비트로 분리)

1110 xxxx 10xx xxxx 10xx xxxx

1110 1010 1011 0000 1000 0000 (EA B0 80)

 

길이가 17비트인 U+1F603은 다음과 같이 4바이트로 변환됩니다.

 

0 0001 1111 0110 0000 0011 (U+1F603)

000 011111 011000 000011 (3비트, 6비트, 6비트, 6비트)

1111 0xxx 10xx xxxx 10xx xxxx 10xx xxxx

1111 0000 1001 1111 1001 1000 1000 0011 (F0 9F 98 83)

 

U+AC00이 EA B0 80으로 U+1F603이 F0 9F 98 83으로 변환되었습니다. UTF-8은 웹에서 가장 흔하게 사용되는 유니코드 변환 형식입니다.

 

다음의 공식 웹사이트에서 유니코드 표준에 관한 자세한 내용을 확인할 수 있습니다.

 

Unicode 15.0.0

 

Unicode 15.0.0

Unicode® 15.0.0 This page summarizes the important changes for the Unicode Standard, Version 15.0.0. This version supersedes all previous versions of the Unicode Standard. A. Summary B. Technical Overview C. Stability Policy Update D. Textual Changes and

www.unicode.org

 

한글 음절 조합 알고리즘

U+AC00부터 U+D7A3까지의 부호점들은 한글 음절을 나타냅니다. 각각의 부호점은 다음 식을 통해 한글 자모를 조합하여 계산할 수도 있습니다.

 

((초성 색인) x 588 + (중성 색인) x 28 + (종성 색인)) + 44032

 

다음은 초성의 색인입니다.

 

[0]ㄱ [1]ㄲ [2]ㄴ [3]ㄷ [4]ㄸ [5]ㄹ [6]ㅁ [7]ㅂ [8]ㅃ [9]ㅅ [10]ㅆ [11]ㅇ [12]ㅈ [13]ㅉ [14]ㅊ [15]ㅋ [16]ㅌ [17]ㅍ [18]ㅎ

 

다음은 중성의 색인입니다.

 

[0]ㅏ [1]ㅐ [2]ㅑ [3]ㅒ [4]ㅓ [5]ㅔ [6]ㅕ [7]ㅖ [8]ㅗ [9]ㅘ [10]ㅙ [11]ㅚ [12]ㅛ [13]ㅜ [14]ㅝ [15]ㅞ [16]ㅟ [17]ㅠ [18]ㅡ [19]ㅢ [20]ㅣ

 

다음은 종성의 색인입니다.

 

[0]받침 없음 [1]ㄱ [2]ㄲ [3]ㄳ [4]ㄴ [5]ㄵ [6]ㄶ [7]ㄷ [8]ㄹ [9]ㄺ [10]ㄻ [11]ㄼ [12]ㄽ [13]ㄾ [14]ㄿ [15]ㅀ [16]ㅁ [17]ㅂ [18]ㅄ [19]ㅅ [20]ㅆ [21]ㅇ [22]ㅈ [23]ㅊ [24]ㅋ [25]ㅌ [26]ㅍ [27]ㅎ

 

예를 들어 음절 '한'의 부호점은 다음과 같이 계산할 수 있습니다.

 

(18 x 588 + 0 x 28 + 4) + 44032 = 54620 = U+D55C

 

U+D55C에 다양한 변환 형식을 적용하면 다음과 같이 변환됩니다.

 

변환 형식 변환된 부호
UTF-32 00 00 D5 5C
UCS-2 D5 5C
UTF-16 D5 5C
UTF-8 ED 95 9C

 

반응형
Comments