가이드

developer

한글 한 글자는 몇 바이트일까? UTF-8, EUC-KR, UTF-16 비교

같은 글이 어떤 곳에서는 200바이트, 다른 곳에서는 300바이트로 나오는 이유. 인코딩별 한글 바이트 계산법과 실무에서 자주 생기는 함정을 정리합니다.

modu.tools Editorial · ·

“한글은 2바이트”라는 말을 한 번쯤 들어 보셨을 거예요. 그런데 요즘 웹 서비스에서 바이트를 세 보면 한글 한 글자가 3바이트로 나오는 경우가 더 많습니다. 둘 다 틀린 말은 아닙니다. 바이트 수는 글자 자체가 아니라 그 글자를 저장하는 방식, 즉 인코딩이 정합니다. 이 글에서는 한국어 글을 다룰 때 마주치는 세 가지 인코딩을 비교하고, 문자 메시지나 데이터베이스 컬럼처럼 바이트 한도가 걸린 곳에서 실수하지 않는 방법을 정리합니다.

글자, 코드 포인트, 바이트는 다르다

먼저 용어를 정리해 두면 뒤가 쉬워집니다.

  • 글자(문자): 사람이 보는 단위입니다. ’한’은 한 글자예요.
  • 코드 포인트: 유니코드가 각 글자에 붙인 번호입니다. ’한’은 U+D55C입니다.
  • 바이트: 그 번호를 컴퓨터에 저장할 때 실제로 차지하는 8비트 묶음의 개수입니다.

유니코드는 현대 한글 완성형 음절 11,172자(가부터 힣까지)를 U+AC00부터 U+D7A3까지 연속된 영역에 배치했습니다. 초성 19개, 중성 21개, 종성 28개(받침 없음 포함)를 곱하면 19 × 21 × 28 = 11,172가 나오는데, 이 영역이 정확히 그 조합 순서대로 채워져 있어요. 이 구조 덕분에 코드 포인트만 보고도 초성, 중성, 종성을 계산으로 분해할 수 있고, 로마자 변환기나 초성 검색 기능이 이 성질을 이용합니다.

UTF-8: 한글은 3바이트

UTF-8은 지금 웹의 사실상 표준 인코딩입니다. RFC 3629에 정의된 방식으로, 코드 포인트의 크기에 따라 1~4바이트를 씁니다.

코드 포인트 범위 바이트 수 예시
U+0000 ~ U+007F 1 영문, 숫자, 기본 기호, 공백
U+0080 ~ U+07FF 2 라틴 확장, 그리스 문자 등
U+0800 ~ U+FFFF 3 한글 음절, 한자 대부분
U+10000 ~ U+10FFFF 4 대부분의 이모지

한글 음절은 U+AC00~U+D7A3이므로 모두 세 번째 줄에 해당합니다. 그래서 UTF-8에서 ’안녕하세요’는 5글자 × 3바이트 = 15바이트입니다. 영어 ’hello’는 5바이트이고요.

이모지는 더 큽니다. 😀(U+1F600) 하나가 4바이트이고, 피부색이나 성별이 붙은 이모지, 가족 이모지처럼 여러 코드 포인트를 이어 붙인 것은 화면에는 한 글자로 보여도 10바이트를 훌쩍 넘기도 합니다.

EUC-KR(및 CP949): 한글은 2바이트

“한글은 2바이트”라는 말의 출처가 바로 EUC-KR입니다. 한국 산업 표준 문자 집합(KS X 1001)을 바탕으로 한 인코딩으로, 영문과 숫자는 1바이트, 한글과 한자는 2바이트로 저장합니다. 오래된 시스템, 일부 공공기관 사이트, 그리고 문자 메시지(SMS)의 바이트 계산 관행이 이 방식을 따르는 경우가 많습니다.

문제는 KS X 1001이 현대 한글 11,172자 중 자주 쓰는 2,350자만 담고 있다는 점입니다. ‘똠’, ‘쀍’, ‘햏’ 같은 글자는 순수 EUC-KR로는 표현할 수 없습니다. 마이크로소프트가 이를 확장한 것이 CP949(통합 완성형)이고, 나머지 글자도 2바이트로 담을 수 있게 했어요. 실무에서 “EUC-KR”이라고 부르는 것이 사실은 CP949인 경우가 많습니다.

UTF-16: 한글은 2바이트, 이모지는 4바이트

UTF-16(RFC 2781)은 U+FFFF 이하의 글자는 2바이트, 그 위는 서로게이트 쌍이라 불리는 4바이트로 저장합니다. JavaScript와 Java의 문자열이 내부적으로 UTF-16을 쓰기 때문에 개발자에게 특히 중요합니다.

예를 들어 JavaScript에서 "한".length는 1이지만 "😀".length는 2입니다. 화면에는 한 글자인 이모지가 길이 2로 세어지는 거예요. 글자 수 제한 입력창이 이모지를 넣었을 때 한도를 이상하게 계산하는 버그의 대부분이 이 차이에서 생깁니다. 사람이 보는 글자 단위로 세려면 Intl.Segmenter 같은 그래핌 단위 분할을 써야 합니다.

같은 문장, 세 가지 결과

“오늘 3시 미팅 OK? 😀” 라는 문장을 예로 들어 보겠습니다. 구성은 한글 4자, 숫자 1자, 영문 2자, 기호 1자(?), 공백 4개, 이모지 1개입니다.

인코딩 계산 합계
UTF-8 한글 4×3 + 숫자·영문·기호·공백 8×1 + 이모지 4 24바이트
UTF-16 일반 글자 12×2 + 이모지 4 28바이트
EUC-KR 한글 4×2 + 나머지 8×1, 이모지는 표현 불가 16바이트 + 이모지 없음

