본문으로 건너뛰기 문자 인코딩 완벽 가이드 | ASCII·UTF-8·UTF-16·EUC-KR 총정리

문자 인코딩 완벽 가이드 | ASCII·UTF-8·UTF-16·EUC-KR 총정리

문자 인코딩 완벽 가이드 | ASCII·UTF-8·UTF-16·EUC-KR 총정리

이 글의 핵심

ASCII, ANSI, Unicode, UTF-8, UTF-16, UTF-32, EUC-KR, CP949 등 모든 문자 인코딩 방식의 원리와 차이점. 한글 깨짐 문제 해결부터 BOM, Endian까지 실전 예제로 완벽 이해.

들어가며: 왜 문자 인코딩을 알아야 하나?

개발하다 보면 한글이 깨지거나, 파일을 읽을 수 없거나, API 응답이 이상하게 나오는 경험을 합니다. 이 모든 문제의 근본 원인은 문자 인코딩입니다. 이 글에서 다룰 내용

  • ASCII, ANSI, Unicode의 역사
  • UTF-8, UTF-16, UTF-32 인코딩 방식
  • 한글 인코딩 (EUC-KR, CP949)
  • BOM, Endian, 인코딩 감지
  • 실전 문제 해결

1. 문자 인코딩의 역사

타임라인

timeline
    title 문자 인코딩 발전사
    1963 : ASCII 제정\n7비트, 128자
    1987 : ISO-8859-1 (Latin-1)\n8비트, 256자
    1991 : Unicode 1.0\n16비트 통합 문자셋
    1992 : UTF-8 발명\n가변 길이 인코딩
    1996 : UTF-16\n서로게이트 페어
    2003 : UTF-8 웹 표준화
    2008 : UTF-8이 웹에서\n가장 많이 사용
    2026 : UTF-8 점유율 98%

왜 여러 인코딩이 존재하나?

flowchart TB
    Problem["문제: 컴퓨터는\n숫자만 이해"]
    
    ASCII["ASCII\n영문 128자"]
    Extended["확장 ASCII\n각국 언어 256자"]
    Unicode["Unicode\n전 세계 문자 통합"]
    
    Problem --> ASCII
    ASCII --> Extended
    Extended --> Unicode
    
    ASCII --> Issue1["문제: 한글, 한자\n표현 불가"]
    Extended --> Issue2["문제: 각국마다\n다른 코드 페이지"]
    Unicode --> Solution["해결: 모든 문자에\n고유 번호 부여"]

2. ASCII: 7비트 문자 집합

ASCII란?

ASCII(American Standard Code for Information Interchange)는 7비트(0-127)로 영문 알파벳, 숫자, 특수문자를 표현합니다.

ASCII 테이블

