Windows Python 스크립트 한글 깨짐 완전 정복 — 삽질 후기
Windows에서 Python 자동화 스크립트 돌릴 때 한글이 깨지는 문제, 실제로 겪고 해결한 방법들. UnicodeEncodeError, CP949, PowerShell 인코딩 문제까지.
문제 상황
Windows에서 Python으로 자동화 스크립트를 만들다 보면 반드시 한 번은 마주치는 상황:
UnicodeEncodeError: 'cp949' codec can't encode character '\U0001f916'
또는 텔레그램 봇이 보내는 메시지에 한글이 ???로 나오거나, 로그 파일이 깨져서 읽을 수가 없거나.
특히 이모지가 포함된 텍스트를 다룰 때, 또는 Google API 응답에서 한글 데이터를 처리할 때 자주 터집니다.
왜 이런 일이 생기나
Windows의 기본 인코딩이 CP949(EUC-KR 계열)이기 때문입니다.
Python 3.x에서 print() 하면 내부적으로 sys.stdout의 인코딩을 따르는데, Windows에서는 이게 CP949입니다. UTF-8로 인코딩된 한글이나 이모지는 CP949로 출력할 수 없어서 에러가 납니다.
해결법 1 — 스크립트 상단에 추가 (가장 확실)
import sys
import io
sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding='utf-8')
sys.stderr = io.TextIOWrapper(sys.stderr.buffer, encoding='utf-8')
스크립트 맨 위에 이 두 줄 추가하면 해당 스크립트 실행 동안 stdout/stderr가 UTF-8로 동작합니다.
단, reconfigure 방식을 쓸 수도 있습니다:
if hasattr(sys.stdout, 'reconfigure'):
sys.stdout.reconfigure(encoding='utf-8', errors='replace')
errors='replace'를 쓰면 인코딩 불가능한 문자가 나와도 에러 대신 ?로 대체합니다. 로그 스크립트처럼 무조건 돌아가야 하는 경우에 유용합니다.
해결법 2 — 환경 변수 설정 (시스템 전체)
set PYTHONIOENCODING=utf-8
또는 PowerShell에서:
$env:PYTHONIOENCODING = "utf-8"
이걸 시스템 환경 변수로 등록해두면 모든 Python 스크립트에 적용됩니다. 하지만 다른 사람 환경에서 돌릴 때 이 변수가 없으면 또 깨지니까, 스크립트 안에서 처리하는 게 더 안전합니다.
해결법 3 — Python 3.7+ PYTHONUTF8 모드
python -X utf8 script.py
또는 환경 변수:
set PYTHONUTF8=1
Python 3.7 이후에서 쓸 수 있는 공식 방법입니다. 이걸 켜면 거의 모든 인코딩 관련 기본값이 UTF-8로 바뀝니다.
파일 읽고 쓸 때
open()에 인코딩 명시가 빠지면 또 문제납니다.
# 잘못된 방법 (Windows에서 CP949로 열림)
with open('output.txt', 'w') as f:
f.write('한글 내용')
# 올바른 방법
with open('output.txt', 'w', encoding='utf-8') as f:
f.write('한글 내용')
읽을 때도 마찬가지:
# 기존 파일이 UTF-8인지 CP949인지 모를 때
with open('file.txt', 'r', encoding='utf-8', errors='ignore') as f:
content = f.read()
PowerShell에서 Python 출력이 깨질 때
PowerShell 자체의 인코딩도 문제가 됩니다. Python 스크립트 출력이 PowerShell 터미널에서 깨진다면:
[Console]::OutputEncoding = [System.Text.Encoding]::UTF8
또는 chcp 65001 명령어로 코드 페이지를 UTF-8로 변경:
chcp 65001
python script.py
마크다운 파일에 BOM이 숨어 있을 때
이건 진짜 찾기 힘든 문제입니다.
PowerShell에서 Set-Content -Encoding UTF8으로 파일 저장하면 **BOM(Byte Order Mark)**이 붙습니다. 바이트로 보면 파일 시작이 EF BB BF로 시작하는 것입니다.
대부분의 프로그램은 이걸 무시하지만, Astro.js 같은 정적 사이트 생성기의 YAML 프론트매터 파서는 BOM 있으면 frontmatter를 아예 안 읽어버립니다.
BOM 없이 저장하려면:
# BOM 없는 UTF-8로 저장
$utf8NoBom = New-Object System.Text.UTF8Encoding $false
[System.IO.File]::WriteAllBytes("파일경로", $utf8NoBom.GetBytes($content))
또는 Python에서:
with open('file.md', 'w', encoding='utf-8-sig') as f: # utf-8-sig = BOM 포함
...
# BOM 없이:
with open('file.md', 'w', encoding='utf-8') as f:
...
요약
| 상황 | 해결법 |
|---|---|
| 스크립트 실행 중 UnicodeEncodeError | sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding='utf-8') |
| 시스템 전체에 적용 | PYTHONIOENCODING=utf-8 환경 변수 |
| 파일 읽기/쓰기 | open(..., encoding='utf-8') 명시 |
| PowerShell 터미널 깨짐 | chcp 65001 또는 OutputEncoding 설정 |
| BOM 때문에 파서 오류 | UTF8Encoding(false)로 저장 |
Windows에서 Python 자동화 스크립트 만들 때 인코딩 문제는 거의 필수 코스입니다. 미리 알고 쓰면 몇 시간씩 날리지 않아도 됩니다.