문자 인코딩(Character Encodings)
문자 인코딩은 인간이 읽을 수 있는 텍스트를 컴퓨터가 이해할 수 있는 이진 형식으로 변환하는 방법을 정의한다.
니다. 컴퓨터는 오직 0과 1만 이해하지만, 우리 인간은 다양한 언어와 기호로 의사소통한다. 문자 인코딩은 이 간극을 메우는 중요한 다리 역할을 한다.
문자 인코딩은 현대 소프트웨어 개발의 기초가 되는 개념이다. UTF-8은 현재 가장 보편적인 인코딩 방식이지만, 레거시 시스템과의 호환성, 효율성, 특정 언어 요구사항 등 다양한 고려사항에 따라 다른 인코딩도 여전히 사용된다.
개발자는 다양한 인코딩 방식을 이해하고, 적절히 처리할 수 있어야 하며, 특히 국제화(i18n)와 지역화(l10n)가 중요한 글로벌 애플리케이션 개발에서는 문자 인코딩에 대한 깊은 이해가 필수적이다.
문자 인코딩의 기본 개념
문자 인코딩은 크게 두 가지 과정을 포함한다:
- 매핑(Mapping): 각 문자를 고유한 숫자(코드 포인트)에 대응시킨다.
- 인코딩(Encoding): 이 숫자를 컴퓨터가 저장할 수 있는 비트 패턴으로 변환한다.
예를 들어, ‘A’라는 문자는 유니코드에서 코드 포인트 U+0041을 가지며, 이것은 UTF-8 인코딩에서 01000001이라는 비트 패턴으로 표현된다.
문자 인코딩의 역사적 발전
ASCII (American Standard Code for Information Interchange)
ASCII는 1960년대에 개발된 가장 초기의 문자 인코딩 표준이다.
- 7비트를 사용하여 128개(2^7)의 문자 표현 가능
- 영문 알파벳, 숫자, 기본 특수문자, 제어 문자만 포함
- 코드 포인트 범위: 0-127
ASCII의 한계는 영어 외 언어를 지원하지 않는다는 점.
확장 ASCII와 8비트 코드 페이지
ASCII의 한계를 극복하기 위해 8비트로 확장된 버전이 등장했다.
- 8비트를 사용하여 256개(2^8)의 문자 표현 가능
- 추가적인 128개 문자 공간에 각 언어별 특수문자 배치
- ISO-8859 시리즈, Windows 코드 페이지(CP1252 등) 등장
코드 페이지의 문제점: 같은 인코딩 번호라도 다른 언어에서는 다른 글자로 해석될 수 있다. 예를 들어, 코드 포인트 0xA9는 서유럽 언어에서는 ‘©‘이지만, 키릴 문자에서는 ‘Щ’로 해석된다.
멀티바이트 인코딩
동아시아 언어(한국어, 중국어, 일본어)는 수천 개의 문자를 가지고 있어 1바이트로는 표현이 불가능했다.
- EUC-KR, CP949: 한국어 인코딩
- Shift-JIS, EUC-JP: 일본어 인코딩
- GB2312, Big5: 중국어 인코딩
이러한 인코딩은 언어별로 달라 국제적 텍스트 교환에 어려움이 있었다.
유니코드와 현대 인코딩
유니코드(Unicode)
유니코드는 모든 언어와 기호를 통합한 문자 집합이다.
- 각 문자에 고유한 코드 포인트 부여 (예: ‘A’는 U+0041)
- 현재 14만 개 이상의 문자 포함, 최대 1,114,112개 문자 지원 가능
- 문자와 숫자 사이의 매핑만 정의 (실제 저장 방식은 다양한 인코딩으로 구현)
UTF-8 (Unicode Transformation Format - 8-bit)
현재 웹과 대부분의 시스템에서 가장 널리 사용되는 유니코드 인코딩.
- 가변 길이 인코딩: 1~4바이트 사용
- ASCII와 완전히 호환 (ASCII 문자는 동일한 1바이트로 인코딩)
- 첫 바이트의 상위 비트 패턴으로 바이트 수 결정
UTF-8 인코딩 규칙:
예시:
- ‘A’ (U+0041): 01000001 (1바이트)
- ‘€’ (U+20AC): 11100010 10000010 10101100 (3바이트)
- ‘한’ (U+D55C): 11101101 10010101 10111100 (3바이트)
UTF-8의 장점:
- 효율적인 저장 (영문은 1바이트만 사용)
- 자기 동기화 능력 (오류 발생 시 빠른 복구 가능)
- ASCII와의 호환성
- 바이트 순서 표시(BOM)가 필요 없음
UTF-16
UTF-16은 주로 Windows 환경과 Java, JavaScript 내부 표현에 사용된다.
- 기본 문자(BMP)는 2바이트로 표현
- 확장 문자는 서로게이트 쌍(4바이트)으로 표현
- 바이트 순서 문제 존재 (빅 엔디안, 리틀 엔디안)
예시:
- ‘A’ (U+0041): 00000000 01000001 또는 01000001 00000000 (엔디안에 따라 다름)
- ‘😊’ (U+1F60A): 서로게이트 쌍으로 표현 (4바이트)
UTF-32
모든 문자를 고정 4바이트로.표현하는 간단한 방식.
- 모든 유니코드 문자를 고정 4바이트로 표현
- 매우 단순하지만 공간 효율성 떨어짐
- 대부분의 문자가 불필요하게 큰 공간 차지
개발자가 알아야 할 인코딩 관련 개념
BOM (Byte Order Mark)
BOM은 텍스트 파일의 시작 부분에 특별한 마커를 넣어 인코딩 타입과 바이트 순서를 표시한다.
- UTF-8 BOM: EF BB BF
- UTF-16 BE (빅 엔디안) BOM: FE FF
- UTF-16 LE (리틀 엔디안) BOM: FF FE
- UTF-32 BE BOM: 00 00 FE FF
- UTF-32 LE BOM: FF FE 00 00
BOM은 종종 문제를 일으키기도 한다.
특히 UTF-8 BOM은 불필요하지만 Windows 텍스트 에디터에서 자주 추가된다.
정규화 (Normalization)
유니코드에서는 같은 문자를 여러 방식으로 표현할 수 있다.
예를 들어, ‘é’는 단일 코드 포인트(U+00E9)로 표현하거나, ’e’(U+0065)와 악센트 결합(U+0301)으로 표현할 수 있다.
유니코드 정규화는 이러한 표현을 일관된 형태로 변환한다:
- NFC: 가능한 한 결합 문자 사용
- NFD: 분해 형태로 표현
- NFKC, NFKD: 호환성 문자까지 고려한 정규화
콜레이션 (Collation)
콜레이션은 문자열 정렬과 비교 규칙을 정의한다.
- 단순 이진 비교는 종종 직관적이지 않은 결과 초래
- 언어별 정렬 규칙 존재 (예: 스페인어에서는 ‘ñ’가 ’n’ 뒤에 정렬)
- 대소문자 구분, 악센트 구분 등의 옵션 제공
프로그래밍 언어별 문자 인코딩 처리
- Python 3에서는 문자열이 기본적으로 유니코드.
- JavaScript는 내부적으로 UTF-16을 사용.
- Java는 내부적으로 UTF-16을 사용.
데이터베이스와 문자 인코딩
데이터베이스에서 인코딩 설정은 매우 중요하다.
- 대부분의 현대 DB는 UTF-8 지원
- 데이터베이스, 테이블, 컬럼 수준에서 문자셋과 콜레이션 설정 가능
MySQL 예시:
주의점:
- MySQL의
utf8
은 최대 3바이트만 지원 (이모지 등 표현 불가) utf8mb4
를 사용해야 4바이트 문자까지 지원
웹 개발에서의 문자 인코딩
HTML과 문자 인코딩
|
|
HTTP와 문자 인코딩
서버 응답 헤더에 인코딩 정보 포함:
|
|
URL 인코딩
URL에는 ASCII 문자만 사용할 수 있으므로, 다른 문자는 퍼센트 인코딩(%xx)으로 변환해야 한다.
일반적인 인코딩 문제와 해결책
인코딩 오류 증상
- 깨진 문자 (mojibake): 다른 인코딩으로 해석된 경우
- 예: “안녕하세요” → “������” (UTF-8을 ASCII로 잘못 해석)
- 예: “안녕하세요” → “¾È³çÇϼ¼¿ä” (UTF-8을 EUC-KR로 잘못 해석)
- 문자 대체: 지원하지 않는 문자가 ‘?‘나 ‘�‘로 대체됨
- 예: “café” → “caf?”
인코딩 문제 해결 방법
- 항상 명시적으로 인코딩 지정하기
- 파일 저장, 데이터베이스 설정, HTTP 헤더 등에서 명시적 선언
- UTF-8 표준화하기
- 가능한 모든 곳에서 UTF-8 사용 (파일, DB, API 응답 등)
- BOM 주의하기
- 특히 PHP, CSV 파일 등에서 UTF-8 BOM이 문제 발생 가능
- 텍스트 처리 순서 유지하기
- 인코딩 → 처리 → 디코딩 순서 일관되게 유지
- 인코딩 감지 라이브러리 활용하기
- Python의
chardet
, JavaScript의jschardet
등
- Python의
인코딩 문제 디버깅
|
|
인코딩 관련 보안 이슈
XSS 공격과 인코딩
크로스 사이트 스크립팅(XSS) 공격은 종종 문자 인코딩을 악용한다.
인코딩 정규화 우회
보안 필터를 우회하기 위해 다양한 표현 방식을 사용할 수 있다.
- “script” → “scr\u0069pt”
- “<” → “%3C” (URL 인코딩)
이를 방지하기 위해 입력을 정규화한 후 검증해야 한다.
Null 바이트 공격
C 기반 언어에서는 null 바이트(\0
)가 문자열 종료로 해석된다.
이를 이용한 공격이 가능하다.
|
|
시스템에 따라 “.txt” 확장자가 무시되고 PHP 파일로 처리될 수 있다.
문자 인코딩 모범 사례
- 일반적인 모범 사례
- UTF-8을 기본으로 사용하기
- 특별한 이유가 없는 한 모든 곳에서 UTF-8 사용
- 인코딩 항상 명시적으로 지정하기
- 애매함을 피하기 위해 명시적 선언
- BOM 사용 피하기 (UTF-8의 경우)
- 특히 웹 개발에서 문제 발생 가능
- 적절한 정규화 적용하기
- 문자열 비교 전 동일한 방식으로 정규화
- 인코딩 일관성 유지하기
- 전체 시스템에서 동일한 인코딩 사용
용어 정리
용어 | 설명 |
---|---|