에듀테크 프로젝트 대시보드
문자 인코딩 (Unicode / UTF-8)
모든 글자에는 유니코드 코드 포인트라는 고유 번호가 붙어 있다 (예: A = U+0041,
가 = U+AC00). 이 번호를 실제 바이트로 바꾸는 규칙이 UTF-8이다.
아래 칸에 아무 글자나 입력하면, 코드 포인트와 UTF-8 바이트가 비트 단위로 어떻게 조립되는지 보여준다.
| 코드 포인트 범위 | 바이트 | 비트 패턴 (접두 / 데이터) |
|---|---|---|
| U+0000 ~ U+007F | 1 | 0xxxxxxx |
| U+0080 ~ U+07FF | 2 | 110xxxxx 10xxxxxx |
| U+0800 ~ U+FFFF | 3 | 1110xxxx 10xxxxxx 10xxxxxx |
| U+10000 ~ U+10FFFF | 4 | 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx |
영문·숫자·기호(0~127)는 UTF-8에서도 딱 1바이트, 그것도 옛 ASCII와 값이 똑같다. 그래서 영어 위주 파일은 UTF-8로 저장해도 크기가 거의 안 늘어난다.
첫 바이트만 보면 110/1110/11110으로 글자가 몇 바이트인지 바로 안다. 이어지는 바이트는 항상 10으로 시작해서 중간부터 읽어도 경계를 다시 찾을 수 있다.
한글(U+AC00~U+D7A3)은 3바이트, 이모지는 대부분 U+1F300 이상이라 4바이트.
그래서 트위터 글자 수 세기나 DB VARCHAR 길이 계산이 헷갈리는 것.
왜 UTF-8이 표준이 됐나?
예전엔 나라마다 EUC-KR·Shift-JIS·Latin-1 등 서로 다른 인코딩을 써서, 한 문서에
한국어와 일본어를 같이 담을 수 없었다 (그 유명한 ��� 깨짐).
유니코드는 세상의 모든 문자에 번호를 하나씩 부여했고, UTF-8은 그 번호를
ASCII 호환 + 가변 길이로 실어 날라서 웹 페이지의 98% 이상이 이걸 쓴다.
파이썬 len("가")는 1(코드 포인트), len("가".encode())는 3(UTF-8 바이트).
자바스크립트 "😀".length는 2인데, 이건 UTF-16 기준으로 이모지가 2개 코드 단위(서로게이트 쌍)이기 때문.
"사람이 세는 글자 수"를 원하면 [...str]이나 Array.from(str)처럼 코드 포인트 단위로 세야 한다.
저장할 때 쓴 인코딩과 열 때 쓴 인코딩이 다르면, 같은 바이트를 다른 규칙으로 해석해서 엉뚱한 글자가 나온다.
UTF-8로 저장한 한글을 EUC-KR로 열면 깨지고, 그 반대도 마찬가지.
해결은 간단하다 — 모두 UTF-8로 통일하고, HTML은 <meta charset="utf-8">,
HTTP 응답은 Content-Type: text/html; charset=utf-8를 명시한다.
파일 맨 앞에 붙는 눈에 안 보이는 3바이트(EF BB BF)로 "이 파일은 UTF-8"이라는 표시.
Windows 메모장이 자주 붙인다. 리눅스 스크립트나 JSON에서는 오히려 오류를 내므로,
보통은 BOM 없는 UTF-8로 저장하는 게 권장된다.