광고
~ / posts / developer

한글 한 글자 몇 바이트? UTF-8 3바이트·EUC-KR 2바이트 차이

한글 한 글자는 UTF-8에서 3바이트, EUC-KR에서 2바이트예요. 같은 문장이 왜 어디선 100바이트, 어디선 150바이트인지 바이트 구조로 보여주고, 글자가 깨지는 이유와 EUC-KR이 못 담는 한글, 바이트 수 계산 코드까지 정리했습니다.

"같은 문장인데 글자 수 사이트마다 바이트가 다르게 나와요." 자소서나 게시판 글자 수를 재다 보면 이런 경험을 해요. 결론부터 말하면 한글 한 글자는 UTF-8에서 3바이트, EUC-KR(또는 CP949)에서 2바이트예요. 영문·숫자는 두 인코딩 모두 1바이트고요. 그래서 한글이 많은 문장은 어떤 인코딩으로 세느냐에 따라 바이트 수가 1.5배까지 차이 나요.

이게 단순한 숫자 차이로 끝나면 좋은데, 인코딩을 잘못 맞추면 글자가 한글 이나 ??? 로 깨지는 진짜 문제로 이어져요. 왜 한글이 3바이트인지 비트 구조로 보여 주고, EUC-KR이 못 담는 한글이 있다는 사실, 그리고 바이트 수를 정확히 재는 코드까지 Python과 Node.js로 확인하며 정리했어요.

광고

바이트와 인코딩이란

컴퓨터는 글자를 숫자로 바꿔 저장해요. "어떤 글자를 어떤 숫자(바이트)로 바꿀지"의 약속이 문자 인코딩이에요. 영어권에서 출발한 ASCII는 글자 하나를 1바이트로 표현했는데, 1바이트로는 256가지밖에 못 담아서 한글·한자·이모지 같은 많은 문자를 담을 수 없었어요.

그래서 한글을 위한 인코딩이 따로 생겼어요. 대표가 EUC-KR(과 그 확장인 CP949)이고, 지금 전 세계 표준이 된 게 UTF-8이에요. 둘은 한글을 바이트로 바꾸는 방식이 완전히 달라요.

UTF-8에서 한글이 3바이트인 이유

UTF-8은 유니코드 번호(코드 포인트)를 바이트로 바꾸는데, 번호 크기에 따라 1~4바이트를 써요. 규칙은 이래요.

유니코드 범위바이트 수담기는 문자
U+0000 ~ U+007F1ASCII 영문·숫자·기본 기호
U+0080 ~ U+07FF2라틴 확장(é, ü), 그리스·키릴
U+0800 ~ U+FFFF3한글, 한자, 일본어
U+10000 ~ U+10FFFF4이모지, 희귀 한자

한글 완성형은 U+AC00(가)부터 U+D7A3(힣)까지라 3바이트 구간에 들어가요. '한'(U+D55C)이 실제로 어떻게 3바이트가 되는지 비트로 쪼개 봤어요.

한 글자의 유니코드 16비트를 UTF-8의 1110xxxx 10xxxxxx 10xxxxxx 틀에 나눠 담아 0xED 0x95 0x9C 세 바이트가 되는 구조도
유니코드 번호를 고정 비트 틀에 끼워 넣는다
list('한'.encode('utf-8'))   # [237, 149, 156]  = 0xED 0x95 0x9C
'한'.encode('utf-8').hex()   # 'ed959c'

3바이트 UTF-8은 1110xxxx 10xxxxxx 10xxxxxx 라는 틀이에요(RFC 3629 UTF-8 표준 3장). 앞의 1110·10·10 은 "이건 3바이트짜리 글자"임을 표시하는 고정 비트고, 나머지 x 자리 16개에 유니코드 번호를 채워요. '한'의 번호 D55C(16비트)가 그 16자리에 정확히 들어가서 세 바이트가 완성돼요. 이 고정 비트 덕분에 UTF-8은 바이트 하나만 봐도 글자의 시작인지 중간인지 알 수 있어, 깨진 데이터에서도 복구가 쉬워요.

EUC-KR에서 한글이 2바이트인 이유와 함정

EUC-KR은 완성형 한글마다 2바이트 코드를 직접 할당한 방식이에요. 구조가 단순해서 한글이 딱 2바이트예요.