같은 문장이 16바이트도, 24바이트도, 28바이트도 될 수 있는 셈이에요. 어떤 서비스가 “최대 100바이트”라고 할 때 그것이 어떤 인코딩 기준인지 모르면 숫자는 의미가 없습니다.

실무에서 자주 생기는 함정

데이터베이스 컬럼 길이. MySQL의 VARCHAR(100)은 문자 수 기준이지만, 오래된 설정에서 utf8(최대 3바이트) 문자 집합을 쓰면 이모지가 저장되지 않습니다. 이모지까지 저장하려면 utf8mb4가 필요합니다. 반대로 Oracle의 VARCHAR2(100)은 기본 설정에서 바이트 기준이라, UTF-8 데이터베이스라면 한글이 33자 남짓밖에 들어가지 않습니다.

문자 메시지. SMS는 보통 90바이트 기준으로 안내되는데, 이때의 바이트 계산은 한글 2바이트를 전제로 하는 경우가 많습니다. 그래서 한글만으로는 45자 정도가 한계예요. 자세한 내용은 문자 메시지 바이트 한도 가이드에 따로 정리했습니다.

API와 로그. HTTP 헤더나 일부 메시징 API는 바이트 단위로 크기를 제한합니다. 영어로 테스트할 때 문제가 없다가 한국어 데이터에서 잘리는 일이 흔합니다. 바이트 경계에서 자르면 한 글자의 3바이트 중 일부만 남아 깨진 글자(�)가 생기기도 합니다.

파일 저장. 엑셀에서 CSV로 저장한 파일이 다른 프로그램에서 깨진다면 대개 CP949와 UTF-8이 섞인 경우입니다. 반대로 UTF-8 CSV를 엑셀이 제대로 열게 하려면 파일 앞에 BOM(EF BB BF)을 붙이는 방법이 흔히 쓰입니다.

바이트를 정확히 세는 방법

  1. 대상 시스템이 어떤 인코딩으로 세는지 먼저 확인합니다. 안내에 없으면 짧은 한글 문장을 직접 넣어 보고 역산하세요.
  2. 공백과 줄바꿈도 바이트에 포함되는지 확인합니다. 줄바꿈은 시스템에 따라 1바이트(LF) 또는 2바이트(CRLF)입니다.
  3. 이모지와 특수 기호는 별도로 확인합니다. EUC-KR 기반 시스템에서는 아예 전송되지 않거나 물음표로 바뀔 수 있어요.

modu.tools의 글자수·바이트 계산기는 같은 글을 UTF-8 기준과 한글 2바이트 기준으로 동시에 보여 줍니다. 입력한 내용은 브라우저 안에서만 계산되고 서버로 전송되지 않습니다.

바이트 단위로 자를 때 주의할 점

한도를 넘는 글을 자동으로 자르는 기능을 만든다면, 바이트 수만 보고 자르면 안 됩니다. UTF-8에서 한글 한 글자는 3바이트가 한 묶음이라, 100바이트에서 기계적으로 자르면 마지막 글자의 앞 1~2바이트만 남을 수 있습니다. 이렇게 잘린 문자열은 받는 쪽에서 깨진 글자로 보이거나, 엄격한 파서에서는 아예 오류가 납니다.

안전한 방법은 글자 단위로 하나씩 더해 가며 누적 바이트를 계산하고, 다음 글자를 더하면 한도를 넘는 지점에서 멈추는 것입니다. 이모지처럼 여러 코드 포인트가 합쳐진 글자는 그래핌 단위로 묶어서 한꺼번에 넣거나 빼야, 가족 이모지가 사람 한 명만 남는 식의 이상한 결과를 피할 수 있어요.

또 하나는 정규화입니다. 맥에서 만든 파일 이름의 한글이 윈도에서 자모가 분리되어 보이는 현상을 본 적이 있을 거예요. 같은 ’한’이라도 완성형 한 글자(NFC)로 저장할 수도 있고, 초성·중성·종성 세 개의 조합형 자모(NFD)로 저장할 수도 있습니다. NFD로 저장된 ’한’은 UTF-8에서 9바이트가 됩니다. 바이트 수가 예상의 세 배로 나온다면 정규화 형식을 의심해 보세요. JavaScript에서는 text.normalize("NFC")로 맞출 수 있습니다.

한 줄 요약 표(복사해 두기)

문자 UTF-8 EUC-KR/CP949 UTF-16
영문 A 1 1 2
공백 1 1 2
한글 한 3 2 2
한글 한글(2자) 6 4 4
이모지 😀 4 (미지원 흔함) 4

SMS·구형 장비 안내가 ’한글 2바이트’라면 EUC-KR식 열을, DB·HTTP·현대 API가 UTF-8이라면 UTF-8 열을 보세요. 두 숫자를 같은 한도에 넣으면 안 됩니다.

자주 묻는 질문

한글 자모(ㄱ, ㅏ)만 쓰면 몇 바이트인가요? 호환용 자모(U+3131~U+318E)도 UTF-8에서 3바이트, EUC-KR에서 2바이트입니다.

공백은 바이트에 포함되나요? 네. 공백은 모든 인코딩에서 하나의 문자이며 UTF-8과 EUC-KR에서 1바이트입니다.

왜 아직도 EUC-KR을 쓰나요? 오래된 시스템과의 호환성 때문입니다. 새로 만드는 시스템이라면 UTF-8을 쓰는 것이 일반적입니다.