코드포인트, UTF-8, UTF-16은 무엇이 다른가
유니코드는 문자마다 고유한 번호(코드포인트, U+0000~U+10FFFF)를 매기는 표준입니다. 번호 자체는 추상적인 값이고, 파일이나 네트워크에서는 그 번호를 바이트로 바꾸는 인코딩이 따로 필요합니다. UTF-8은 번호 크기에 따라 1~4바이트를, UTF-16은 2바이트 또는 4바이트(서로게이트 쌍)를 씁니다. 같은 문자라도 "코드포인트 수", "UTF-16 길이", "UTF-8 바이트 수"는 서로 다른 숫자이고, 눈에 보이는 글자 수와도 다를 수 있습니다. 자바스크립트의 length는 UTF-16 코드 유닛 개수입니다.
UTF-8 구간표 (빌드 시 계산)
코드포인트가 커질수록 필요한 바이트가 늘어납니다. 첫 바이트의 앞부분이 전체 길이를, 뒤따르는 바이트는 항상 10으로 시작해 이어짐을 표시합니다. 3바이트 구간에는 UTF-16 서로게이트용 U+D800~U+DFFF(2,048개)가 들어 있지만 문자로 쓰이지 않아 개수에서 뺐습니다.
| 코드포인트 범위 | 바이트 수 | 비트 패턴 | 실을 수 있는 비트 | 코드포인트 개수 |
|---|---|---|---|---|
| U+0000 ~ U+007F | 1 | 0xxxxxxx | 7 | 128 |
| U+0080 ~ U+07FF | 2 | 110xxxxx 10xxxxxx | 11 | 1,920 |
| U+0800 ~ U+FFFF | 3 | 1110xxxx 10xxxxxx 10xxxxxx | 16 | 61,440 |
| U+10000 ~ U+10FFFF | 4 | 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx | 21 | 1,048,576 |
대표 문자의 실제 값
| 문자 | 코드포인트 | 10진 | UTF-8(16진) | UTF-16(16진) | UTF-8 바이트 |
|---|---|---|---|---|---|
| A | U+0041 | 65 | 41 | 0041 | 1 |
| é | U+00E9 | 233 | C3 A9 | 00E9 | 2 |
| 가 | U+AC00 | 44032 | EA B0 80 | AC00 | 3 |
| 한 | U+D55C | 54620 | ED 95 9C | D55C | 3 |
| € | U+20AC | 8364 | E2 82 AC | 20AC | 3 |
| ⠓ | U+2813 | 10259 | E2 A0 93 | 2813 | 3 |
| 😀 | U+1F600 | 128512 | F0 9F 98 80 | D83D DE00 | 4 |
이 표의 값은 빌드 때 TextEncoder로 직접 계산한 결과입니다. 검산 예: "가"는 U+AC00=1010 1100 0000 0000(2진)이고, 3바이트 패턴 1110xxxx 10xxxxxx 10xxxxxx에 4비트·6비트·6비트로 나눠 넣으면 1110 1010, 10 110000, 10 000000, 즉 EA B0 80이 됩니다. "😀"는 U+1F600이 BMP를 넘어서므로 UTF-16에서 D83D DE00 두 유닛(서로게이트 쌍)으로 쪼개지고, UTF-8에서는 F0 9F 98 80 네 바이트입니다.
한글 음절은 계산으로 위치가 정해진다
완성형 한글 음절은 U+AC00(가)부터 U+D7A3(힣)까지 연속해서 배치되어 있고, 초성 19개 × 중성 21개 × 종성 28개(받침 없음 포함) =11,172자입니다. 코드는 0xAC00 + (초성번호×21 + 중성번호)×28 + 종성번호로 구합니다. "한"은 ㅎ(18번)·ㅏ(0번)·ㄴ(4번)이므로 (18×21+0)×28+4 = 10,588, 여기에 0xAC00을 더하면 0xD55C, 즉 U+D55C입니다. 자모 단위로 풀어서 보고 싶다면 한글 자모 분리·결합기를 이용하세요.
"한 글자"가 여러 코드포인트인 경우
사람 눈에는 한 글자인데 코드포인트는 여러 개인 경우가 많습니다. 아래 표는 빌드 시 Intl.Segmenter로 센 값입니다.
| 예시 | 눈에 보이는 글자 | 코드포인트 구성 | 코드포인트 수 | UTF-16 길이 | UTF-8 바이트 |
|---|---|---|---|---|---|
| 썸즈업 + 피부톤 | 1 | U+1F44D U+1F3FD | 2 | 4 | 8 |
| 태극기(국기) | 1 | U+1F1F0 U+1F1F7 | 2 | 4 | 8 |
| e + 결합 악센트 | 1 | U+0065 U+0301 | 2 | 2 | 3 |
| "각"의 자모 분해형(NFD) | 1 | U+1100 U+1161 U+11A8 | 3 | 3 | 9 |
| 가족 이모지(ZWJ 결합) | 1 | U+1F468 U+200D U+1F469 U+200D U+1F467 | 5 | 8 | 18 |
그래서 같은 "각"이어도 완성형(NFC)이면 U+AC01 하나, 자모 분해형(NFD)이면 U+1100 U+1161 U+11A8 세 개입니다. 파일명이나 검색에서 겉보기에 같은 글자가 일치하지 않는 문제의 흔한 원인입니다. 정규화가 필요하면 한글 NFC 변환기를 쓰세요.
한계와 주의점
- 블록 이름은 대표 범위만 담았습니다. 이 도구의 블록 목록은 자주 쓰는 수십 개 범위만 포함하며, 목록에 없는 문자는 "기타/미분류"로 나옵니다. 유니코드 공식 블록 전체와 일치한다고 보장하지 않습니다.
- 일반 범주는 브라우저의 유니코드 속성 지원에 의존합니다. 브라우저가 쓰는 유니코드 버전보다 새로 추가된 문자는 "미할당"으로 나올 수 있습니다.
- 상세 분석은 첫 글자에 대해서만 합니다. 첫 번째 "눈에 보이는 글자"가 여러 코드포인트라면 첫 코드포인트가 상세 칸에 나오고, 나머지는 아래 표에서 확인합니다.
- 단독 서로게이트는 UTF-8로 못 씁니다. U+D800~U+DFFF 값은 UTF-8로 인코딩하면 대체 문자(EF BF BD)로 바뀝니다. 이런 값이 입력되면 별도로 표시합니다.
- 글꼴에 없는 문자는 네모로 보입니다. 코드포인트가 유효해도 화면에 그려지는지는 글꼴에 달려 있습니다. 문자 자체를 찾을 때는 특수문자·기호 복사나 이모지 찾기가 편합니다.
보이지 않거나 똑같이 생긴 문자들
글자 수를 세었더니 하나가 남거나, 겉보기에 같은 두 문자열이 비교에서 다르다고 나오는 일의 상당수는 아래 같은 문자 때문입니다. 이 도구에 의심되는 부분만 붙여 넣으면 코드포인트가 바로 드러납니다. 바이트 수와 UTF-16 길이는 빌드 시 계산한 값입니다.
| 이름 | 코드포인트 | UTF-8 바이트 | UTF-8(16진) | UTF-16 길이 | 특징 |
|---|---|---|---|---|---|
| 줄바꿈 없는 공백(NBSP) | U+00A0 | 2 | C2 A0 | 1 | 웹 문서·워드프로세서에서 흔히 섞이며 일반 공백(U+0020)과 다른 문자 |
| 폭 없는 공백(ZWSP) | U+200B | 3 | E2 80 8B | 1 | 폭이 0이라 보이지 않지만 글자 수·비교에 잡힘 |
| 폭 없는 결합자(ZWJ) | U+200D | 3 | E2 80 8D | 1 | 이모지를 하나로 묶는 접착제 역할 |
| 변형 선택자-16(VS16) | U+FE0F | 3 | EF B8 8F | 1 | 앞 문자를 그림(이모지) 모양으로 표시하라는 표시 |
| 바이트 순서 표식(BOM) | U+FEFF | 3 | EF BB BF | 1 | 파일 맨 앞에 붙은 보이지 않는 문자로 알려져 있음 |
| 전각 공백 | U+3000 | 3 | E3 80 80 | 1 | CJK 문서의 넓은 공백, 일반 공백과 다름 |
모양이 비슷한 쌍도 있습니다. 아래 쌍은 화면에서는 거의 구별되지 않을 수 있지만 코드포인트가 다르고, 문자열 비교 결과는 모두 '다름'입니다.
| 쌍 | 첫째 코드포인트 | 둘째 코드포인트 | UTF-8 바이트(첫째/둘째) | 같은 문자열인가 |
|---|---|---|---|---|
| 라틴 대문자 A / 키릴 А | U+0041 | U+0410 | 1 / 2 | 다름 |
| 라틴 소문자 a / 키릴 а | U+0061 | U+0430 | 1 / 2 | 다름 |
| 라틴 A / 전각 A | U+0041 | U+FF21 | 1 / 3 | 다름 |
이런 문자가 비밀번호, 아이디, 도메인 이름에 섞이면 사람 눈으로는 알아채기 어렵습니다. 낯선 링크나 계정 이름이 수상할 때 의심 구간을 복사해 코드포인트를 확인하는 습관은 유용하지만, 이 도구는 보안 판정기가 아니라 확인용 조회기입니다.
같은 문자열을 세는 네 가지 방법
아래 표는 같은 문자열을 눈에 보이는 글자(자소 클러스터), 코드포인트, UTF-16 길이, UTF-8 바이트로 각각 센 결과입니다. 시스템마다 "길이"가 가리키는 단위가 다르다는 점이 한눈에 보입니다.
| 문자열 | 눈에 보이는 글자 | 코드포인트 | UTF-16 길이 | UTF-8 바이트 |
|---|---|---|---|---|
| hello | 5 | 5 | 5 | 5 |
| 홍길동 | 3 | 3 | 3 | 9 |
| ab😀 | 3 | 3 | 4 | 6 |
| hello홍길동😀 | 9 | 9 | 10 | 18 |
| ❤ + VS16 | 1 | 2 | 2 | 6 |
| "한" NFD | 1 | 3 | 3 | 9 |
사용 시나리오 3가지
1. 서버 개발자: 글자 수 제한과 바이트 제한이 어긋날 때
한글 이름을 받는 입력란이 "20자 이내"라고 안내하면서 저장 칸은 20바이트라면, 한글 한 글자가 UTF-8에서 3바이트이므로 7글자를 넘기면 저장에 실패합니다 (7×3=21바이트). 위 표의 "hello홍길동😀"는 글자 9개인데 UTF-8로는 18바이트입니다. 영문 5자(5바이트) + 한글 3자(9바이트) + 이모지 1자(4바이트)로 계산되며, 이 도구에 붙여 넣으면 같은 숫자가 나옵니다. 또 MySQL의 구형 'utf8'(utf8mb3) 문자셋은 최대 3바이트까지만 저장해 4바이트 이모지를 넣지 못하고 utf8mb4가 필요하다고 알려져 있으니, 이모지 입력 오류가 생기면 사용 중인 DB의 문자셋 설정 문서를 확인하세요.
2. 웹 편집자: 복사한 문장에 숨은 공백이 섞였을 때
여러 사이트에서 복사한 문구를 붙였더니 줄바꿈이 이상하거나 검색에서 한 단어가 두 단어로 취급되는 경우, 의심 구간을 붙여 넣어 U+00A0(NBSP)이나 U+200B(ZWSP)가 있는지 봅니다. 보이지 않는 문자는 상세 표의 범주에 '공백류(Z)'나 '서식 문자(Cf, 보이지 않음)'로 표시됩니다. 찾았다면텍스트 공백 정리기로 정리할지 직접 지울지 정하세요. 정리기가 어떤 문자까지 건드리는지는 해당 페이지에서 먼저 확인하는 편이 안전합니다.
3. 문서 작성자: 이모지 하나가 왜 두 글자로 세어지는가
"❤"(U+2764)에 변형 선택자 U+FE0F가 붙으면 화면에서는 하트 하나지만 코드포인트는 2개, UTF-8로는 6바이트입니다. 글자 수 제한이 있는 입력란(소셜 게시물 등)이 이모지를 어떻게 세는지는 서비스마다 규칙이 다릅니다. 제한에 걸리는 이유를 확인할 때 이 도구의 '입력 전체' 요약 네 숫자를 비교해 보면 어느 단위로 세는지 가늠할 수 있습니다.
UTF-16 서로게이트 쌍이 만들어지는 과정
U+10000 이상의 코드포인트(보충 평면, 총 1,048,576개)는 16비트 하나에 들어가지 않으므로 UTF-16은 두 유닛으로 쪼갭니다. 먼저 코드포인트에서 0x10000을 빼고, 위쪽 10비트를 0xD800에, 아래쪽 10비트를 0xDC00에 더합니다. "😀"(U+1F600)는 0x1F600-0x10000=0xF600, 위쪽 10비트가 0x3D, 아래쪽 10비트가 0x200이므로 0xD800+0x3D=0xD83D, 0xDC00+0x200=0xDE00가 되고, 위 대표 문자 표의 값과 일치합니다. 앞 유닛(0xD800~0xDBFF)과 뒤 유닛(0xDC00~0xDFFF)이 항상 짝으로 나와야 하고, 짝이 깨진 단독 서로게이트는 올바른 텍스트가 아닙니다. 그래서 문자열을 slice나 substring으로 UTF-16 길이 기준 자르다 이모지 한가운데를 가르면 깨진 문자가 생길 수 있습니다.
흔한 오해
- "유니코드 = UTF-8"이 아닙니다. 유니코드는 번호 체계이고 UTF-8·UTF-16·UTF-32는 그 번호를 바이트로 옮기는 서로 다른 방식입니다.
- "글자 수 = length"가 아닙니다. 자바스크립트 length는 UTF-16 유닛 수라서 이모지가 2 이상으로 나올 수 있습니다.
- "모양이 같으면 같은 문자"가 아닙니다. 라틴 A와 키릴 А처럼 코드포인트가 다른 문자가 있습니다.
- "코드포인트가 있으면 화면에 보인다"가 아닙니다. 글꼴 지원, 운영체제의 이모지 버전, 앱의 렌더링에 따라 네모(□)나 다른 모양이 될 수 있습니다.
이럴 때 활용하세요
HTML에 직접 넣기 어려운 기호를 € 같은 엔티티로 적을 때, 데이터베이스 컬럼 길이가 바이트인지 글자인지 따질 때, "글자 수 제한 100자인데 이모지를 넣었더니 초과"처럼 길이 계산이 어긋나는 이유를 확인할 때, 점자 같은 기호의 코드값을 확인할 때 씁니다. 문장 전체의 바이트 수는 텍스트 바이트 계산기가, 이진·ASCII 표기는텍스트 이진·ASCII 변환기가 더 적합합니다.
자주 묻는 질문
이모지도 분석할 수 있나요?
네. 코드포인트가 큰 이모지도 분석합니다. 국기나 피부톤처럼 여러 코드포인트가 결합된 이모지는 상세 칸에는 첫 코드포인트만 나오지만, 아래 표에 모든 코드포인트가 나열됩니다.
JS에서 "😀".length가 2인 이유는 무엇인가요?
자바스크립트 문자열은 UTF-16 코드 유닛 단위로 길이를 셉니다. U+1F600은 BMP 밖 문자라 유닛 두 개(D83D, DE00)로 저장되어 length가 2입니다. 코드포인트 단위는 Array.from이나 for...of로 셀 수 있습니다.
같은 글자인데 코드포인트가 다를 수 있나요?
네. 한글의 완성형과 자모 분해형, 전각·반각 문자, 모양이 비슷한 다른 문자(예: 라틴 A와 키릴 А)가 대표적입니다. 이 도구로 코드포인트를 비교하면 구별할 수 있습니다.
UTF-8 바이트 수와 글자 수는 왜 다른가요?
영문은 1바이트, 한글과 대부분의 기호는 3바이트, 이모지 등은 4바이트이기 때문입니다. 바이트 제한이 있는 시스템에서는 한글이 영문보다 3배가량 공간을 차지합니다.
유니코드 블록 정보는 정확한가요?
표시되는 블록명은 대부분 유니코드 표준(Blocks.txt)의 블록 이름을 따르지만, 지역 표시 기호처럼 블록의 일부 범위를 따로 떼어 보여 주는 항목도 있습니다. 또한 전체 블록이 아니라 자주 쓰는 범위만 수록했습니다. 목록에 없으면 "기타/미분류 범위"로 표시됩니다.
복사한 글에서 보이지 않는 문자를 찾으려면 어떻게 하나요?
의심되는 구간만 입력란에 붙여 넣고 아래 표에서 코드포인트와 범주를 확인하세요. 일반 공백은 U+0020이고, U+00A0·U+200B·U+3000 등이 보이면 다른 문자가 섞인 것입니다. 입력 전체 요약에서 눈에 보이는 글자 수보다 코드포인트 수가 많으면 보이지 않는 문자나 결합 문자가 있을 가능성이 큽니다.
'눈에 보이는 글자'는 어떤 기준으로 세나요?
브라우저의 Intl.Segmenter가 제공하는 자소 클러스터(grapheme cluster) 기준입니다. 이모지 결합이나 결합 악센트를 하나로 묶어 세지만, 지원하지 않는 브라우저에서는 코드포인트 수로 대신 표시됩니다. 브라우저·버전에 따라 새 이모지의 묶음 방식이 다를 수 있습니다.
이 도구가 입력한 내용을 서버로 보내나요?
아니요. 분석은 모두 브라우저 안에서 실행되며 입력한 문자를 서버로 전송하거나 저장하지 않습니다. 복사 버튼도 클립보드에 쓰기만 합니다.
HTML 엔티티는 어떤 형태로 써야 하나요?
10진은 €, 16진은 €처럼 쓰며 둘 다 같은 문자(€, U+20AC)를 가리킵니다. 문서 인코딩이 UTF-8이고 편집기가 문자를 그대로 저장할 수 있다면 엔티티 없이 문자를 직접 쓰는 편이 읽기 쉽습니다. 엔티티는 < 같은 예약 문자를 본문에 보이게 할 때나 입력이 어려운 기호에 유용합니다.
이모지의 피부톤이나 가족 이모지는 왜 길이가 긴가요?
피부톤은 별도의 수정자 코드포인트가 기본 이모지 뒤에 붙고, 가족 이모지는 사람 이모지들을 폭 없는 결합자(U+200D)로 이어 붙인 구조이기 때문입니다. 위 표의 가족 이모지는 코드포인트 5개로 이뤄져 있으며, 지원하지 않는 환경에서는 분리되어 보일 수 있습니다.