len('한'.encode('euc-kr'))   # 2
len('가'.encode('euc-kr'))   # 2

그런데 여기 함정이 있어요. 순수 EUC-KR은 완성형 한글 11,172자 중 2,350자만 2바이트로 담아요. 자주 쓰는 글자는 대부분 들어가지만, '똠', '펲', '삡' 같은 글자는 빠져 있어요. 직접 세어 봤어요.

완성형 한글 11172자를 2바이트로 담는 범위 비교
02,0004,0006,0008,00010,00012,000EUC-KR 2바이트 · 담기는 한글 수 2,350자2,350EUC-KR 2바이트CP949(확장완성형) 2바이트 · 담기는 한글 수 11,172자11,172CP949(확장완성형) 2바이트

EUC-KR이 못 담는 8822자는 Windows 확장인 CP949에서야 2바이트로 담긴다

단위: 자 · 자료: Python 3.9 로 U+AC00~U+D7A3 각 글자를 인코딩해 2바이트로 담기는 개수를 직접 집계
표로 보기
구분담기는 한글 수
EUC-KR 2바이트2,350
CP949(확장완성형) 2바이트11,172

그래서 윈도우는 EUC-KR을 확장한 CP949(통합 완성형)를 썼고, 여기서 11,172자 전부가 2바이트로 담겨요. 흔히 "EUC-KR"이라고 부르는 한글 파일이 사실은 CP949인 경우가 많아요. 이름이 섞여 쓰이는 이유예요. 어느 쪽이든 이모지나 중국어 간체 같은 다른 문자는 담을 수 없어요. 한국어 외의 글자를 섞어야 한다면 UTF-8 말고는 답이 없어요.

같은 문장, 다른 바이트

안녕하세요 Hello 123! 를 각 인코딩으로 재 봤어요.

인코딩바이트 수한글 1자영문 1자
UTF-82631
EUC-KR / CP9492121
UTF-1634 (+BOM 2)22

한글 5자가 UTF-8에선 15바이트, EUC-KR에선 10바이트라 5바이트 차이가 나요. 한글이 많을수록 격차가 커지죠. UTF-16은 한글이 2바이트지만 영문도 2바이트라, 영문이 많으면 오히려 UTF-8보다 커요. 바이트 수를 직접 확인하려면 글자 수·바이트 세기에 문장을 넣으면 인코딩별로 보여 줘요.

s = '안녕하세요 Hello 123!'
len(s.encode('utf-8'))   # 26
len(s.encode('cp949'))   # 21
Buffer.byteLength('안녕하세요 Hello 123!', 'utf8');  // 26
광고
광고

글자가 깨지는 이유 — 저장과 읽기가 다를 때

인코딩을 잘못 맞추면 글자가 깨져요. 증상으로 원인을 구분할 수 있어요.

증상원인예
한글 (낯선 라틴 글자)UTF-8로 저장한 걸 EUC-KR 등으로 읽음한글 3바이트를 다른 1바이트 글자로 해석
??? 또는 □□대상 인코딩에 없는 글자 변환UTF-8 → EUC-KR로 저장 시 이모지
뷁逑 (깨진 한자·한글)EUC-KR을 UTF-8로 읽음2바이트를 3바이트 틀로 잘못 해석
맨 앞에 BOM을 글자로 해석UTF-8 BOM(EF BB BF)

핵심은 "어떻게 저장했는지"와 "어떻게 읽는지"가 달라서 생긴다는 거예요. CSV를 엑셀에서 열면 한글이 깨지는 고전적 문제도 이거예요. 엑셀이 기본으로 CP949로 읽는데 파일은 UTF-8이라서요. UTF-8로 저장하면서 BOM을 붙이면 엑셀이 알아보지만, BOM은 또 다른 곳에서 문제를 일으켜서(예: JSON 파싱) 양날의 검이에요. 관련 이야기는 JSON 파싱 에러 원인의 보이지 않는 문자 부분에서 다뤘어요.

어떤 인코딩을 써야 하나

지금은 고민할 필요가 거의 없어요. UTF-8로 통일이 정답이에요.

