글자 깨짐은 왜 생길까? ASCII·Unicode·UTF-8 문자 인코딩 완벽 가이드

텍스트 파일을 열었더니 「锟斤拷烫烫烫」나 「â¥人」 같은 알 수 없는 문자가 가득했던 적 있나요? 혹은 API에서 받은 한국어가 전부 「??」로 표시됐던 경험은요? 이런 짜증스러운 문제들은 거의 모두 같은 원인에서 비롯됩니다: 문자 인코딩 불일치. 문자 인코딩을 이해하는 것은 기술적 문제를 해결하는 것을 넘어, 컴퓨터가 문자를 처리하는 방법의 기초를 이해하는 일입니다.

1. 문자와 숫자: 컴퓨터는 어떻게 글자를 저장할까?

컴퓨터 내부는 0과 1만 존재합니다. 글자를 저장하려면 「글자 → 숫자」 대응표가 필요합니다. 「65는 대문자 A」, 「44032는 한글 '가'」처럼요. 이 대응 규칙이 바로 문자 인코딩(Character Encoding)입니다.

문자 인코딩에는 두 가지 요소가 있습니다:

  • 문자 집합(Character Set): 「어떤 문자들이 있고」 각 문자의 번호(코드 포인트, Code Point)를 정의
  • 인코딩 방식: 「그 번호를 바이트 시퀀스로 어떻게 표현할지」를 정의

이 차이가 중요합니다: Unicode는 문자 집합(14만 개 이상 문자의 번호 체계), UTF-8은 그 인코딩 방식(번호를 바이트로 변환하는 규칙)입니다.

2. ASCII: 모든 것의 시작

1963년 미국 표준 협회(ANSI)가 ASCII(American Standard Code for Information Interchange)를 제정했습니다. 7비트로 128개 문자(0–127)를 정의하며, 영문자·숫자·기호·제어 문자를 포함합니다.

ASCII는 영어에는 완벽하지만, 128개 문자로는 유럽 언어의 악센트 문자도, 한국어도, 중국어도 담을 수 없습니다.

대문자 A가 왜 65일까요?
설계자는 대소문자 차이를 32(6번째 비트의 차이)로 정했습니다. 비트 하나만 뒤집으면 대소문자를 변환할 수 있는 영리한 설계입니다. Python에서 ord('A') == 65, ord('a') == 97인 것도 이 때문입니다.

3. 군웅할거의 시대: EUC-KR·CP949·Big5·GBK

ASCII로는 부족해서 각 지역과 조직이 독자적으로 확장을 시작했습니다. 이것이 글자 깨짐 문제의 근원입니다.

3.1 한국어: KS X 1001 / EUC-KR / CP949

KS X 1001은 1987년 한국에서 제정된 한국어 문자 집합입니다. EUC-KR은 KS X 1001을 기반으로 한 인코딩으로 Unix/Linux 환경에서 많이 쓰였습니다. CP949(또는 MS949)는 Microsoft가 EUC-KR을 확장한 인코딩으로, Windows 한국어 버전의 기본 인코딩입니다. 이들은 UTF-8이 등장하기 전까지 한국어 처리의 기반이었습니다.

3.2 중국어(번체): Big5

Big5는 1984년 대만에서 제정된 번체 중국어 인코딩으로, 약 13,060자를 수록합니다.

3.3 중국어(간체): GBK

중국 대륙의 GBK는 21,003자를 수록하며 Windows 간체 중국어 버전의 기본 인코딩입니다.

인코딩대상 언어문자 수바이트/문자주요 사용 지역
ASCII영어1281전 세계(영어)
EUC-KR/CP949한국어약 11,1722한국
Big5번체 중국어13,0602대만·홍콩
GBK간체 중국어21,0032중국 대륙
Shift-JIS일본어약 6,8791–2일본
UTF-8모든 언어140,000+1–4전 세계

4. Unicode: 분열을 끝낸 통합 표준

1991년 Unicode 1.0이 발표되어, 세계 모든 문자에 고유한 코드 포인트(Code Point)를 부여했습니다(U+ 16진수로 표기):

  • U+0041 → 대문자 A
  • U+AC00 → 한글 「가」
  • U+4E2D → 한자 「中」
  • U+1F600 → 😀(웃는 이모지)

현재 Unicode 14.0은 159개 문자 체계에 걸쳐 144,697개 이상의 문자를 정의합니다.

Unicode ≠ UTF-8
가장 흔한 혼동입니다. Unicode는 「문자에 번호를 부여하는 표준」이고, UTF-8은 「그 번호를 바이트로 저장하는 방식」입니다. UTF-8은 Unicode 구현 방식 중 하나이며, UTF-16과 UTF-32도 있습니다.

