Python 3.15, UTF-8 기본값이 되면 한글 CSV는?

엑셀로 저장한 한글 CSV와 그 파일을 읽는 스크립트는 그대로 두고 Python만 3.15로 올렸다. 어제까지 읽히던 한글은 오늘도 제대로 읽힐까?

Python 3.15, UTF-8 기본값이 되면 한글 CSV는?
세 줄 요약
  • Python 3.15는 UTF-8 모드를 기본으로 켠다. open()에서 인코딩을 생략하던 코드가 주요 확인 대상이다.
  • 이미 CP949로 저장된 파일은 그대로 남는다. 해당 파일을 읽을 때는 encoding="cp949"처럼 실제 형식을 지정한다.
  • EncodingWarning으로 기본값을 쓰는 위치를 찾고, UTF-8 모드를 켠 상태에서 실제 입출력 파일을 시험한다.

소스 코드가 아니라 텍스트 입출력의 기본값이 바뀐다

Python 3.15는 10월 9일 출시됐고, 10월 21일에는 3.15.1이 나왔다. 3.15 계열의 변화 중 업무 스크립트에 바로 영향을 줄 수 있는 것은 UTF-8 모드의 기본 활성화다.

PEP 686은 운영체제 로캘에 따라 기본 인코딩이 달라지는 문제를 줄이려는 변경이라고 설명한다. 특히 UTF-8이 아닌 로캘 인코딩을 사용하던 Windows 코드에서 기존 동작과 차이가 생길 수 있다. 파이썬 소스 파일 자체의 기본 인코딩은 이미 UTF-8이었다.

예를 들어 open("sales.csv")는 파일에 어떤 문자가 들어 있는지 보고 인코딩을 알아내는 코드가 아니다. 3.15의 io 문서에 따르면 기본값은 UTF-8이며, UTF-8 모드를 끈 경우에는 로캘 인코딩을 따른다. 버전을 바꿨다고 기존 CSV의 바이트까지 UTF-8로 바뀌지는 않는다.

CP949 파일에는 CP949를 지정해야 한다

파일의 인코딩을 이미 알고 있다면 그 값을 읽기·쓰기 코드에 넣는 것이 가장 분명하다. 다음 표는 파일을 만든 시스템이나 명세로 인코딩을 확인한 경우의 선택이다. 확장자가 CSV이거나 한글이 들어 있다는 이유만으로 CP949라고 정할 수는 없다.

확인된 파일 조건읽을 때 지정할 값의미
UTF-8 텍스트encoding="utf-8"운영체제 기본값에 의존하지 않는다.
CP949로 내보낸 한글 텍스트encoding="cp949"파일을 저장한 인코딩으로 해석한다.
UTF-8 BOM을 포함할 수 있는 텍스트encoding="utf-8-sig"읽을 때 시작 부분의 UTF-8 BOM을 건너뛴다.

CP949와 UTF-8 BOM 처리 방식은 표준 codecs 문서에 명시돼 있다. 예를 들어 CP949로 저장됐음을 확인한 보고서라면 다음처럼 읽는 인코딩을 고정할 수 있다.

with open("sales.csv", encoding="cp949") as source:
    text = source.read()

이 예제는 읽기 규칙을 명시한다. 파일 형식을 UTF-8로 통일하려면 원래 인코딩으로 읽고 UTF-8로 다시 저장하는 별도 변환 과정이 필요하다. 저장한 파일을 전달받는 시스템이 요구하는 인코딩도 확인해야 한다.

EncodingWarning은 기본값을 쓴 위치를 보여준다

기존 코드에서 인코딩을 생략한 곳은 -X warn_default_encoding 옵션으로 찾을 수 있다. 공식 설명에 따르면 기본 인코딩을 사용하는 지점에서 EncodingWarning이 나온다.

python -X warn_default_encoding your_script.py

여기서 python은 점검하려는 인터프리터를 가리켜야 한다. 경고는 실행한 코드 경로에서 발생하므로 드물게 들어오는 파일이나 예외 처리 분기도 시험에 포함해야 한다. 이 옵션이 파일의 실제 인코딩을 판별하거나 아직 실행하지 않은 모든 분기를 검사해 주는 것은 아니다.

UTF-8 모드를 끄는 것과 파일 형식을 정하는 것은 다르다

명령행 옵션 -X utf8=0은 UTF-8 모드를 명시적으로 끈다. 전환 때문에 동작이 달라졌는지 비교할 때는 다음 두 실행을 대조할 수 있다.

python -X utf8=0 -X warn_default_encoding your_script.py
python -X utf8 your_script.py

모드를 끈다고 어디서나 CP949가 되는 것은 아니다. 그 환경의 로캘 인코딩으로 돌아간다. 반대로 운영체제의 로캘을 의도적으로 따라야 하는 입출력이라면 encoding="locale"로 그 의도를 명시할 수 있다. PEP 686은 이 값이 UTF-8 모드에서도 로캘 인코딩을 사용하도록 정의한다.

업무 파일 교환에서는 파일을 만드는 쪽과 읽는 쪽이 같은 인코딩에 합의하는 것이 우선이다. 기본값을 쓰던 위치를 찾고 실제 파일 형식을 정한 뒤, UTF-8 모드에서 입력과 출력 결과를 확인하면 된다.

자주 묻는 질문

Python을 올리면 CP949 CSV가 UTF-8로 변환되는가?

아니다. 바뀌는 것은 인코딩을 생략했을 때의 해석·저장 기본값이다. 기존 파일을 변환하려면 원래 인코딩으로 읽은 뒤 원하는 인코딩으로 다시 저장해야 한다.

모든 한글 파일에 cp949를 지정하면 되는가?

아니다. 한글은 UTF-8로도 저장된다. 파일을 생성한 시스템의 인코딩을 확인한 뒤 지정해야 한다.

Python 3.15로 옮길 때 먼저 볼 곳은 인코딩을 생략한 파일 입출력이다. 인코딩을 코드에 명시하면 운영체제나 Python 버전의 기본값에 의존하는 부분을 줄일 수 있다.