Skip to Content

8장. CP949 — 통합 완성형의 확장

만화로 보는 요약 — 먼저 읽어보세요

만화로 보는 요약 — 먼저 읽어보세요

CP949는 EUC-KR을 깨지 않고 빈 코드 공간에 나머지 한글을 채워 호환성을 지켰지만, 일부 두 번째 바이트가 ASCII와 겹쳐 바이트 단위 처리의 함정을 남겼다.

면접 실전 질문: ① CP949가 EUC-KR과 호환되는 이유는? ② CP949가 해결한 한글 문제는? ③ 두 번째 바이트의 ASCII 충돌은 왜 위험한가?


배경 — 표준을 갈아엎지 않고 늘리기

7장은 뼈아픈 상태로 끝났어요. 완성형(EUC-KR)이 국가 표준으로 자리 잡았는데, 정작 한글 음절의 79%가 빠져 있었죠. 하나 쓰려면 8바이트 편법을 동원해야 했고, 그마저도 대부분 시스템에서 안 먹혔습니다.

그렇다고 이제 와서 표준을 조합형으로 갈아엎을 순 없었어요. 이미 수많은 문서와 프로그램이 EUC-KR에 맞춰 돌아가고 있었으니까요. 기존 걸 다 깨면서 새 표준으로 이사하는 건 현실적 으로 불가능했습니다. 그럼 방법은 하나예요. 기존 완성형은 손대지 않고, 빈 공간에 나머지 8,822자를 밀어 넣는 것.

이 현실적인 확장을 해낸 주인공이 뜻밖에도 마이크로소프트였습니다.

스토리 — 마이크로소프트가 빈칸을 메우다

마이크로소프트는 Windows에 한글을 넣으면서 통합형 한글 코드(UHC, Unified Hangul Code), 흔히 CP949(코드페이지 949)라 부르는 확장을 만듭니다. 발상은 아주 실용적이었어요.

  • 기존 EUC-KR의 2,350자는 있던 자리에 그대로 둔다. (하위 호환)
  • EUC-KR이 안 쓰고 비워둔 빈 코드 공간에, 나머지 8,822자를 2바이트로 채워 넣는다.

결과적으로 CP949는 한글 음절 11,172자 전부를 2바이트로 표현하게 됩니다. 7장에서 8바이트 편법으로 겨우 표현하던 이, 이제 어떻게 되는지 바이트로 볼까요.

print("가".encode("euc-kr")) # b'\xb0\xa1' ← 완성형 2,350자 print("가".encode("cp949")) # b'\xb0\xa1' ← 똑같다! EUC-KR을 그대로 품음(하위 호환) print("뷁".encode("euc-kr")) # b'\xa4\xd4\xa4\xb2\xa4\xce\xa4\xaa' ← 8바이트 편법 print("뷁".encode("cp949")) # b'\x94\xee' ← 2바이트로 깔끔하게 해결!

는 EUC-KR이든 CP949든 똑같은 0xB0 0xA1이에요. 즉 기존 EUC-KR 파일은 CP949로 그대로 열립니다. 아무것도 안 깨져요. 그러면서 같은 음절까지 2바이트로 담아냈으니, 한국 개발자에겐 사실상 구원이었습니다. CP949는 Windows의 압도적인 점유율을 타고 한국의 사실상 표준이 됐어요. 국가가 정한 순수 표준이 아니라 벤더(MS)의 코드페이지가 표준 자리를 차지한 거죠. (5장에서 Windows가 Latin-1을 확장한 CP1252로 서유럽을 장악한 것과 판박이예요.)

핵심 — 하위 호환의 승리, 그리고 되풀이된 함정

CP949가 잘한 건 분명합니다. 하위 호환을 지키면서 확장했다는 것. 기존 자산을 하나도 안 깨고 빈 곳만 채우는 이 전략은, 오늘날 소프트웨어 버전을 올릴 때도 그대로 통하는 정석이에요. 리누스 토르발스가 리눅스 커널에 못 박은 “유저 공간을 깨지 마라(don’t break userspace)” 와 같은 정신입니다.

하지만 CP949도 6·7장에서 예고된 그 함정을 끝내 피하지 못했습니다.

확장 영역 글자 중 일부는, 2번째 바이트가 ASCII 문자와 겹칩니다.

b = "똠".encode("cp949") # b'\x8cc' print(b[0], b[1]) # 140 99 ← 두 바이트 print(chr(b[1])) # c ← 두 번째 바이트(0x63)가 하필 ASCII 'c'!

은 CP949로 0x8C 0x63 두 바이트인데, 두 번째 바이트 0x63알파벳 c와 똑같은 값이에요. 무슨 일이 벌어질까요? 문자열을 바이트 단위로 훑으면서 특정 ASCII 문자를 찾는 순진한 코드를 생각해 보세요. 예를 들어 경로에서 /를, 또는 어떤 글자 c를 찾는 코드가, 한글 두 번째 바이트를 진짜 c로 착각할 수 있습니다. 한글이 든 문자열을 바이트로 자르거나 검색하다가 엉뚱한 곳이 잘리고 깨지는 거예요.

이게 바로 7장 조합형에서 봤고(의 둘째 바이트가 a), 9장 Shift-JIS에서 더 지독하게 만날 바로 그 병입니다. 선행 바이트는 128 이상이라 구분되지만, 뒤따르는 바이트까지 ASCII를 피하도록 설계하진 못했다. 기존 바이트 배치를 안 건드리려다 보니 어쩔 수 없었어요. 가변 폭 멀티바이트 인코딩이 공통으로 짊어진 숙명입니다.

정리

  • 완성형 표준을 못 갈아엎으니, 기존 EUC-KR은 그대로 두고 빈 공간에 나머지 8,822자를 채운 확장이 CP949(=UHC, 통합형 한글 코드, 마이크로소프트)다. 한글 11,172자 전부를 2바이트로.
  • 하위 호환의 승리: EUC-KR 파일이 CP949로 그대로 열린다(상위 집합). “기존 걸 깨지 마라”는 실무 확장의 정석.
  • 되풀이된 함정: 확장 영역 글자 일부는 2번째 바이트가 ASCII와 겹친다(=0x8C 0x63, 0x63=c). 바이트 단위 문자열 처리에서 한글이 깨지는 고전 버그 — 7장 조합형·9장 Shift-JIS와 같은 병.
  • CP949는 국가 표준이 아니라 벤더(MS)의 코드페이지가 점유율로 표준이 된 사례다.

생각해볼 질문: 하위 호환을 지키려고 2번째 바이트를 ASCII 영역까지 내준 이 맞바꿈 — 만약 당신이 설계자였다면, 호환성을 포기하고 깨끗한 바이트 배치를 택했을까요, 아니면 CP949처럼 기존 자산을 지키는 쪽을 택했을까요? 여기까지가 한국의 사정이었어요. 그런데 이 “2번째 바이트가 ASCII와 겹친다”는 병을, 바다 건너 일본은 훨씬 먼저, 훨씬 지독하게 앓고 있었습니다. 일본의 Shift-JIS에서는 한글도 아닌 역슬래시(\)와 엔화 기호(¥)가 뒤엉키고, 소스 코드가 컴파일되다 깨지는 일까지 벌어져요. 같은 병이 어디까지 갈 수 있는지, 바다 건너로 가봅시다.

9장 · Shift-JIS와 바다 건너의 혼란

Last updated on