JSON에 저장한 한글이 이스케이프돼 보이고 Windows에서는 읽다가 깨진 이유
문제 발생
안내 문구를 JSON 파일로 저장해 두는 스크립트였습니다.
import json
with open("messages.json", "w") as f:
json.dump({"greeting": "안녕하세요"}, f)파일을 열어 보니 한글 대신 이런 게 들어 있었습니다.
{"greeting": "\uc548\ub155\ud558\uc138\uc694"}사람이 고쳐야 하는 파일이라 ensure_ascii=False를 붙여 한글이 그대로 보이게 했습니다. 그런데 이번엔 Windows를 쓰는 동료가 같은 파일을 읽다가 UnicodeDecodeError를 만났습니다. 제 맥에서는 아무 문제가 없었는데 말입니다.
원인 분석
json.dump는 기본으로 ASCII 밖의 문자를 전부 이스케이프합니다. Python 문서에 따르면 ensure_ascii가 True(기본값)이면 비ASCII 문자와 출력할 수 없는 문자가 모두 이스케이프된다고 보장합니다. 문서는 RFC가 허용하는 범위에서 이 기본값을 택했다고 적어 두었습니다. 그러니 처음 파일은 깨진 게 아니라 \uXXXX로 적힌 정상적인 JSON이었습니다. False로 주면 따옴표, 역슬래시, 제어 문자만 빼고 그대로 씁니다.
진짜 문제는 open()의 인코딩이었습니다. open() 문서에 따르면 인코딩을 지정하지 않으면 기본값은 플랫폼마다 다르고, locale.getencoding()이 돌려주는 값을 씁니다. io 문서는 이 상황을 대놓고 짚습니다 — 대부분의 유닉스 플랫폼이 기본으로 UTF-8 로캘을 쓰다 보니 JSON, TOML, Markdown처럼 UTF-8로 된 파일을 열 때 인코딩 지정을 잊는 개발자가 많고, 대부분의 Windows 사용자는 로캘 인코딩이 UTF-8이 아니라서 버그가 생긴다고요.
ensure_ascii=True일 때는 파일에 ASCII만 들어가니 어떤 인코딩으로 읽어도 문제가 없었습니다. 한글을 그대로 쓰게 바꾸는 순간, 그동안 숨어 있던 인코딩 문제가 드러난 겁니다.
해결 방안
- 파일을 열 때는 항상
encoding="utf-8"을 적습니다. 쓸 때도, 읽을 때도요.
with open("messages.json", "w", encoding="utf-8") as f:
json.dump(data, f, ensure_ascii=False, indent=2)
with open("messages.json", encoding="utf-8") as f:
data = json.load(f)-
ensure_ascii는 사람이 읽을 파일인지로 정합니다. 사람이 열어서 고치는 설정이나 문구 파일이라면False가 낫습니다. 다른 시스템에 넘기는 페이로드처럼 사람이 볼 일이 없다면 기본값 그대로 둬도 읽어 들인 데이터는 똑같습니다. -
빠뜨린 곳은 경고로 찾습니다.
-X warn_default_encoding옵션을 켜거나PYTHONWARNDEFAULTENCODING환경 변수를 설정하면, 기본 인코딩이 쓰이는 곳마다EncodingWarning이 납니다. Python 3.10부터 들어간 기능입니다. CI에서 켜 두면 새로 생기는 곳도 바로 잡힙니다.
python -X warn_default_encoding -W error::EncodingWarning -m pytest- UTF-8 모드는 보조 수단으로 둡니다. io 문서에 따르면 Python UTF-8 모드를 켜면 기본 인코딩을 로캘 대신 UTF-8로 바꿀 수 있고, PEP 686에 따라 Python 3.15부터는 이 모드가 기본이 됩니다. 다만 그 전 버전에서는 실행 환경마다 켜 줘야 하니, 코드에
encoding을 적는 쪽이 어디서 돌려도 확실합니다.
댓글0
댓글을 남기려면 로그인이 필요해요. 로그인
아직 댓글이 없어요. 첫 의견을 편하게 남겨 보세요.