EUC-KR·CP949를 지금도 봐야 하는 경우는 정해져 있어요. 오래된 공공기관 시스템, 레거시 ERP, 과거에 만든 CSV·텍스트 파일, 일부 금융 전문(電文) 규격 등이에요. 이런 데이터를 받으면 받는 즉시 UTF-8로 변환하고, 내부에서는 UTF-8만 쓰는 게 좋아요.

# EUC-KR(CP949) 파일을 UTF-8로 변환
with open('legacy.txt', encoding='cp949') as f:
    text = f.read()
with open('converted.txt', 'w', encoding='utf-8') as f:
    f.write(text)
# 명령 한 줄로 변환 (iconv)
iconv -f CP949 -t UTF-8 legacy.csv > converted.csv
광고
광고

바이트 수가 중요한 실무 상황

글자 수가 아니라 바이트 수가 기준인 곳이 의외로 많아요.

이런 곳에서는 글자 수만 믿지 말고 바이트 수를 함께 확인해야 해요.

자주 묻는 질문

한글 한 글자는 몇 바이트인가요?

UTF-8에서는 3바이트, EUC-KR·CP949에서는 2바이트예요. UTF-16에서는 2바이트고요. 영문과 숫자는 UTF-8과 EUC-KR 모두 1바이트예요. 지금은 UTF-8이 표준이라 한글 3바이트로 계산하는 경우가 가장 많아요.

글자가 한글처럼 깨지는 이유는 무엇인가요?

UTF-8로 저장한 한글을 EUC-KR 같은 다른 인코딩으로 읽었기 때문이에요. 한글 3바이트가 각각 다른 라틴 문자로 해석되면서 생겨요. 파일을 올바른 인코딩(UTF-8)으로 다시 열거나, 읽는 쪽의 인코딩 설정을 맞추면 해결돼요.

엑셀에서 CSV 한글이 깨져요.

엑셀이 기본으로 CP949로 읽는데 파일이 UTF-8이라서 그래요. UTF-8 BOM을 붙여 저장하면 엑셀이 알아보고, 또는 엑셀의 데이터 가져오기에서 인코딩을 UTF-8로 지정하면 돼요. 반대로 BOM은 일부 프로그램에서 문제를 일으키니 상황에 맞게 선택하세요.

EUC-KR과 CP949는 같은 건가요?

CP949는 EUC-KR의 확장이에요. 순수 EUC-KR은 완성형 한글 2,350자만 담지만, CP949는 11,172자 전부를 담아요. '똠' 같은 글자는 EUC-KR엔 없고 CP949에만 있어요. 흔히 EUC-KR이라 부르는 한글 파일이 실제로는 CP949인 경우가 많아요.

지금 새 프로젝트는 어떤 인코딩을 써야 하나요?

UTF-8을 쓰세요. 웹 표준이고 모든 문자를 담을 수 있어요. 데이터베이스, API, 소스 파일까지 UTF-8로 통일하면 인코딩 변환으로 생기는 사고가 거의 없어져요. 레거시 EUC-KR 데이터는 받는 즉시 UTF-8로 변환하는 게 좋아요.

정리

한글은 UTF-8에서 3바이트, EUC-KR에서 2바이트예요. UTF-8은 유니코드 번호를 고정 비트 틀에 나눠 담는 방식이라 한글이 3바이트가 되고, EUC-KR은 완성형마다 2바이트를 직접 할당하지만 담을 수 있는 글자가 제한적이에요. 지금은 UTF-8로 통일하고, 레거시 데이터는 받는 즉시 변환하는 게 정답이에요.

바이트 수를 직접 확인하려면 글자 수·바이트 세기를 쓰고, 이모지처럼 더 복잡하게 세지는 문자 이야기는 이모지 글자 수와 grapheme에서 이어서 볼 수 있어요.

참고한 자료

#한글 바이트#UTF-8 EUC-KR 차이#한글 몇 바이트#글자 깨짐#CP949#인코딩 변환#한글 3바이트
← 이전 글URL-safe Base64(Base64URL) 차이와 패딩 = 복원, Incorrect padding 해결다음 글 →UUID v4 v7 차이, 128비트 구조와 충돌 확률을 계산으로 정리
광고

더 둘러보기

💻 개발자 다른 도구

🔥 많이 찾는 도구

🔮 재미로 한 번 더

📝 읽어 볼 만한 글

글 전체 보기 →