5. UTF-8: 왜 세계 표준이 됐을까?

UTF-8은 1992년 Ken Thompson과 Rob Pike가 설계한 가변 길이 인코딩으로, 코드 포인트 크기에 따라 1~4바이트를 사용합니다:

  • 1바이트: ASCII 범위(U+0000–U+007F)——UTF-8은 ASCII와 완전히 호환
  • 2바이트: 대부분의 유럽 언어 문자
  • 3바이트: 한글·중국어·일본어(U+0800–U+FFFF)
  • 4바이트: 이모지·고대 문자(U+10000 이상)

UTF-8이 전 세계 웹 페이지의 97% 이상에서 사용되는 이유:

  • ASCII 하위 호환: 영문 콘텐츠는 변환 없이 기존 시스템과 공존 가능
  • 자기 동기화: 스트림 어디서부터든 올바르게 디코딩 가능
  • 바이트 순서 문제 없음: UTF-16/32와 달리 엔디언 문제가 없음
  • 균형 잡힌 공간 효율: 영어 1바이트, 한국어 3바이트
인코딩바이트/문자ASCII 호환바이트 순서 문제주요 용도
UTF-81–4(가변)있음없음네트워크 통신·파일 저장
UTF-162 또는 4없음있음(BOM 필요)Windows 내부·Java 문자열
UTF-324(고정)없음있음내부 처리·빠른 인덱싱

6. 글자 깨짐은 왜 생길까?

글자 깨짐이란 바이트 시퀀스를 잘못된 인코딩으로 해독하는 것입니다. 대표적인 시나리오:

6.1 EUC-KR 파일을 UTF-8로 열기

EUC-KR로 인코딩된 한국어 바이트 시퀀스는 UTF-8 규칙을 따르지 않아 「â–»」나 「?」로 표시됩니다.

6.2 데이터베이스의 「??」 문제

MySQL 연결 문자 집합이 latin1인데 UTF-8로 인코딩된 한국어를 저장하려 하면, 해독 불가 바이트가 ?로 변환됩니다. 이 과정은 되돌릴 수 없어 원본 데이터가 영구 손상됩니다.

6.3 HTML charset 선언 누락

HTML에 <meta charset="UTF-8">이 없으면 브라우저가 인코딩을 추측하는데, 잘못 추측하면 한국어가 전부 깨져 보입니다.

MySQL의 utf8 함정
MySQL의 utf8 문자 집합은 최대 3바이트 UTF-8 문자만 지원합니다. 이모지 등 4바이트 문자를 저장하려면 반드시 utf8mb4를 사용해야 합니다. utf8을 쓰면 이모지 저장 시 오류가 발생하거나 데이터가 손상됩니다.

7. URL 인코딩과 문자 인코딩

URL에는 제한된 ASCII 문자만 사용할 수 있어, 한국어 등은 퍼센트 인코딩(Percent-Encoding)이 필요합니다: UTF-8 바이트 시퀀스의 각 바이트를 %XX(16진수)로 표현합니다.

예: 「한국어」 → %ED%95%9C%EA%B5%AD%EC%96%B4

현대 표준(RFC 3986)에서는 URL 인코딩의 기반을 UTF-8로 명확히 규정합니다.

URL 인코딩/디코딩 도구 사용하기

8. 글자 깨짐을 근본적으로 방지하는 방법

근본적인 해결책은 시스템 전체에서 UTF-8을 통일하는 것입니다:

  • 파일: 모든 텍스트 파일을 UTF-8(BOM 없음)로 저장
  • HTML: <head> 최상단에 <meta charset="UTF-8"> 추가
  • 데이터베이스: CHARACTER SET utf8mb4 사용
  • DB 연결: SET NAMES utf8mb4 또는 PDO에서 charset=utf8mb4
  • HTTP 헤더: Content-Type: text/html; charset=utf-8

텍스트 변환 도구 사용하기   Base64 인코딩 도구

9. 정리

문자 인코딩의 역사는 분열에서 통합으로 가는 여정입니다:

  • ASCII(1963): 128자로 영어의 디지털화 기반 마련
  • 군웅할거 시대: EUC-KR, GBK, Big5, Shift-JIS 난립, 글자 깨짐 빈발
  • Unicode(1991+): 모든 문자에 고유 코드 포인트 부여
  • UTF-8(1992+): ASCII 호환·공간 효율·바이트 순서 문제 없음, 전 세계 웹의 97% 이상 채택

기술 스택 전체(파일·코드·DB·HTTP 헤더)에서 UTF-8을 통일하면 글자 깨짐은 거의 발생하지 않습니다. 깨짐이 생겼을 때는 어느 단계에서 인코딩 선언과 실제 저장이 불일치하는지 찾아내는 것이 빠른 해결책입니다.