Dec  Hex  Char  |  Dec  Hex  Char  |  Dec  Hex  Char
-------------------------------------------------
 32  20   Space |  64  40   @      |  96  60   `
 33  21   !     |  65  41   A      |  97  61   a
 34  22   "     |  66  42   B      |  98  62   b
 35  23   #     |  67  43   C      |  99  63   c
...
 48  30   0     |  80  50   P      | 112  70   p
 49  31   1     |  81  51   Q      | 113  71   q
...
 57  39   9     |  90  5A   Z      | 122  7A   z

ASCII 제어 문자

# 주요 제어 문자
NUL = 0x00  # Null
LF  = 0x0A  # Line Feed (\n)
CR  = 0x0D  # Carriage Return (\r)
ESC = 0x1B  # Escape
DEL = 0x7F  # Delete
# 줄바꿈 방식
# Unix/Linux: LF (\n)
# Windows: CR+LF (\r\n)
# Mac (Classic): CR (\r)

ASCII 예제

# 문자 → 코드
ord('A')  # 65
ord('a')  # 97
ord('0')  # 48
# 코드 → 문자
chr(65)   # 'A'
chr(97)   # 'a'
# ASCII 범위 확인
def is_ascii(text):
    return all(ord(c) < 128 for c in text)
is_ascii("Hello")  # True
is_ascii("안녕")    # False

3. ANSI와 코드 페이지

ANSI란?

ANSI는 8비트(0-255)로 확장하여 각국 언어를 지원합니다. 하지만 코드 페이지(Code Page)마다 128-255 범위의 의미가 다릅니다.

주요 코드 페이지

코드 페이지이름지역특징
CP437OEM-US미국DOS 기본
CP850Latin-1서유럽DOS 다국어
CP949확장 완성형한국Windows 한글
CP932Shift-JIS일본Windows 일본어
CP936GBK중국Windows 중국어
ISO-8859-1Latin-1서유럽Unix/Web
ISO-8859-15Latin-9서유럽유로(€) 추가

코드 페이지 문제

# 같은 바이트 값이 다른 의미
byte_value = 0xC7
# CP949 (한글): '한'
text_korean = byte_value.to_bytes(1, 'big').decode('cp949')  # 에러 (2바이트 필요)
# ISO-8859-1 (Latin-1): 'Ç'
text_latin = byte_value.to_bytes(1, 'big').decode('latin-1')  # 'Ç'
# 같은 파일을 다른 인코딩으로 읽으면 깨짐!

4. Unicode: 전 세계 문자 통합

Unicode란?

Unicode는 전 세계 모든 문자에 고유한 코드 포인트(Code Point)를 부여한 문자 집합입니다.

Unicode 구조

U+0000 ~ U+10FFFF (1,114,112개 코드 포인트)
U+0000 ~ U+007F   : ASCII (128자)
U+0080 ~ U+00FF   : Latin-1 Supplement
U+0100 ~ U+017F   : Latin Extended-A
U+0370 ~ U+03FF   : Greek
U+0400 ~ U+04FF   : Cyrillic
U+0600 ~ U+06FF   : Arabic
U+0E00 ~ U+0E7F   : Thai
U+3040 ~ U+309F   : Hiragana (일본어)
U+30A0 ~ U+30FF   : Katakana (일본어)
U+4E00 ~ U+9FFF   : CJK Unified Ideographs (한중일 한자)
U+AC00 ~ U+D7AF   : Hangul Syllables (한글 11,172자)
U+1F600 ~ U+1F64F : Emoticons (이모지)

한글 Unicode 범위

# 한글 음절 (가-힣)
print(f"가: U+{ord('가'):04X}")  # U+AC00
print(f"힣: U+{ord('힣'):04X}")  # U+D7A3
# 한글 자모 (ㄱ-ㅎ, ㅏ-ㅣ)
print(f"ㄱ: U+{ord('ㄱ'):04X}")  # U+3131
print(f"ㅎ: U+{ord('ㅎ'):04X}")  # U+314E
print(f"ㅏ: U+{ord('ㅏ'):04X}")  # U+314F
print(f"ㅣ: U+{ord('ㅣ'):04X}")  # U+3163
# 이모지
print(f"😀: U+{ord('😀'):04X}")  # U+1F600

Unicode vs 인코딩

Unicode: 문자 집합 (Character Set)
         각 문자에 번호(코드 포인트) 부여
         예: '한' = U+D55C
UTF-8/UTF-16/UTF-32: 인코딩 방식 (Encoding)
                      코드 포인트를 바이트로 변환하는 방법
                      예: U+D55C → UTF-8: ED 95 9C (3바이트)
                                → UTF-16: D5 5C (2바이트)

5. UTF-8: 가변 길이 인코딩

UTF-8이란?

UTF-8은 1-4바이트 가변 길이로 Unicode를 인코딩합니다. 웹 표준이며, ASCII와 완벽히 호환됩니다.

UTF-8 인코딩 규칙

코드 포인트 범위        | 바이트 수 | 인코딩 패턴
U+0000   ~ U+007F     | 1바이트   | 0xxxxxxx
U+0080   ~ U+07FF     | 2바이트   | 110xxxxx 10xxxxxx
U+0800   ~ U+FFFF     | 3바이트   | 1110xxxx 10xxxxxx 10xxxxxx
U+10000  ~ U+10FFFF   | 4바이트   | 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx

UTF-8 인코딩 예시

영문 ‘A’ (U+0041)

코드 포인트: U+0041 (65)
이진수: 0100 0001
UTF-8 인코딩:
0100 0001 = 0x41 (1바이트)
메모리: 41

한글 ‘한’ (U+D55C)

코드 포인트: U+D55C (54,620)
이진수: 1101 0101 0101 1100
UTF-8 인코딩 (3바이트):
1110xxxx 10xxxxxx 10xxxxxx
1110 1101  10 010101  10 011100
   E   D      9   5      9   C
메모리: ED 95 9C

이모지 ’😀’ (U+1F600)

코드 포인트: U+1F600 (128,512)
이진수: 0001 1111 0110 0000 0000
UTF-8 인코딩 (4바이트):
11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
11110 000  10 011111  10 011000  10 000000
   F   0      9   F      9   8      8   0
메모리: F0 9F 98 80

UTF-8 디코딩 과정과 자기 동기화:

UTF-8 디코딩 알고리즘:

바이트 스트림: ED 95 9C EA B8 80 (한글)

1. 첫 바이트 읽기 (0xED = 11101101):
   
   110xxxxx: 2바이트
   1110xxxx: 3바이트 ← 매칭!
   11110xxx: 4바이트
   
   → 3바이트 문자로 판단

2. 다음 2바이트 읽기 (0x95, 0x9C):
   
   10xxxxxx 패턴 확인:
   0x95 = 10010101 ✓
   0x9C = 10011100 ✓
   
   → 유효한 연속 바이트

3. 비트 추출:
   
   1110 1101  10 010101  10 011100
        ^^^^     ^^^^^^     ^^^^^^
        
   비트 추출:
   1101 (from byte 1)
   010101 (from byte 2)
   011100 (from byte 3)
   
   결합: 1101 010101 011100 = D55C (hex)

4. Unicode 변환:
   
   U+D55C = '한'

자기 동기화 (Self-Synchronization):

손상된 바이트 스트림:
... ?? ED 95 9C EA B8 80 ...
     ↑ 손상

일반 인코딩:
- 손상 지점부터 모든 문자 깨짐
- 파일 끝까지 복구 불가

UTF-8:
- 10xxxxxx: 연속 바이트 (다음 문자 일부)
- 0xxxxxxx, 110xxxxx, 1110xxxx, 11110xxx: 시작 바이트
  
복구 과정:
1. ?? (손상) → 건너뜀
2. ED (1110xxxx) → 새 문자 시작!
3. 95, 9C → 연속 바이트
4. 정상 디코딩 재개

→ 한 문자만 손실, 나머지 복구

예시:
손상: 48 65 6C ?? ED 95 9C
     H  e  l  ?  한
     
손상된 1문자만 깨지으며, '한'은 정상 디코딩!

네트워크 패킷 손실에도 강건함:
패킷 1: [48 65 6C 6C 6F]
패킷 2: [손실]
패킷 3: [ED 95 9C]
→ 패킷 3부터 정상 디코딩 가능

UTF-8 인코딩/디코딩 내부 구현:

UTF-8 인코더 의사 코드:

function utf8_encode(code_point):
    if code_point <= 0x7F:
        # 1바이트 (ASCII)
        return [code_point]
    
    elif code_point <= 0x7FF:
        # 2바이트
        byte1 = 0xC0 | (code_point >> 6)
        byte2 = 0x80 | (code_point & 0x3F)
        return [byte1, byte2]
    
    elif code_point <= 0xFFFF:
        # 3바이트
        byte1 = 0xE0 | (code_point >> 12)
        byte2 = 0x80 | ((code_point >> 6) & 0x3F)
        byte3 = 0x80 | (code_point & 0x3F)
        return [byte1, byte2, byte3]
    
    else:
        # 4바이트
        byte1 = 0xF0 | (code_point >> 18)
        byte2 = 0x80 | ((code_point >> 12) & 0x3F)
        byte3 = 0x80 | ((code_point >> 6) & 0x3F)
        byte4 = 0x80 | (code_point & 0x3F)
        return [byte1, byte2, byte3, byte4]

예시: '한' (U+D55C = 54620)

code_point = 0xD55C (이진: 1101 0101 0101 1100)

3바이트 인코딩:
byte1 = 0xE0 | (0xD55C >> 12)
      = 0xE0 | 0x0D
      = 0xED (1110 1101)

byte2 = 0x80 | ((0xD55C >> 6) & 0x3F)
      = 0x80 | (0x355 & 0x3F)
      = 0x80 | 0x15
      = 0x95 (1001 0101)

byte3 = 0x80 | (0xD55C & 0x3F)
      = 0x80 | 0x1C
      = 0x9C (1001 1100)

결과: ED 95 9C

UTF-8 디코더 의사 코드:

function utf8_decode(bytes):
    byte1 = bytes[0]
    
    if (byte1 & 0x80) == 0:
        # 1바이트 (0xxxxxxx)
        return byte1
    
    elif (byte1 & 0xE0) == 0xC0:
        # 2바이트 (110xxxxx)
        byte2 = bytes[1]
        code_point = ((byte1 & 0x1F) << 6) | (byte2 & 0x3F)
        return code_point
    
    elif (byte1 & 0xF0) == 0xE0:
        # 3바이트 (1110xxxx)
        byte2 = bytes[1]
        byte3 = bytes[2]
        code_point = ((byte1 & 0x0F) << 12) | 
                     ((byte2 & 0x3F) << 6) | 
                     (byte3 & 0x3F)
        return code_point
    
    elif (byte1 & 0xF8) == 0xF0:
        # 4바이트 (11110xxx)
        byte2 = bytes[1]
        byte3 = bytes[2]
        byte4 = bytes[3]
        code_point = ((byte1 & 0x07) << 18) | 
                     ((byte2 & 0x3F) << 12) | 
                     ((byte3 & 0x3F) << 6) | 
                     (byte4 & 0x3F)
        return code_point

에러 처리:
- 연속 바이트가 10xxxxxx 패턴 아니면: 에러
- Overlong 인코딩 (보안 위험): 에러
  예: U+0041 ('A')을 2바이트로 인코딩
      → C1 81 (정상: 41)
      → 거부!
- UTF-8 최대값 초과 (U+10FFFF 이상): 에러

UTF-8 장점

flowchart TB
    UTF8[UTF-8]
    
    Adv1["✅ ASCII 호환\n영문은 1바이트"]
    Adv2["✅ 자기 동기화\n중간부터 읽기 가능"]
    Adv3["✅ 바이트 순서 무관\nEndian 문제 없음"]
    Adv4["✅ 웹 표준\n98% 점유율"]
    
    UTF8 --> Adv1
    UTF8 --> Adv2
    UTF8 --> Adv3
    UTF8 --> Adv4

Python으로 UTF-8 인코딩

# 문자열 → 바이트
text = "Hello 한글 😀"
# UTF-8 인코딩
utf8_bytes = text.encode('utf-8')
print(utf8_bytes)
# b'Hello \xed\x95\x9c\xea\xb8\x80 \xf0\x9f\x98\x80'
# 바이트 분석
for i, byte in enumerate(utf8_bytes):
    print(f"{i:2d}: 0x{byte:02X} ({byte:3d}) {chr(byte) if byte < 128 else '?'}")
# 출력:
#  0: 0x48 ( 72) H
#  1: 0x65 (101) e
#  2: 0x6C (108) l
#  3: 0x6C (108) l
#  4: 0x6F (111) o
#  5: 0x20 ( 32)  
#  6: 0xED (237) ?  ← '한' 시작
#  7: 0x95 (149) ?
#  8: 0x9C (156) ?
#  9: 0xEA (234) ?  ← '글' 시작
# 10: 0xB8 (184) ?
# 11: 0x80 (128) ?
# 12: 0x20 ( 32)  
# 13: 0xF0 (240) ?  ← '😀' 시작
# 14: 0x9F (159) ?
# 15: 0x98 (152) ?
# 16: 0x80 (128) ?
# 바이트 → 문자열
decoded = utf8_bytes.decode('utf-8')
print(decoded)  # "Hello 한글 😀"

6. UTF-16과 UTF-32

UTF-16

UTF-16은 2바이트 또는 4바이트로 인코딩합니다. Windows, Java, JavaScript 내부에서 사용됩니다.

UTF-16 인코딩 규칙

코드 포인트 범위        | 바이트 수 | 방식
U+0000   ~ U+FFFF     | 2바이트   | 그대로 인코딩
U+10000  ~ U+10FFFF   | 4바이트   | 서로게이트 페어

서로게이트 페어 (Surrogate Pair)

# 이모지 '😀' (U+1F600)를 UTF-16으로 인코딩
# 1. U+1F600 - 0x10000 = 0xF600
# 2. 상위 10비트: 0x3D (61)
# 3. 하위 10비트: 0x200 (512)
# 4. High Surrogate: 0xD800 + 0x3D = 0xD83D
# 5. Low Surrogate: 0xDC00 + 0x200 = 0xDE00
text = "😀"
utf16_bytes = text.encode('utf-16-le')
print(utf16_bytes.hex())  # '3dd8 00de' (Little-Endian)
# UTF-16 BE (Big-Endian)
utf16_be = text.encode('utf-16-be')
print(utf16_be.hex())  # 'd83d de00'

UTF-16 예시

text = "Hello 한글"
# UTF-16 LE (Little-Endian)
utf16_le = text.encode('utf-16-le')
print(utf16_le.hex())
# 48 00 65 00 6c 00 6c 00 6f 00 20 00 5c d5 00 ae 00 b8
# UTF-16 BE (Big-Endian)
utf16_be = text.encode('utf-16-be')
print(utf16_be.hex())
# 00 48 00 65 00 6c 00 6c 00 6f 00 20 d5 5c ae 00 b8 00

UTF-32

UTF-32는 모든 문자를 4바이트 고정 길이로 인코딩합니다.

text = "A한😀"
# UTF-32 LE
utf32 = text.encode('utf-32-le')
print(utf32.hex())
# 41 00 00 00  5c d5 00 00  00 f6 01 00
# 각 문자가 정확히 4바이트
# 'A':  0x00000041
# '한': 0x0000D55C
# '😀': 0x0001F600

인코딩 비교

text = "Hello 한글 😀"
encodings = ['utf-8', 'utf-16-le', 'utf-16-be', 'utf-32-le']
for enc in encodings:
    encoded = text.encode(enc)
    print(f"{enc:12s}: {len(encoded):2d} bytes | {encoded.hex()[:40]}...")
# 출력:
# utf-8       : 17 bytes | 48656c6c6f20ed959ceab880f09f9880
# utf-16-le   : 20 bytes | 480065006c006c006f002000d55c00aeb800...
# utf-16-be   : 20 bytes | 004800650069006c006f0020d55cae00b800...
# utf-32-le   : 36 bytes | 4100000065000000...

7. 한글 인코딩 (EUC-KR, CP949)

한글 인코딩 역사

timeline
    title 한글 인코딩 발전
    1987 : KS X 1001\n완성형 2,350자
    1992 : EUC-KR\n완성형 표준
    1996 : CP949 (MS)\n확장 완성형 11,172자
    2000s : UTF-8\nUnicode 기반

EUC-KR

EUC-KR은 2바이트로 한글 2,350자를 표현합니다.

# EUC-KR 인코딩
text = "한글"
euckr_bytes = text.encode('euc-kr')
print(euckr_bytes.hex())  # c7d1 b1db
# '한': 0xC7D1
# '글': 0xB1DB
# 문제: '똠', '쀍' 같은 글자는 표현 불가
try:
    "똠".encode('euc-kr')
except UnicodeEncodeError as e:
    print(f"❌ EUC-KR로 인코딩 불가: {e}")

CP949 (확장 완성형)

CP949는 EUC-KR을 확장하여 11,172자 모두 지원합니다.

# CP949 인코딩
text = "똠방각하"
cp949_bytes = text.encode('cp949')
print(cp949_bytes.hex())
# EUC-KR에 없는 글자도 표현 가능
text2 = "쀍똠뙠"
print(text2.encode('cp949').hex())

UTF-8 vs EUC-KR 비교

text = "Hello 한글"
# UTF-8: 영문 1바이트, 한글 3바이트
utf8 = text.encode('utf-8')
print(f"UTF-8:   {len(utf8)} bytes | {utf8.hex()}")
# UTF-8:   14 bytes | 48656c6c6f20ed959ceab880
# EUC-KR: 영문 1바이트, 한글 2바이트
euckr = text.encode('euc-kr')
print(f"EUC-KR:  {len(euckr)} bytes | {euckr.hex()}")
# EUC-KR:  10 bytes | 48656c6c6f20c7d1b1db

8. BOM과 Endian

BOM (Byte Order Mark)

BOM은 파일 시작 부분에 붙는 특수 바이트로, 인코딩 방식과 바이트 순서를 표시합니다.

인코딩      | BOM (hex)      | 크기
UTF-8      | EF BB BF       | 3바이트
UTF-16 LE  | FF FE          | 2바이트
UTF-16 BE  | FE FF          | 2바이트
UTF-32 LE  | FF FE 00 00    | 4바이트
UTF-32 BE  | 00 00 FE FF    | 4바이트

BOM 예시

# UTF-8 with BOM
text = "Hello"
with open('file_with_bom.txt', 'wb') as f:
    f.write(b'\xef\xbb\xbf')  # BOM
    f.write(text.encode('utf-8'))
# 파일 내용 (hex):
# EF BB BF 48 65 6C 6C 6F
# ^^^^^^^^ BOM
#          ^^^^^^^^^^^^^^ "Hello"
# UTF-8 without BOM (권장)
with open('file_no_bom.txt', 'wb') as f:
    f.write(text.encode('utf-8'))
# 파일 내용 (hex):
# 48 65 6C 6C 6F

BOM 감지 및 제거

def detect_and_remove_bom(data):
    """BOM 감지 및 제거"""
    bom_signatures = [
        (b'\xef\xbb\xbf', 'utf-8-sig'),
        (b'\xff\xfe\x00\x00', 'utf-32-le'),
        (b'\x00\x00\xfe\xff', 'utf-32-be'),
        (b'\xff\xfe', 'utf-16-le'),
        (b'\xfe\xff', 'utf-16-be'),
    ]
    
    for bom, encoding in bom_signatures:
        if data.startswith(bom):
            return data[len(bom):], encoding
    
    return data, None
# 사용
with open('file.txt', 'rb') as f:
    data = f.read()
data, encoding = detect_and_remove_bom(data)
if encoding:
    print(f"✅ BOM detected: {encoding}")
    text = data.decode(encoding.replace('-sig', '))
else:
    print("ℹ️  No BOM, assuming UTF-8")
    text = data.decode('utf-8')

Endian (바이트 순서)

# Big-Endian: 큰 바이트가 먼저
# Little-Endian: 작은 바이트가 먼저
# 예: 0x1234를 메모리에 저장
# Big-Endian:    12 34
# Little-Endian: 34 12
# UTF-16에서 중요
text = "한"  # U+D55C
# UTF-16 BE (Big-Endian)
be = text.encode('utf-16-be')
print(be.hex())  # d5 5c
# UTF-16 LE (Little-Endian)
le = text.encode('utf-16-le')
print(le.hex())  # 5c d5
# UTF-8은 바이트 단위라 Endian 무관
utf8 = text.encode('utf-8')
print(utf8.hex())  # ed 95 9c (항상 같음)

9. 실전 문제 해결

문제 1: 한글 깨짐 (���)

원인

# ❌ UTF-8로 저장했는데 EUC-KR로 읽음
with open('file.txt', 'w', encoding='utf-8') as f:
    f.write("한글")
# 잘못된 읽기
with open('file.txt', 'r', encoding='euc-kr') as f:
    text = f.read()
    print(text)  # '���' (깨짐)

해결

# ✅ 올바른 인코딩으로 읽기
with open('file.txt', 'r', encoding='utf-8') as f:
    text = f.read()
    print(text)  # '한글' (정상)
# ✅ 인코딩 자동 감지
import chardet
with open('file.txt', 'rb') as f:
    raw_data = f.read()
    result = chardet.detect(raw_data)
    encoding = result['encoding']
    confidence = result['confidence']
    
    print(f"Detected: {encoding} ({confidence*100:.1f}% confidence)")
    
    text = raw_data.decode(encoding)
    print(text)

문제 2: UnicodeDecodeError

# ❌ 잘못된 인코딩으로 디코딩
utf8_bytes = "한글".encode('utf-8')
try:
    text = utf8_bytes.decode('ascii')
except UnicodeDecodeError as e:
    print(f"❌ {e}")
    # 'ascii' codec can't decode byte 0xed in position 0
# ✅ 에러 처리 옵션
# 1. 무시
text = utf8_bytes.decode('ascii', errors='ignore')
print(text)  # "" (한글 제거됨)
# 2. 대체
text = utf8_bytes.decode('ascii', errors='replace')
print(text)  # "������" (? 문자로 대체)
# 3. XML/HTML 엔티티
text = utf8_bytes.decode('ascii', errors='xmlcharrefreplace')
print(text)  # "&#54620;&#44544;" (숫자 참조)

문제 3: 웹에서 한글 깨짐

import requests
# ❌ 잘못된 방법
response = requests.get('https://example.com/korean-page')
print(response.text)  # 깨질 수 있음
# ✅ Content-Type 헤더 확인
response = requests.get('https://example.com/korean-page')
content_type = response.headers.get('Content-Type', ')
print(f"Content-Type: {content_type}")
# Content-Type: text/html; charset=euc-kr
# ✅ 올바른 인코딩으로 디코딩
if 'euc-kr' in content_type.lower():
    text = response.content.decode('euc-kr')
else:
    text = response.text  # requests가 자동 감지
# ✅ 또는 chardet으로 자동 감지
import chardet
detected = chardet.detect(response.content)
text = response.content.decode(detected['encoding'])

문제 4: CSV 파일 인코딩

import csv
# ❌ Windows Excel에서 저장한 CSV (CP949)
with open('data.csv', 'r', encoding='utf-8') as f:
    reader = csv.reader(f)
    for row in reader:
        print(row)  # UnicodeDecodeError!
# ✅ 올바른 인코딩
with open('data.csv', 'r', encoding='cp949') as f:
    reader = csv.reader(f)
    for row in reader:
        print(row)
# ✅ 인코딩 자동 감지
import chardet
with open('data.csv', 'rb') as f:
    raw_data = f.read()
    detected = chardet.detect(raw_data)
    encoding = detected['encoding']
with open('data.csv', 'r', encoding=encoding) as f:
    reader = csv.reader(f)
    for row in reader:
        print(row)

10. 프로그래밍 언어별 처리

Python

# 기본 인코딩: UTF-8
text = "Hello 한글 😀"
# 인코딩
utf8 = text.encode('utf-8')
utf16 = text.encode('utf-16')
euckr = text.encode('euc-kr')  # 이모지는 에러
# 디코딩
text = utf8.decode('utf-8')
# 파일 I/O
with open('file.txt', 'w', encoding='utf-8') as f:
    f.write(text)
with open('file.txt', 'r', encoding='utf-8') as f:
    text = f.read()
# 바이트 문자열 리터럴
utf8_bytes = b'\xed\x95\x9c\xea\xb8\x80'
text = utf8_bytes.decode('utf-8')  # "한글"

JavaScript/Node.js

// JavaScript 내부: UTF-16
const text = "Hello 한글 😀";
// 문자열 길이 (주의: 서로게이트 페어)
console.log(text.length);  // 11 (😀가 2로 계산됨)
// 올바른 길이
console.log([...text].length);  // 10
// UTF-8 인코딩 (Node.js)
const buffer = Buffer.from(text, 'utf-8');
console.log(buffer);  // <Buffer 48 65 6c 6c 6f 20 ...>
// 디코딩
const decoded = buffer.toString('utf-8');
console.log(decoded);  // "Hello 한글 😀"
// 지원 인코딩
// utf-8, utf-16le, latin1, base64, hex, ascii

Java

// Java 내부: UTF-16
String text = "Hello 한글 😀";
// UTF-8 인코딩
byte[] utf8Bytes = text.getBytes(StandardCharsets.UTF_8);
System.out.println(Arrays.toString(utf8Bytes));
// 디코딩
String decoded = new String(utf8Bytes, StandardCharsets.UTF_8);
System.out.println(decoded);
// 파일 I/O
// UTF-8로 쓰기
Files.writeString(
    Path.of("file.txt"), 
    text, 
    StandardCharsets.UTF_8
);
// UTF-8로 읽기
String content = Files.readString(
    Path.of("file.txt"), 
    StandardCharsets.UTF_8
);

C++

#include <iostream>
#include <fstream>
#include <string>
#include <codecvt>
#include <locale>
int main() {
    // UTF-8 문자열 (C++11)
    std::string utf8_str = u8"Hello 한글 😀";
    
    // UTF-16 문자열
    std::u16string utf16_str = u"Hello 한글 😀";
    
    // UTF-32 문자열
    std::u32string utf32_str = U"Hello 한글 😀";
    
    // UTF-8 → UTF-16 변환
    std::wstring_convert<std::codecvt_utf8_utf16<char16_t>, char16_t> converter;
    std::u16string utf16 = converter.from_bytes(utf8_str);
    
    // 파일 쓰기 (UTF-8)
    std::ofstream file("file.txt", std::ios::binary);
    file << utf8_str;
    file.close();
    
    // 파일 읽기
    std::ifstream input("file.txt", std::ios::binary);
    std::string content((std::istreambuf_iterator<char>(input)),
                        std::istreambuf_iterator<char>());
    
    std::cout << content << std::endl;
    
    return 0;
}

Go

package main
import (
    "fmt"
    "unicode/utf8"
    "golang.org/x/text/encoding/korean"
    "golang.org/x/text/transform"
    "io"
    "strings"
)
func main() {
    // Go 내부: UTF-8
    text := "Hello 한글 😀"
    
    // 바이트 길이 vs 문자(rune) 길이
    fmt.Println("Bytes:", len(text))           // 17
    fmt.Println("Runes:", utf8.RuneCountInString(text))  // 10
    
    // UTF-8 → EUC-KR 변환
    encoder := korean.EUCKR.NewEncoder()
    euckrBytes, _, _ := transform.Bytes(encoder, []byte(text))
    fmt.Printf("EUC-KR: %x\n", euckrBytes)
    
    // EUC-KR → UTF-8 변환
    decoder := korean.EUCKR.NewDecoder()
    utf8Text, _, _ := transform.String(decoder, string(euckrBytes))
    fmt.Println(utf8Text)
}

고급 주제

정규화 (Normalization)

import unicodedata
# 한글 '가'를 표현하는 두 가지 방법
# 1. 완성형 (NFC): U+AC00
nfc = "가"
print(f"NFC: {len(nfc)} chars, {nfc.encode('utf-8').hex()}")
# NFC: 1 chars, eab080
# 2. 조합형 (NFD): U+1100 + U+1161 (ㄱ + ㅏ)
nfd = unicodedata.normalize('NFD', nfc)
print(f"NFD: {len(nfd)} chars, {nfd.encode('utf-8').hex()}")
# NFD: 2 chars, e384 80e185a1
# 비교
print(nfc == nfd)  # False (다른 바이트 시퀀스)
# 정규화 후 비교
print(unicodedata.normalize('NFC', nfc) == 
      unicodedata.normalize('NFC', nfd))  # True

인코딩 감지

import chardet
def detect_encoding(file_path):
    """파일 인코딩 자동 감지"""
    with open(file_path, 'rb') as f:
        raw_data = f.read()
    
    result = chardet.detect(raw_data)
    
    return {
        'encoding': result['encoding'],
        'confidence': result['confidence'],
        'language': result.get('language', ')
    }
# 사용
info = detect_encoding('unknown.txt')
print(f"Encoding: {info['encoding']}")
print(f"Confidence: {info['confidence']*100:.1f}%")
# 올바른 인코딩으로 읽기
with open('unknown.txt', 'r', encoding=info['encoding']) as f:
    content = f.read()

인코딩 변환

def convert_file_encoding(input_file, output_file, from_enc, to_enc):
    """파일 인코딩 변환"""
    # 원본 읽기
    with open(input_file, 'r', encoding=from_enc) as f:
        content = f.read()
    
    # 새 인코딩으로 저장
    with open(output_file, 'w', encoding=to_enc) as f:
        f.write(content)
    
    print(f"✅ Converted: {from_enc}{to_enc}")
# EUC-KR → UTF-8 변환
convert_file_encoding('old.txt', 'new.txt', 'euc-kr', 'utf-8')

웹 개발에서의 인코딩

HTML

<!DOCTYPE html>
<html>
<head>
    <!-- ✅ UTF-8 선언 (필수) -->
    <meta charset="UTF-8">
    <title>한글 페이지</title>
</head>
<body>
    <h1>안녕하세요</h1>
</body>
</html>

HTTP 헤더

from flask import Flask, Response
app = Flask(__name__)
@app.route('/korean')
def korean_page():
    content = "<h1>안녕하세요</h1>"
    
    # ✅ Content-Type에 charset 명시
    return Response(
        content,
        mimetype='text/html; charset=utf-8'
    )
# ❌ charset 없으면 브라우저가 추측 (깨질 수 있음)

JSON

import json
data = {"name": "홍길동", "message": "안녕하세요"}
# JSON은 기본적으로 UTF-8
json_str = json.dumps(data, ensure_ascii=False)
print(json_str)
# {"name": "홍길동", "message": "안녕하세요"}
# ensure_ascii=True (기본값)
json_str_ascii = json.dumps(data, ensure_ascii=True)
print(json_str_ascii)
# {"name": "\ud64d\uae38\ub3d9", "message": "\uc548\ub155\ud558\uc138\uc694"}

URL 인코딩

from urllib.parse import quote, unquote
# URL에 한글 포함
text = "한글 검색"
# URL 인코딩 (UTF-8 기반)
encoded = quote(text)
print(encoded)
# %ED%95%9C%EA%B8%80%20%EA%B2%80%EC%83%89
# URL 디코딩
decoded = unquote(encoded)
print(decoded)  # "한글 검색"
# 완전한 URL
url = f"https://example.com/search?q={encoded}"
print(url)
# https://example.com/search?q=%ED%95%9C%EA%B8%80%20%EA%B2%80%EC%83%89

데이터베이스 인코딩

MySQL

-- 데이터베이스 생성 (UTF-8)
CREATE DATABASE mydb
CHARACTER SET utf8mb4
COLLATE utf8mb4_unicode_ci;
-- utf8mb4: 4바이트 UTF-8 (이모지 지원)
-- utf8: 3바이트 UTF-8 (이모지 미지원, deprecated)
-- 테이블 생성
CREATE TABLE users (
    id INT PRIMARY KEY,
    name VARCHAR(100) CHARACTER SET utf8mb4
);
-- 연결 시 인코딩 설정
SET NAMES utf8mb4;

PostgreSQL

-- 데이터베이스 생성
CREATE DATABASE mydb
ENCODING 'UTF8'
LC_COLLATE 'ko_KR.UTF-8'
LC_CTYPE 'ko_KR.UTF-8';
-- 클라이언트 인코딩 확인
SHOW client_encoding;
-- 인코딩 변경
SET client_encoding TO 'UTF8';

Python + DB

import psycopg2
# PostgreSQL 연결
conn = psycopg2.connect(
    host='localhost',
    database='mydb',
    user='user',
    password='pass',
    client_encoding='utf8'
)
cursor = conn.cursor()
# 한글 데이터 삽입
cursor.execute(
    "INSERT INTO users (name) VALUES (%s)",
    ("홍길동",)
)
# 조회
cursor.execute("SELECT name FROM users")
name = cursor.fetchone()[0]
print(name)  # "홍길동"

실전 도구

명령줄 도구

# 1. file 명령어로 인코딩 확인
file -i file.txt
# file.txt: text/plain; charset=utf-8
# 2. iconv로 인코딩 변환
iconv -f EUC-KR -t UTF-8 old.txt > new.txt
# 3. 여러 파일 일괄 변환
find . -name "*.txt" -exec iconv -f EUC-KR -t UTF-8 {} -o {}.utf8 \;
# 4. hexdump로 바이트 확인
echo "한글" | hexdump -C
# 00000000  ed 95 9c ea b8 80 0a
# 5. BOM 제거
tail -c +4 file_with_bom.txt > file_no_bom.txt  # UTF-8 BOM (3바이트)

Python 스크립트

#!/usr/bin/env python3
"""
파일 인코딩 일괄 변환 도구
"""
import os
import sys
import chardet
from pathlib import Path
def convert_directory(directory, from_enc=None, to_enc='utf-8'):
    """디렉토리 내 모든 텍스트 파일 인코딩 변환"""
    for file_path in Path(directory).rglob('*.txt'):
        try:
            # 원본 읽기
            with open(file_path, 'rb') as f:
                raw_data = f.read()
            
            # 인코딩 감지
            if from_enc is None:
                detected = chardet.detect(raw_data)
                source_enc = detected['encoding']
                confidence = detected['confidence']
                
                if confidence < 0.7:
                    print(f"⚠️  {file_path}: Low confidence ({confidence:.2f})")
                    continue
            else:
                source_enc = from_enc
            
            # 이미 UTF-8이면 스킵
            if source_enc.lower().replace('-', ') == 'utf8':
                print(f"✓ {file_path}: Already UTF-8")
                continue
            
            # 변환
            text = raw_data.decode(source_enc)
            
            # 저장
            with open(file_path, 'w', encoding=to_enc) as f:
                f.write(text)
            
            print(f"✅ {file_path}: {source_enc}{to_enc}")
            
        except Exception as e:
            print(f"❌ {file_path}: {e}")
if __name__ == '__main__':
    if len(sys.argv) < 2:
        print("Usage: python convert_encoding.py <directory>")
        sys.exit(1)
    
    convert_directory(sys.argv[1])

인코딩 비교표

저장 공간 비교

text = "Hello 한글 😀"
encodings = {
    'ASCII (영문만)': 'ascii',
    'UTF-8': 'utf-8',
    'UTF-16 LE': 'utf-16-le',
    'UTF-16 BE': 'utf-16-be',
    'UTF-32 LE': 'utf-32-le',
    'EUC-KR': 'euc-kr',
    'CP949': 'cp949',
}
print(f"원본 텍스트: {text}\n")
print(f"{'인코딩':15s} | {'바이트':6s} | Hex")
print("-" * 50)
for name, enc in encodings.items():
    try:
        encoded = text.encode(enc)
        hex_str = encoded.hex()[:30] + ('...' if len(encoded) > 15 else ')
        print(f"{name:15s} | {len(encoded):4d}B | {hex_str}")
    except UnicodeEncodeError:
        print(f"{name:15s} | {'N/A':6s} | (인코딩 불가)")
# 출력:
# 원본 텍스트: Hello 한글 😀
# 
# 인코딩           | 바이트 | Hex
# --------------------------------------------------
# ASCII (영문만)   |    N/A | (인코딩 불가)
# UTF-8           |   17B | 48656c6c6f20ed959ceab880f0...
# UTF-16 LE       |   20B | 480065006c006c006f00200000...
# UTF-16 BE       |   20B | 004800650069006c006f002000...
# UTF-32 LE       |   36B | 410000006500000069000000...
# EUC-KR          |    N/A | (인코딩 불가)
# CP949           |    N/A | (인코딩 불가)

특성 비교

인코딩바이트/문자ASCII 호환한글 효율이모지주요 사용처
ASCII1영문 전용
EUC-KR1-2✅✅한국 레거시
CP9491-2✅✅Windows 한글
UTF-81-4웹, Linux, 현대 표준
UTF-162-4✅✅Windows, Java 내부
UTF-324내부 처리

실전 시나리오

시나리오 1: 레거시 시스템 연동

# 문제: 은행 API가 EUC-KR 응답
import requests
response = requests.get('http://legacy-bank-api.com/account')
# ❌ 자동 디코딩 (UTF-8 가정)
# print(response.text)  # 깨짐
# ✅ 올바른 처리
content = response.content  # 바이트
text = content.decode('euc-kr')
print(text)
# ✅ 또는 requests에 힌트 제공
response.encoding = 'euc-kr'
print(response.text)

시나리오 2: 다국어 지원 애플리케이션

import locale
import sys
def setup_encoding():
    """시스템 인코딩 설정"""
    # 표준 출력 인코딩 확인
    print(f"stdout encoding: {sys.stdout.encoding}")
    
    # 시스템 로케일
    print(f"System locale: {locale.getpreferredencoding()}")
    
    # UTF-8 강제 (Python 3.7+)
    if sys.stdout.encoding != 'utf-8':
        sys.stdout.reconfigure(encoding='utf-8')
# 다국어 텍스트 처리
texts = {
    'en': "Hello",
    'ko': "안녕하세요",
    'ja': "こんにちは",
    'zh': "你好",
    'ar': "مرحبا",
    'ru': "Здравствуйте",
    'emoji': "👋🌍"
}
for lang, text in texts.items():
    utf8 = text.encode('utf-8')
    print(f"{lang:5s}: {text:15s} | {len(utf8):2d} bytes | {utf8.hex()[:30]}")

시나리오 3: 파일 업로드 처리

from flask import Flask, request
import chardet
app = Flask(__name__)
@app.route('/upload', methods=['POST'])
def upload_file():
    file = request.files['file']
    
    # 바이너리로 읽기
    content = file.read()
    
    # 인코딩 감지
    detected = chardet.detect(content)
    encoding = detected['encoding']
    confidence = detected['confidence']
    
    print(f"Detected: {encoding} ({confidence*100:.1f}%)")
    
    # UTF-8로 변환
    if encoding.lower() != 'utf-8':
        try:
            text = content.decode(encoding)
            utf8_content = text.encode('utf-8')
            
            return {
                'status': 'converted',
                'from': encoding,
                'to': 'utf-8',
                'content': text
            }
        except Exception as e:
            return {'status': 'error', 'message': str(e)}, 400
    
    return {
        'status': 'ok',
        'encoding': 'utf-8',
        'content': content.decode('utf-8')
    }

베스트 프랙티스

1. 항상 UTF-8 사용

# ✅ 파일 I/O
with open('file.txt', 'w', encoding='utf-8') as f:
    f.write("한글")
# ✅ 소스 코드 인코딩 선언 (Python 2)
# -*- coding: utf-8 -*-
# ✅ HTML
# <meta charset="UTF-8">
# ✅ HTTP 헤더
# Content-Type: text/html; charset=utf-8
# ✅ 데이터베이스
# CREATE DATABASE mydb CHARACTER SET utf8mb4;

2. 바이너리 모드로 읽고 명시적 디코딩

# ✅ 안전한 방법
with open('file.txt', 'rb') as f:
    raw_data = f.read()
# 인코딩 확인 후 디코딩
text = raw_data.decode('utf-8')
# ❌ 위험한 방법 (시스템 기본 인코딩 사용)
with open('file.txt', 'r') as f:  # encoding 미지정
    text = f.read()

3. 에러 처리

# ✅ 에러 처리 전략
def safe_decode(data, encodings=['utf-8', 'cp949', 'euc-kr', 'latin-1']):
    """여러 인코딩 시도"""
    for enc in encodings:
        try:
            return data.decode(enc), enc
        except UnicodeDecodeError:
            continue
    
    # 모두 실패하면 에러 무시하고 디코딩
    return data.decode('utf-8', errors='replace'), 'utf-8'
# 사용
with open('unknown.txt', 'rb') as f:
    data = f.read()
text, encoding = safe_decode(data)
print(f"Decoded as {encoding}: {text}")

4. BOM 처리

# ✅ UTF-8 BOM 자동 처리
with open('file.txt', 'r', encoding='utf-8-sig') as f:
    text = f.read()  # BOM이 있으면 자동 제거
# ✅ BOM 없이 저장 (권장)
with open('file.txt', 'w', encoding='utf-8') as f:
    f.write(text)
# ❌ BOM 포함 저장 (피하기)
with open('file.txt', 'w', encoding='utf-8-sig') as f:
    f.write(text)

문제 해결 체크리스트

한글이 깨질 때

# 1. 파일 인코딩 확인
import chardet
with open('file.txt', 'rb') as f:
    result = chardet.detect(f.read())
    print(result)
# 2. 올바른 인코딩으로 읽기
with open('file.txt', 'r', encoding='cp949') as f:
    text = f.read()
# 3. UTF-8로 재저장
with open('file.txt', 'w', encoding='utf-8') as f:
    f.write(text)

웹에서 한글이 깨질 때

# 1. HTTP 헤더 확인
import requests
response = requests.get('https://example.com')
print(response.encoding)  # ISO-8859-1 (잘못된 추측)
# 2. 올바른 인코딩 설정
response.encoding = 'utf-8'
print(response.text)
# 3. Content-Type 헤더 확인
print(response.headers.get('Content-Type'))
# text/html; charset=euc-kr
# 4. 명시적 디코딩
text = response.content.decode('euc-kr')

데이터베이스에서 한글이 깨질 때

# 1. 연결 인코딩 확인
import pymysql
conn = pymysql.connect(
    host='localhost',
    user='user',
    password='pass',
    database='mydb',
    charset='utf8mb4'  # ✅ 명시적 지정
)
# 2. 테이블 인코딩 확인
cursor = conn.cursor()
cursor.execute("SHOW CREATE TABLE users")
print(cursor.fetchone())
# 3. 인코딩 변환
# ALTER TABLE users CONVERT TO CHARACTER SET utf8mb4;

정리

인코딩 선택 가이드

flowchart TD
    Start[새 프로젝트 시작] --> Q1{언어는?}
    
    Q1 -->|영문만| ASCII["ASCII\n또는 UTF-8"]
    Q1 -->|다국어| UTF8["✅ UTF-8\n권장"]
    Q1 -->|레거시 연동| Q2{시스템은?}
    
    Q2 -->|Windows 한글| CP949[CP949]
    Q2 -->|Unix 한글| EUCKR[EUC-KR]
    Q2 -->|일본어| SJIS[Shift-JIS]
    
    UTF8 --> Best["✅ 최선의 선택\n- 웹 표준\n- 모든 문자 지원\n- ASCII 호환"]

핵심 원칙

# 1. 항상 UTF-8 사용
encoding = 'utf-8'
# 2. 인코딩 명시
with open('file.txt', 'w', encoding='utf-8') as f:
    f.write(text)
# 3. 바이너리 모드 + 명시적 디코딩
with open('file.txt', 'rb') as f:
    data = f.read()
text = data.decode('utf-8')
# 4. 에러 처리
try:
    text = data.decode('utf-8')
except UnicodeDecodeError:
    text = data.decode('utf-8', errors='replace')
# 5. 테스트
assert "한글 😀".encode('utf-8').decode('utf-8') == "한글 😀"

인코딩별 요약

인코딩바이트장점단점사용 시기
UTF-81-4웹 표준, ASCII 호환한글 3바이트모든 새 프로젝트
UTF-162-4한글 2바이트ASCII 비호환Windows/Java 내부
UTF-324고정 길이공간 낭비내부 처리
EUC-KR1-2한글 2바이트일부 한글 미지원레거시 시스템
CP9491-2모든 한글 지원Windows 전용Windows 한글

디버깅 도구

Python 인코딩 디버거

def analyze_encoding(file_path):
    """파일 인코딩 상세 분석"""
    with open(file_path, 'rb') as f:
        raw_data = f.read()
    
    print(f"📄 파일: {file_path}")
    print(f"📊 크기: {len(raw_data)} bytes\n")
    
    # BOM 확인
    if raw_data.startswith(b'\xef\xbb\xbf'):
        print("🔖 BOM: UTF-8")
    elif raw_data.startswith(b'\xff\xfe'):
        print("🔖 BOM: UTF-16 LE")
    elif raw_data.startswith(b'\xfe\xff'):
        print("🔖 BOM: UTF-16 BE")
    else:
        print("🔖 BOM: None")
    
    # 인코딩 감지
    detected = chardet.detect(raw_data)
    print(f"\n🔍 감지된 인코딩: {detected['encoding']}")
    print(f"📈 신뢰도: {detected['confidence']*100:.1f}%")
    
    # 여러 인코딩으로 시도
    print("\n🧪 디코딩 테스트:")
    encodings = ['utf-8', 'cp949', 'euc-kr', 'utf-16', 'latin-1']
    
    for enc in encodings:
        try:
            text = raw_data.decode(enc)
            preview = text[:50].replace('\n', '\\n')
            print(f"  ✅ {enc:10s}: {preview}")
        except UnicodeDecodeError as e:
            print(f"  ❌ {enc:10s}: {e}")
    
    # Hex dump (처음 100바이트)
    print(f"\n🔢 Hex Dump (first 100 bytes):")
    for i in range(0, min(100, len(raw_data)), 16):
        hex_str = ' '.join(f'{b:02x}' for b in raw_data[i:i+16])
        ascii_str = '.join(chr(b) if 32 <= b < 127 else '.' for b in raw_data[i:i+16])
        print(f"  {i:04x}: {hex_str:48s} | {ascii_str}")
# 사용
analyze_encoding('mystery.txt')


자주 묻는 질문 (FAQ)

Q. 이 내용을 실무에서 언제 쓰나요?

A. ASCII, ANSI, Unicode, UTF-8, UTF-16, UTF-32, EUC-KR, CP949 등 모든 문자 인코딩 방식의 원리와 차이점. 한글 깨짐 문제 해결부터 BOM, Endian까지 실전 예제로 완… 실무에서는 위 본문의 예제와 선택 가이드를 참고해 적용하면 됩니다.

Q. 선행으로 읽으면 좋은 글은?

A. 각 글 하단의 이전 글 또는 관련 글 링크를 따라가면 순서대로 배울 수 있습니다. C++ 시리즈 목차에서 전체 흐름을 확인할 수 있습니다.

Q. 더 깊이 공부하려면?

A. cppreference와 해당 라이브러리 공식 문서를 참고하세요. 글 말미의 참고 자료 링크도 활용하면 좋습니다.

참고 자료


같이 보면 좋은 글 (내부 링크)

이 주제와 연결되는 다른 글입니다.


이 글에서 다루는 키워드 (관련 검색어)

인코딩, UTF-8, Unicode, ASCII, EUC-KR, 한글, 문자셋, charset 등으로 검색하시면 이 글이 도움이 됩니다.