Python 2.x 한글 인코딩 완전 정복 (UnicodeDecodeError 해결)
Python 2.x로 작성된 레거시 코드에서 한글을 다루다 보면 어김없이 마주치는 오류가 있습니다.
plaintext
UnicodeDecodeError: 'ascii' codec can't decode byte 0xed in position 0Python 2의 기본 인코딩이 ASCII이기 때문에 발생하는 문제입니다. 원인과 해결법을 단계별로 정리합니다.
참고: Python 3에서는 기본 인코딩이 UTF-8로 바뀌어 이 문제가 대부분 해소됩니다. 가능하다면 Python 3으로 마이그레이션하는 것이 근본적인 해결책입니다.
문제의 원인: str vs unicode
Python 2에는 두 가지 문자열 타입이 공존합니다.
| 타입 | 설명 | 리터럴 |
|---|---|---|
str | 바이트 시퀀스. 기본 인코딩 ASCII | "hello" |
unicode | 유니코드 코드포인트 | u"안녕" |
python
# Python 2에서의 함정
s = "안녕" # str 타입 → bytes (UTF-8 인코딩된 바이트)
u = u"안녕" # unicode 타입 → 유니코드 코드포인트
# ASCII str과 unicode를 + 연산하면 암묵적 변환 → 오류 발생!
result = s + u # UnicodeDecodeError!해결 방법 1: 파일 인코딩 선언
소스 파일 최상단에 인코딩을 선언합니다. Python 2.x에서 한글이 포함된 파일은 반드시 필요합니다.
python
# -*- coding: utf-8 -*-
name = "홍길동" # 이제 SyntaxError 없이 파싱됨해결 방법 2: 유니코드 리터럴 사용
문자열 앞에 u를 붙여 명시적으로 unicode 타입으로 선언합니다.
python
# -*- coding: utf-8 -*-
# str 타입 (바이트)
name_str = "홍길동"
# unicode 타입 (권장)
name_uni = u"홍길동"
print(type(name_str)) # <type 'str'>
print(type(name_uni)) # <type 'unicode'>해결 방법 3: encode / decode 명시적 변환
외부에서 받아온 바이트 문자열은 decode로 unicode로 변환하고, 출력할 때는 encode로 다시 바이트로 변환합니다.
python
# -*- coding: utf-8 -*-
# 바이트(str)로 받은 한글 → unicode로 디코딩
raw_bytes = "안녕하세요" # UTF-8 바이트
decoded = raw_bytes.decode("utf-8") # unicode 타입으로 변환
# unicode → 바이트(str)로 인코딩
encoded = decoded.encode("utf-8")
print(type(decoded)) # <type 'unicode'>
print(type(encoded)) # <type 'str'>해결 방법 4: 시스템 기본 인코딩 변경
스크립트 시작 시 Python의 기본 인코딩을 UTF-8로 강제 변경합니다. 외부 라이브러리가 내부적으로 str 연산을 하는 경우에도 효과적입니다.
python
# -*- coding: utf-8 -*-
import sys
reload(sys)
sys.setdefaultencoding("utf-8")이 방법은 전역 설정을 변경하므로 부작용이 있을 수 있습니다. 코드의 특정 지점에서만 인코딩 처리를 하는 방식을 우선 고려하세요.
해결 방법 5: codecs 모듈로 파일 입출력
파일을 읽고 쓸 때 인코딩을 명시적으로 지정합니다.
python
# -*- coding: utf-8 -*-
import codecs
# UTF-8 인코딩으로 파일 읽기 → unicode 타입으로 반환
with codecs.open("data.txt", "r", encoding="utf-8") as f:
content = f.read() # unicode 타입
# unicode → UTF-8로 파일 쓰기
with codecs.open("output.txt", "w", encoding="utf-8") as f:
f.write(u"한글 출력")실전 패턴: 한글 처리 함수 래퍼
레거시 코드베이스에서 일관되게 사용할 수 있는 유틸리티 함수입니다.
python
# -*- coding: utf-8 -*-
def to_unicode(s, encoding="utf-8"):
"""str 또는 unicode를 안전하게 unicode로 변환"""
if isinstance(s, unicode):
return s
elif isinstance(s, str):
return s.decode(encoding)
else:
return unicode(s)
def to_str(u, encoding="utf-8"):
"""unicode 또는 str을 안전하게 UTF-8 str로 변환"""
if isinstance(u, unicode):
return u.encode(encoding)
elif isinstance(u, str):
return u
else:
return str(u)
# 사용 예시
name = to_unicode("홍길동") # → unicode
output = to_str(u"안녕") # → UTF-8 바이트Python 3 마이그레이션 시 주의사항
Python 3에서는 str이 곧 unicode이고, 바이트는 bytes 타입으로 명확히 분리되어 이 혼란이 사라집니다.
python
# Python 3 에서는
name = "홍길동" # str = unicode (그냥 씀)
raw = name.encode("utf-8") # bytes 타입
back = raw.decode("utf-8") # str 타입으로 복원2to3 도구를 사용하면 Python 2 코드를 3으로 자동 변환해볼 수 있습니다.
shell
$ 2to3 -w my_script.pyLast updated on