이것은 문서의 이전 버전입니다!
유니코드 문자 인코딩1). UTF는 "UCS 변환 포맷"으로 전송·저장에 적합한 표현 방법을 이르는데, UTF-8은 그 중에서도 가장 널리 쓰이는 축에 속한다.
Ken Thompson과 Rob Pike가 Plan 9에서 사용할 목적으로—그래서 원래 이름은 FSS-UTF(File System Safe UTF)이었다—만들었는데 그 설계가 매우 깔끔하고 효율적이라 온갖 곳에서 다 쓰이고 있다. 보통 메모리 상에 들어 있는 유니코드 문자열(UTF-16이나 UTF-32인 경우가 대부분) 빼고는 다 UTF-8을 써도 된다고 해도 과언이 아닐 정도. 웹에서도 웬만한 기존 문자 인코딩보다 많이 쓰이고2) 기존 문자 인코딩의 흔한 문제점(깨진문자 등)을 해결할 수 있어서 꾸준히 대체되는 추세.
UTF-8은 8비트 바이트를 기준으로 하는 인코딩으로, 각 문자는 1바이트에서 4바이트까지의 가변 길이로 표현된다. 실제로 몇 바이트를 쓰는지는 문자의 코드 포인트에 따라 결정된다:
코드 포인트 | UTF-8 | ||
---|---|---|---|
시작 끝 | 비트 패턴 | 비트 패턴 | 바이트 시작 바이트 끝 |
U+0000 U+007F | 0aaaaaaa | 0aaaaaaa | 00 7F |
U+0080 U+07FF | 00000ccc bbaaaaaa | 110cccbb 10aaaaaa | C2 80 DF BF |
U+0800 U+FFFF | ddddcccc bbaaaaaa | 1110dddd 10ccccbb 10aaaaaa | E0 A0 80 EF BF BF |
U+10000 U+10FFFF | 000fffee ddddcccc bbaaaaaa | 11110fff 10eedddd 10ccccbb 10aaaaaa | F0 90 80 80 F4 8F BF BF |
반대로, 각 바이트가 문자의 어떤 부분인지는 비트 패턴을 보고 파악할 수 있다:
비트 패턴 | 바이트 범위 | 설명 |
---|---|---|
0xxxxxxx | 00 - 7F | 코드 포인트 0xxxxxxx . |
10xxxxxx | 80 - BF | 110xxxxx , 1110xxxx 및 11110xxx 바이트 다음에 나오는 "나머지" 바이트. 한 개의 나머지 바이트는 코드 포인트의 6비트를 인코딩한다. |
110xxxyy | C0 - DF | 코드 포인트 00000xxx yy...... 의 시작. 한 개의 나머지 바이트가 따라온다. |
1110xxxx | E0 - EF | 코드 포인트 xxxx.... ........ 의 시작. 두 개의 나머지 바이트가 따라온다. |
11110xxx | F0 - F7 | 코드 포인트 000xxx.. ........ ........ 의 시작. 세 개의 나머지 바이트가 따라온다. |
11111xxx | F8 - FF | (사용하지 않음) |
추가 규칙으로, U+0000 같은 경우 기본적으로는 00
, C0 80
, D0 80 80
, F0 80 80 80
같은 여러 가지 방법으로 인코딩될 수 있지만 이 중 가장 짧은 00
만이 올바른 UTF-8 표현이 된다. 따라서 실제로는 다음과 같은 제약이 추가된다:
80
(U+0000~003F에 대응)과 81
(U+0040~007F에 대응)은 사용하지 않는다.D0
뒤에는 80
부터 9F
까지의 바이트가 올 수 없다(U+0000~07FF에 대응).F0
뒤에는 80
부터 8F
까지의 바이트가 올 수 없다(U+0000~FFFF에 대응).F4
뒤에는 90
부터 BF
까지의 바이트가 올 수 없다(U+110000~13FFFF에 대응).F5
(U+140000~17FFFF에 대응)부터 F7
(U+1C0000~1FFFFF에 대응)까지는 사용하지 않는다.결과적으로 모든 유니코드 문자열은 정확히 하나의 올바른 UTF-8 표현을 갖는다. (종종 이 성질을 무시하고 대강 구현하는 경우가 있는데 십중팔구 보안취약점으로 자주 깨지곤 한다…) 기술적으로 말하면 서로게이트 문자도 오면 안 되는데, 현실에서는 안 그런 경우도 있다(수정 UTF-8 참고).
UTF-8의 설계 원칙은 당시 요구되던 "적절한" 유니코드 문자 인코딩의 성질을 정확히 만족한다:
그 밖에 바이트순서마크가 붙은 UTF-16과의 구분이 자명하다거나(바이트 FE
/FF
가 나올 수가 없으므로) 하는 소소한 장점도 있지만 딱히 의도한 것 같지는 않다.
기술적으로, UTF-8은 항상 빅엔디안을 사용하기 때문에(높은 자리의 비트가 앞쪽 바이트에 온다) 바이트순서마크(U+FEFF)는 필요가 없다. 하지만 윈도 메모장(…) 같은 것들이 굳이 필요하지도 않은데 꾸준히 U+FEFF에 대응되는 UTF-8 문자열 EF BB BF
를 맨 앞에 붙이는 경우가 많은데, 이게 보통 성가신 것이 아니다.
별로 장점은 없는 것 같지만 굳이 장점을 따지자면:
사실 이것도 그다지 장점같아 보이지는 않는데, 단점은 좀 많이 심각하다. 간단히 말하면 UTF-8 바이트 순서 마크는 UTF-8을 딱히 고려 안 하는 프로그램을 무참히 깨뜨린다. 예를 들어:
#!
여야 하는데, 바이트 순서 마크가 있으면 그 조건이 깨져서 동작하지 않게 된다.
그러하니 제발 좀 쓰지 말자. 참고로 vim과 파이썬에서는 UTF-8 바이트 순서 마크를 필요로 하는 거지같은 상황을 위하여 각각 'bomb'
옵션(…)과 utf-8-bom
인코딩을 별도로 제공하고 있긴 하다.
Modified UTF-8. 자바에서 특히 많이 볼 수 있는데, 기본적인 목적은 UTF-8의 목적을 최대한 살리면서 U+0000도 널 바이트로 인코딩되지 않게 하자에 있다. 따라서 수정 UTF-8에서는 U+0000을 00
이 아닌 C0 80
으로 인코딩한다. (다른 문자는 전혀 영향을 받지 않는다. 따라서 C0 81
은 U+0001로 디코딩되는 게 아니고 여전히 오류이다.)
수정 UTF-8의 큰 장점은 내부적으로는 U+0000가 들어 가도 널로 끝나는 문자열에서 널 문자 때문에 문자열을 잘라 먹는 일이 없도록 하기 위해서일텐데, 정작 수정 UTF-8이 흔히 쓰이는 자바의 경우 어느 케이스에서나4) 문자열 길이를 앞에 붙이고 있다. -_-; 아마 내부적으로만 쓰고 있는 듯.
Compatibility Encoding Scheme for UTF-16: 8-Bit (UTR #26). 내부적으로 UTF-16을 쓰고 외부 표현으로 UTF-8을 쓰는 구현체 중 안타깝게도 UTF-8 구현을 좀 밥솥같이 또는 대강 대충 한 것들을 위한 호환용 문자 인코딩. CESU-8에서 U+10000 이상의 문자는 4바이트로 인코딩되는 게 아니라, 서로게이트 문자 두 개에 대응되는 3바이트 표현 두 개로 인코딩된다.
CESU-8은 기본적으로는 밥솥같은 구현체를 위한 안타까운 인코딩이지만, 기술적으로 아주 장점이 없는 것은 아니다. 내부적으로 UTF-16을 사용할 경우 CESU-8의 바이트 기반 정렬 순서는 UTF-16 정렬 순서와 동일하고, 변환 또한 "진짜" UTF-8을 쓸 경우보다 눈꼽만큼 더 쉬워진다(적어도 덧셈 한 번은 줄어 든다).