UnicodeDecodeError 'utf-8' codec can't decode byte 완벽 해결 (cp949·euc-kr)
파이썬 개발 가이드 3편
UnicodeDecodeError: 'utf-8' codec can't decode byte 0xec in position 0이 에러 메시지를 그대로 복사해서 검색창에 친 분, 잘 오셨습니다. 결론부터 말하면 encoding을 추측으로 바꿔 끼우지 마세요. utf-8 → cp949 → euc-kr → latin1을 차례로 갈아 끼우다 보면 운 좋게 한 번은 읽히지만, 그건 "왜 됐는지 모르는 상태"라서 다음 파일에서 또 막힙니다. 이 글은 (1) 실제 인코딩을 한 줄로 진단 → (2) byte 값·position으로 원인 분류 → (3) 상황별 복붙 코드로 끝내는 방식입니다.
1단계: 추측 금지, 실제 인코딩부터 확인하세요
일단 이걸 쳐보세요. 터미널에서 파일 경로만 바꾸면 됩니다.
# charset-normalizer (요즘 사실상 표준, chardet보다 빠름)
pip install charset-normalizer
python -m charset_normalizer your_file.csv스크립트 안에서 쓰고 싶다면:
from charset_normalizer import from_path
result = from_path("your_file.csv").best()
print(result.encoding) # 예: 'cp949', 'utf_8', 'euc_kr'
# (구버전 chardet을 쓴다면)
import chardet
with open("your_file.csv", "rb") as f:
print(chardet.detect(f.read()))
# {'encoding': 'EUC-KR', 'confidence': 0.99, ...}진단 결과가 나오면 그 인코딩을 encoding=에 그대로 넣으면 끝입니다. 그래도 감을 잡고 싶다면, 에러 메시지의 byte 값으로도 대략 분류할 수 있습니다.
byte 값·position 원인 분류표
| 에러의 byte 값 | 추정 실제 인코딩 | 상황 |
|---|---|---|
0xec, 0xed, 0xea | UTF-8로 인코딩된 한글 | 파일은 정상 UTF-8인데 읽는 쪽 코드가 엉뚱한 코덱일 때 |
0xb0~0xc8 대 (예 0xb0, 0xc7) | cp949 / euc-kr 한글 | 윈도우 메모장·엑셀·레거시 시스템이 만든 한글 파일 |
0xef 0xbb 0xbf (position 0) | UTF-8 BOM | 엑셀이 "CSV UTF-8"로 저장한 파일 |
0xff 0xfe / 0xfe 0xff | UTF-16 (LE/BOM) | 메모장 "유니코드" 저장 |
0x80~0xff 단독, position이 들쭉날쭉 | 혼합 인코딩 의심 | 여러 소스가 합쳐진 로그·CSV |
position 0에서 터지면 파일 맨 앞 글자/BOM 문제라 진단이 쉽고, position이 큰 숫자면 앞부분은 ASCII(영문)라 멀쩡히 읽히다가 첫 한글에서 깨진 케이스입니다.
2단계: 상황별 복붙 해결 코드 4종
진단으로 알아낸 인코딩을 각 상황에 맞게 명시해줍니다.
① 일반 파일 읽기 — open()
# ❌ Before: encoding 미지정 → OS 기본값(윈도우=cp949)에 의존
with open("data.txt") as f:
text = f.read() # UnicodeDecodeError 발생
# ✅ After: encoding을 명시
with open("data.txt", encoding="cp949") as f: # 또는 "utf-8"
text = f.read()② pandas read_csv
import pandas as pd
# ❌ Before
df = pd.read_csv("sales.csv") # 'utf-8' codec can't decode byte 0xb0 ...
# ✅ After: 엑셀이 만든 한글 CSV는 보통 cp949
df = pd.read_csv("sales.csv", encoding="cp949")
# ✅ "CSV UTF-8(쉼표로 분리)"로 저장한 파일 → BOM 제거
df = pd.read_csv("sales.csv", encoding="utf-8-sig")💡 엑셀 한글 CSV는 십중팔구
cp949, 엑셀에서 "CSV UTF-8" 옵션으로 저장했다면utf-8-sig입니다.
③ subprocess
import subprocess
# ❌ Before: bytes로 받아 디코딩 시 깨짐
out = subprocess.run(["git", "log"], capture_output=True).stdout
# out.decode() # 윈도우 한글 출력에서 에러
# ✅ After: text=True + encoding 명시
out = subprocess.run(
["git", "log"],
capture_output=True,
text=True,
encoding="utf-8", # 윈도우 콘솔 출력이면 "cp949"가 맞을 때도 많음
).stdout④ requests 웹 응답
import requests
r = requests.get("https://example.co.kr/legacy.html")
# 케이스 A: 서버가 euc-kr인데 헤더가 부실해 requests가 잘못 추측
text = r.content.decode("euc-kr") # bytes를 직접 디코딩
# 케이스 B: r.text를 쓰고 싶으면 인코딩을 강제 지정
r.encoding = "utf-8" # 이 줄 이후 r.text가 올바르게 디코딩됨
text = r.textr.text는 r.encoding 값으로 디코딩하므로, 헤더 추측이 틀렸으면 r.encoding을 먼저 고정하거나 r.content를 직접 디코딩하는 게 안전합니다.
3단계: 그래도 안 될 때 — errors 옵션과 BOM
진짜 인코딩을 모르거나, 한 파일에 인코딩이 섞여 있을 때 임시로 쓰는 옵션입니다. 데이터 손실 위험을 반드시 이해하고 쓰세요.
with open("dirty.txt", encoding="utf-8", errors="replace") as f:
text = f.read()errors 옵션 비교표
| 옵션 | 동작 | 깨진 byte 출력 예 | 위험도 |
|---|---|---|---|
strict (기본) | 예외 발생 | UnicodeDecodeError | 안전(읽기 실패) |
ignore | 디코딩 불가 문자를 삭제 | 한글 (글자 사라짐) | ⚠️ 데이터 손실, 복구 불가 |
replace | �(U+FFFD)로 치환 | 한�글 | ⚠️ 원본 추적 불가 |
backslashreplace | 원본 byte를 \x..로 표시 | 한\xeb글 | 손실은 없으나 사람이 읽기 어려움 |
핵심 경고: errors="ignore"는 "에러는 안 나니까 됐다"가 아니라 글자가 조용히 사라지는 가장 위험한 옵션입니다. 금액·수량 같은 데이터를 다룬다면 절대 쓰지 마세요. 디버깅 중 byte 위치를 확인하고 싶을 때만 backslashreplace를 잠깐 쓰는 정도가 적절합니다.
BOM(0xef 0xbb 0xbf) 처리
position 0에서 \ufeff 같은 보이지 않는 문자가 첫 컬럼명에 붙어 KeyError가 난다면 BOM입니다. utf-8 대신 utf-8-sig로 읽으면 자동으로 제거됩니다.
with open("excel_export.csv", encoding="utf-8-sig") as f:
...실무 한마디
저는 신규 파이프라인을 짤 때 무조건 첫 줄에 python -m charset_normalizer를 돌려 인코딩을 로그로 남깁니다. 운영 환경에서 "어제까지 되던 배치가 갑자기 깨진" 사고의 8할은 데이터 공급처가 UTF-8→cp949(혹은 그 반대)로 저장 방식을 바꿨기 때문입니다. 진단을 자동화해두면 원인 파악이 5분 → 5초로 줄어듭니다.
윈도우 환경 팁 (PEP 686, PYTHONUTF8)
Python은 PEP 686에 따라 3.15부터 UTF-8 모드 기본화가 진행 중입니다. 그 전이라도 윈도우에서 cp949 기본값에 시달린다면 환경변수로 강제할 수 있습니다.
# 윈도우(PowerShell)
$env:PYTHONUTF8 = "1"
python app.py
# 또는 실행 시 옵션
python -X utf8 app.py이러면 open()의 기본 encoding이 UTF-8이 됩니다. 단, cp949 파일을 읽는 코드라면 여전히 encoding="cp949"를 명시해야 합니다. 기본값을 바꿔도 파일 자체의 인코딩은 바뀌지 않으니까요.
결론: 진단 → 해결 3단계 체크리스트
- 진단:
python -m charset_normalizer 파일명으로 실제 인코딩 확인 (추측 금지) - 분류: 에러의 byte 값으로 원인 짐작 —
0xec대=UTF-8 한글,0xb0~0xc8대=cp949/euc-kr,0xef bb bf=BOM - 해결:
open(encoding=)/read_csv(encoding="cp949"|"utf-8-sig")/subprocess(text=True, encoding=)/requests는r.content.decode()또는r.encoding지정
errors="ignore"는 마지막 수단도 아닙니다. 데이터가 사라지니 피하세요.
자주 묻는 질문 (FAQ)
Q. 윈도우 메모장으로 저장한 한글 txt가 안 읽혀요.
A. 메모장 기본 저장이 cp949(ANSI)인 경우가 많습니다. open("f.txt", encoding="cp949")로 읽으세요. 메모장에서 "다른 이름으로 저장 → 인코딩: UTF-8"로 다시 저장하면 이후 utf-8로 통일됩니다.
Q. 엑셀에서 만든 CSV의 한글이 pandas에서 깨집니다.
A. 엑셀 한글 CSV는 보통 encoding="cp949"입니다. "CSV UTF-8(쉼표로 분리)" 형식으로 저장했다면 encoding="utf-8-sig"를 쓰면 BOM까지 함께 처리됩니다.
Q. euc-kr 레거시 파일과 utf-8 파일이 섞여 있어요. 한 번에 처리할 방법은?
A. 파일별로 charset_normalizer로 인코딩을 먼저 감지한 뒤 그 값으로 열도록 자동화하세요. 한 파일 안에 인코딩이 섞였다면 errors="backslashreplace"로 깨진 byte 위치를 찾아 원본 공급처에 인코딩 통일을 요청하는 것이 정석입니다.
다음 편 예고 — 4편: Python 가상환경·패키지 설치 후 자주 터지는 SSL/인증서 에러 트러블슈팅
이 글은 AI 에이전트가 자료 조사와 1차 초안 작성을 담당하고, 사람 편집자가 사실관계·출처·톤과 맥락을 검토한 뒤 발행했습니다. 환경(OS·버전)에 따라 결과가 다를 수 있으니 적용 전 공식 문서를 함께 확인하세요. 오류를 발견하시면 이메일로 제보해 주세요 — 확인 후 신속히 정정합니다.
댓글
첫 번째 댓글을 남겨보세요.