문자 인코딩(Character Encodings)

문자 인코딩은 인간이 읽을 수 있는 텍스트를 컴퓨터가 이해할 수 있는 이진 형식으로 변환하는 방법을 정의한다.
니다. 컴퓨터는 오직 0과 1만 이해하지만, 우리 인간은 다양한 언어와 기호로 의사소통한다. 문자 인코딩은 이 간극을 메우는 중요한 다리 역할을 한다.

문자 인코딩은 현대 소프트웨어 개발의 기초가 되는 개념이다. UTF-8은 현재 가장 보편적인 인코딩 방식이지만, 레거시 시스템과의 호환성, 효율성, 특정 언어 요구사항 등 다양한 고려사항에 따라 다른 인코딩도 여전히 사용된다.

개발자는 다양한 인코딩 방식을 이해하고, 적절히 처리할 수 있어야 하며, 특히 국제화(i18n)와 지역화(l10n)가 중요한 글로벌 애플리케이션 개발에서는 문자 인코딩에 대한 깊은 이해가 필수적이다.

문자 인코딩의 기본 개념

문자 인코딩은 크게 두 가지 과정을 포함한다:

  1. 매핑(Mapping): 각 문자를 고유한 숫자(코드 포인트)에 대응시킨다.
  2. 인코딩(Encoding): 이 숫자를 컴퓨터가 저장할 수 있는 비트 패턴으로 변환한다.
    예를 들어, ‘A’라는 문자는 유니코드에서 코드 포인트 U+0041을 가지며, 이것은 UTF-8 인코딩에서 01000001이라는 비트 패턴으로 표현된다.

문자 인코딩의 역사적 발전

ASCII (American Standard Code for Information Interchange)

ASCII는 1960년대에 개발된 가장 초기의 문자 인코딩 표준이다.

1
2
'A' -> 65 (십진수) -> 1000001 (이진수)
'a' -> 97 (십진수) -> 1100001 (이진수)

ASCII의 한계는 영어 외 언어를 지원하지 않는다는 점.

확장 ASCII와 8비트 코드 페이지

ASCII의 한계를 극복하기 위해 8비트로 확장된 버전이 등장했다.

코드 페이지의 문제점: 같은 인코딩 번호라도 다른 언어에서는 다른 글자로 해석될 수 있다. 예를 들어, 코드 포인트 0xA9는 서유럽 언어에서는 ‘©‘이지만, 키릴 문자에서는 ‘Щ’로 해석된다.

멀티바이트 인코딩

동아시아 언어(한국어, 중국어, 일본어)는 수천 개의 문자를 가지고 있어 1바이트로는 표현이 불가능했다.

유니코드와 현대 인코딩

유니코드(Unicode)

유니코드는 모든 언어와 기호를 통합한 문자 집합이다.

UTF-8 (Unicode Transformation Format - 8-bit)

현재 웹과 대부분의 시스템에서 가장 널리 사용되는 유니코드 인코딩.

UTF-8 인코딩 규칙:

1
2
3
4
1바이트: 0xxxxxxx (ASCII 호환)
2바이트: 110xxxxx 10xxxxxx
3바이트: 1110xxxx 10xxxxxx 10xxxxxx
4바이트: 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx

예시:

UTF-8의 장점:

UTF-16

UTF-16은 주로 Windows 환경과 Java, JavaScript 내부 표현에 사용된다.

예시:

UTF-32

모든 문자를 고정 4바이트로.표현하는 간단한 방식.

개발자가 알아야 할 인코딩 관련 개념

BOM (Byte Order Mark)

BOM은 텍스트 파일의 시작 부분에 특별한 마커를 넣어 인코딩 타입과 바이트 순서를 표시한다.

BOM은 종종 문제를 일으키기도 한다.
특히 UTF-8 BOM은 불필요하지만 Windows 텍스트 에디터에서 자주 추가된다.

정규화 (Normalization)

유니코드에서는 같은 문자를 여러 방식으로 표현할 수 있다.
예를 들어, ‘é’는 단일 코드 포인트(U+00E9)로 표현하거나, ’e’(U+0065)와 악센트 결합(U+0301)으로 표현할 수 있다.

유니코드 정규화는 이러한 표현을 일관된 형태로 변환한다:

콜레이션 (Collation)

콜레이션은 문자열 정렬과 비교 규칙을 정의한다.

프로그래밍 언어별 문자 인코딩 처리

데이터베이스와 문자 인코딩

데이터베이스에서 인코딩 설정은 매우 중요하다.

MySQL 예시:

1
2
3
4
5
6
7
-- 데이터베이스 생성 시 인코딩 지정
CREATE DATABASE mydb CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;

-- 테이블 생성 시 인코딩 지정
CREATE TABLE users (
    name VARCHAR(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci
) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;

주의점:

웹 개발에서의 문자 인코딩

HTML과 문자 인코딩

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
<!DOCTYPE html>
<html>
<head>
    <!-- HTML 문서의 인코딩 선언 -->
    <meta charset="UTF-8">
    <!-- 이전 방식 (권장하지 않음) -->
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
</head>
<body>
    <!-- HTML 엔티티 사용 -->
    &copy; 2023
    <!-- 직접 유니코드 문자 사용 -->
    © 2023
</body>
</html>

HTTP와 문자 인코딩

서버 응답 헤더에 인코딩 정보 포함:

1
Content-Type: text/html; charset=UTF-8

URL 인코딩

URL에는 ASCII 문자만 사용할 수 있으므로, 다른 문자는 퍼센트 인코딩(%xx)으로 변환해야 한다.

1
2
3
// JavaScript에서의 URL 인코딩
const encoded = encodeURIComponent("안녕하세요?");  // "%EC%95%88%EB%85%95%ED%95%98%EC%84%B8%EC%9A%94%3F"
const decoded = decodeURIComponent("%EC%95%88%EB%85%95%ED%95%98%EC%84%B8%EC%9A%94%3F");

일반적인 인코딩 문제와 해결책

인코딩 오류 증상

인코딩 문제 해결 방법

  1. 항상 명시적으로 인코딩 지정하기
    • 파일 저장, 데이터베이스 설정, HTTP 헤더 등에서 명시적 선언
  2. UTF-8 표준화하기
    • 가능한 모든 곳에서 UTF-8 사용 (파일, DB, API 응답 등)
  3. BOM 주의하기
    • 특히 PHP, CSV 파일 등에서 UTF-8 BOM이 문제 발생 가능
  4. 텍스트 처리 순서 유지하기
    • 인코딩 → 처리 → 디코딩 순서 일관되게 유지
  5. 인코딩 감지 라이브러리 활용하기
    • Python의 chardet, JavaScript의 jschardet

인코딩 문제 디버깅

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
# Python에서 인코딩 문제 디버깅
def debug_encoding(text_bytes):
    encodings = ['utf-8', 'euc-kr', 'cp949', 'shift-jis', 'iso-8859-1']
    
    print(f"Original bytes: {text_bytes.hex(' ')}")
    
    for enc in encodings:
        try:
            decoded = text_bytes.decode(enc)
            print(f"{enc}: {decoded}")
        except UnicodeDecodeError:
            print(f"{enc}: Decoding failed")
            
    # 자동 감지 시도
    import chardet
    detected = chardet.detect(text_bytes)
    print(f"Detected: {detected}")

인코딩 관련 보안 이슈

XSS 공격과 인코딩

크로스 사이트 스크립팅(XSS) 공격은 종종 문자 인코딩을 악용한다.

1
2
3
4
5
6
7
8
9
// XSS 방어를 위한 HTML 이스케이핑
function escapeHtml(text) {
    return text
        .replace(/&/g, "&amp;")
        .replace(/</g, "&lt;")
        .replace(/>/g, "&gt;")
        .replace(/"/g, "&quot;")
        .replace(/'/g, "&#039;");
}

인코딩 정규화 우회

보안 필터를 우회하기 위해 다양한 표현 방식을 사용할 수 있다.

이를 방지하기 위해 입력을 정규화한 후 검증해야 한다.

Null 바이트 공격

C 기반 언어에서는 null 바이트(\0)가 문자열 종료로 해석된다.
이를 이용한 공격이 가능하다.

1
filename = "malicious.php\0.txt"

시스템에 따라 “.txt” 확장자가 무시되고 PHP 파일로 처리될 수 있다.

문자 인코딩 모범 사례

  1. UTF-8을 기본으로 사용하기
    • 특별한 이유가 없는 한 모든 곳에서 UTF-8 사용
  2. 인코딩 항상 명시적으로 지정하기
    • 애매함을 피하기 위해 명시적 선언
  3. BOM 사용 피하기 (UTF-8의 경우)
    • 특히 웹 개발에서 문제 발생 가능
  4. 적절한 정규화 적용하기
    • 문자열 비교 전 동일한 방식으로 정규화
  5. 인코딩 일관성 유지하기
    • 전체 시스템에서 동일한 인코딩 사용

용어 정리

용어설명

참고 및 출처