콘텐츠로 이동

유니코드 코드 포인트와 UTF-8·UTF-16 인코딩

문자마다 부여한 고유한 숫자 값을 코드 포인트라고 한다. ASCII는 영문자와 숫자만 담아서 아랍어나 이모지 같은 문자를 표현할 수 없다. 유니코드는 이 문제를 풀기 위해 만든 규격이다.

  • 유니코드 1.0은 65,536개 문자(U+0000 ~ U+FFFF)를 담았다. 이 범위를 0평면, BMP(Basic Multilingual Plane)라고 부른다.
  • 6만여 개로는 부족해서 이후 평면을 늘렸다. 지금은 0평면부터 16평면까지 17개 평면, U+0000 ~ U+10FFFF를 쓴다.

예를 들어 안녕의 코드 포인트는 U+C548, U+B155이고, a안b녕은 U+0061 U+C548 U+0062 U+B155다.

코드 유닛은 바이트를 나누는 단위다. 같은 바이트열이라도 코드 유닛을 무엇으로 보느냐에 따라 다른 문자열로 읽힌다. 아래는 a안b녕의 코드 포인트 값을 패딩 없이 이어 붙인 61 C5 48 62 B1 55를 읽은 결과다.

읽는 방식 나눈 결과 해석
1바이트 단위(Latin-1) 61 C5 48 62 B1 55 aÅHb±U
2바이트 단위 61C5 4862 B155 懅䡢녕
2바이트 단위, a·b에 00 패딩 0061 C548 0062 B155 a안b녕

코드 유닛을 맞추려면 a와 b에 00 패딩이 들어간다. 코드 유닛이 클수록 영문자에 낭비되는 메모리가 늘고, 코드 유닛이 코드 포인트보다 작으면 한 유닛에 문자를 담을 수 없다.

인코딩은 코드 포인트를 코드 유닛의 나열로 표현하는 일이고, 디코딩은 그 반대다. 유니코드 인코딩은 코드 유닛 크기에 따라 UTF-8(1바이트), UTF-16(2바이트), UTF-32(4바이트)로 나뉜다.

UTF-8은 한 바이트의 최상위 비트를 식별자로 써서 0xxxxxxx 형태로 U+0000 ~ U+007F를 표현한다. 이 범위는 ASCII와 같다. 그보다 큰 코드 포인트는 여러 바이트로 나눈다.

코드 포인트 범위 바이트 형식
U+0000 ~ U+007F 0xxxxxxx
U+0080 ~ U+07FF 110xxxxx 10xxxxxx
U+0800 ~ U+FFFF 1110xxxx 10xxxxxx 10xxxxxx
U+10000 ~ U+10FFFF 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx

안(U+C548)은 16비트가 필요하므로 3바이트 형식에 비트를 채운다.

U+C548 = 1100 0101 0100 1000
1110xxxx 10xxxxxx 10xxxxxx
11101100 10010101 10001000 = EC 95 88

안은 코드 포인트로는 2바이트지만 UTF-8로는 3바이트가 된다. 영문자와 숫자는 1바이트로 끝나서 효율적이지만, 한글처럼 BMP 뒤쪽에 있는 문자는 오히려 바이트 수가 늘어난다.

UTF-16은 코드 유닛이 2바이트라서 0평면(BMP) 문자는 변환 없이 한 유닛으로 표현한다. 1~16평면의 문자는 두 유닛, 즉 서로게이트 쌍으로 표현한다.

  • 1~16평면은 U+10000을 빼면 0x00000 ~ 0xFFFFF, 20비트로 모두 표현된다.
  • 이 20비트를 10비트씩 나눠 110110xx xxxxxxxx(상위 서로게이트)와 110111xx xxxxxxxx(하위 서로게이트)에 담는다.
  • 상위 서로게이트는 U+D800 ~ U+DBFF, 하위 서로게이트는 U+DC00 ~ U+DFFF다. 유니코드는 이 구간을 문자에 배정하지 않고 비워 두었기 때문에, BMP 문자와 서로게이트가 섞여도 구분할 수 있다.

예를 들어 🠝(U+1F81D)는 UTF-16에서 D83E DC1D 두 유닛이 된다. 서로게이트 쌍은 UTF-16의 개념이다. UTF-8의 멀티바이트 시퀀스(EC 95 88 등)는 서로게이트 쌍이 아니다.

UTF-16과 UTF-32는 한 코드 유닛이 여러 바이트라서 바이트를 놓는 순서가 문제가 된다. 소프트웨어에 따라 빅 엔디언(BE)을 쓰기도 하고 리틀 엔디언(LE)을 쓰기도 한다. 그래서 파일 앞에 BOM(Byte Order Mark)을 붙여 순서를 알린다. UTF-8은 코드 유닛이 1바이트라 순서 문제가 없지만, 인코딩 표시용으로 3바이트 BOM(EF BB BF)을 붙이기도 한다.

안녕🠝(U+C548 U+B155 U+1F81D)의 바이트 수는 다음과 같다.

인코딩 BOM 없음 BOM 포함
UTF-8 10 (3 + 3 + 4) 13
UTF-16 8 (2 + 2 + 4) 10

🠝은 1평면 문자라서 0평면만 표시하는 프로그램에서는 제대로 보이지 않는다.

UTF-8은 영문자를 1바이트로 표현하고, 바이트 순서가 없어 네트워크 통신에서 BOM 문제를 피할 수 있다.