파이썬 3.15 정식 D-13! 내 코드 안 깨지나 미리 점검하기 - UTF-8 기본값(cp949 탈출)·lazy import 마이그레이션 체크리스트 🐍
목차12개 항목
안녕하세요! Mojangcam입니다 😎
드디어 파이썬 3.15 정식 버전이 2026년 10월 1일에 나옵니다. 오늘이 9월 18일이니까 딱 D-13이에요.
지금까지 이 블로그에서 3.15는 베타 4편, rc1편, rc2편까지 "뭐가 새로 생겼나" 위주로 다뤘는데요. 오늘은 방향을 좀 바꿔볼게요. 신기능 구경이 아니라 "내가 지금 쓰는 코드가 3.15에서 안 깨지나?" 를 점검하는 글입니다.
특히 한국에서 파이썬 쓰는 분들한테는 이번 3.15가 진짜 큰 변화예요. 그 악명 높은 cp949 인코딩 지옥이 드디어 끝나거든요. 근데 끝나는 대신, 그 동안 cp949에 기대서 돌아가던 코드는 반대로 깨질 수 있어요. 😅

📌 준비물
- Python 3.15.0rc2 (2026-09-01 릴리스, 정식 전 마지막 rc)
- 기존에 쓰던 Python 3.13 또는 3.14 (비교용으로 같이 깔아두면 좋아요)
- 테스트용 텍스트 파일 몇 개 (가능하면 cp949로 저장된 한글 파일 하나)
- 편한 에디터 (VS Code 추천)
정식 전에 미리 돌려보는 게 목적이니까, 기존 버전은 지우지 말고 따로 설치하세요. 윈도우는 설치할 때 py 런처가 같이 깔리니까 py -3.15 스크립트.py 로 버전을 골라 실행할 수 있어요.
# 설치된 파이썬 버전 확인 (윈도우)
py --list
# 3.15로 실행
py -3.15 test.py
# 맥/리눅스
python3.15 test.py🧠 왜 지금 점검해야 하나요?
"정식 나오면 그때 하지 뭐" 하시는 분들 많은데요. 이번엔 조금 다릅니다.
3.15의 PEP 686은 파일을 여는 방식 자체의 기본값을 바꿉니다. 이게 무서운 이유는, 에러가 안 나고 조용히 깨질 수도 있다는 거예요. 프로그램은 잘 돌아가는데 CSV에 한글이 ????? 로 들어가 있거나, 로그 파일이 열어보니 외계어인 상황이요.
특히 매일 자동으로 도는 스크립트(엑셀 자동화, 크롤링, 메일 발송 같은 것들)는 사람이 결과를 매번 확인하지 않으니까 며칠 뒤에나 발견하게 되죠. 그래서 미리 점검이 필요합니다.
🧩 변화 1 - 이제 파일은 무조건 UTF-8로 열려요 (PEP 686)
가장 중요한 변화부터 볼게요.
3.14까지는 open()에 encoding을 안 적으면 운영체제 로케일 인코딩을 썼어요. 한글 윈도우에서는 그게 바로 cp949(euc-kr 계열) 였고요. 맥·리눅스에서는 UTF-8이었죠. 그래서 같은 코드를 윈도우에서 돌리면 깨지고 맥에서는 멀쩡한, 그 유명한 현상이 나왔던 거예요.
3.15부터는 운영체제와 상관없이 무조건 UTF-8입니다.
# Python 3.15 / 인코딩을 안 적으면?
with open("메모.txt") as f: # 3.14 이하(윈도우) → cp949
print(f.read()) # 3.15 → UTF-8 (OS 무관)하나씩 뜯어볼게요 💡
- open()의 기본 encoding : 3.15부터 UTF-8 고정. PYTHONUTF8 같은 환경변수를 안 건드려도 그래요.
- subprocess(text=True) : 이쪽도 같이 UTF-8 기준으로 동작해요.
- encoding="locale" : 예전처럼 "OS 로케일을 따라가라"고 명시하고 싶으면 이 특수값을 쓰면 됩니다.
- locale.getencoding() : 현재 OS 로케일 인코딩이 뭔지 직접 알아낼 때 쓰는 함수예요.
🛠️ 내 코드가 위험한지 30초 만에 확인하는 법
제일 빠른 방법은 인코딩을 생략한 open() 을 전부 찾아내는 것이에요. 파이썬에는 이걸 위한 경고 옵션이 원래부터 있어요.
# 기본 인코딩에 의존하는 코드에 경고를 띄워줍니다
py -3.15 -X warn_default_encoding my_script.py이렇게 돌리면 EncodingWarning 이 뜨면서 몇 번째 줄이 문제인지 알려줘요. 여기 걸린 줄들이 3.15에서 동작이 바뀔 후보들입니다.
🛠️ 고치는 방법은 셋 중 하나
① 가장 권장 - 인코딩을 직접 적기
# 앞으로 만들 파일: UTF-8로 통일
with open("result.txt", "w", encoding="utf-8") as f:
f.write("안녕하세요")
# 예전에 cp949로 저장해둔 파일을 읽을 때
with open("old_data.csv", encoding="cp949") as f:
print(f.read())제일 확실합니다. 어차피 버전이 뭐든 똑같이 동작하니까요. 인코딩은 그냥 항상 적는다를 습관으로 만드는 게 정답이에요.
② 예전 동작 그대로 쓰고 싶다면
# "OS 로케일을 따르라"고 명시 (윈도우 한글이면 cp949)
with open("legacy.txt", encoding="locale") as f:
print(f.read())③ 급하면 통째로 되돌리기 (임시방편)
# 환경변수로 UTF-8 모드 끄기
set PYTHONUTF8=0 # 윈도우 CMD
# 또는 실행 옵션으로
py -3.15 -X utf8=0 my_script.py③은 임시방편이에요. 파이썬이 UTF-8로 가는 흐름은 되돌아오지 않으니, 시간 벌어놓고 결국 ①로 고치는 게 맞습니다.
💡 엑셀 한글 깨짐은 이야기가 조금 달라요
자동화 글감으로 자주 나오는 케이스인데요. UTF-8로 CSV를 저장했는데 엑셀에서 열면 한글이 깨지는 문제는 3.15와 무관하게 예전부터 있던 별개 이슈예요. 엑셀이 BOM 없는 UTF-8을 잘 못 알아보거든요.
import csv
# 엑셀에서 두 번 클릭해 열 CSV라면 utf-8-sig
with open("보고서.csv", "w", encoding="utf-8-sig", newline="") as f:
w = csv.writer(f)
w.writerow(["이름", "부서", "점수"])
w.writerow(["홍길동", "영업", 92])utf-8-sig : UTF-8인데 파일 맨 앞에 BOM 표식을 붙여주는 인코딩이에요. 엑셀이 이걸 보고 "아 UTF-8이구나" 하고 알아봅니다.
🧩 변화 2 - lazy import, 이제 진짜 씁니다 (PEP 810)
두 번째는 명시적 지연 임포트예요. rc 단계에서 확정돼서 정식에 그대로 들어갑니다.
# Python 3.15+
lazy import json
lazy from pathlib import Pathlazy 를 붙인 임포트는 그 이름을 실제로 쓰는 순간까지 모듈을 안 불러와요. 무거운 라이브러리를 맨 위에 잔뜩 적어두고 실제로는 특정 명령에서만 쓰는 스크립트라면, 시작 속도가 눈에 띄게 빨라집니다.
하나씩 뜯어볼게요 💡
- lazy : 예약어가 아니라 소프트 키워드예요. 그래서 lazy 라는 변수명을 쓰던 기존 코드는 안 깨집니다.
- 모듈 최상단에서만 가능 : 함수 안, 클래스 안, try/except/finally 안에서 쓰면 SyntaxError가 납니다.
- 안 되는 조합 : lazy from module import * 와 lazy from __future__ import ... 는 불가예요.
- sys.lazy_modules : 지연된 모듈은 sys.modules 대신 여기에 이름이 올라가요.
- -X lazy_imports=all : 소스를 안 고치고 전부 지연시키는 실행 옵션. 반대로 normal(기본값)은 lazy 키워드만 존중해요.
⚠️ 여기서 주의할 점
lazy import 를 쓰면 임포트 실패가 나중에 터집니다. 원래는 프로그램 시작하자마자 ModuleNotFoundError 로 알려줬을 걸, 그 모듈을 처음 쓰는 시점에 터뜨리거든요.
24시간 돌아가는 디스코드 봇처럼 새벽에 조용히 죽으면 곤란한 프로그램이라면, 지연 임포트는 아껴 쓰고 에러 핸들링·로깅을 같이 깔아두는 걸 권합니다. (관련 글: 「파이썬 업무 자동화 #13 - 로그·재시도·실패 알림 체계 만들기」, 「디스코드 봇 만들기 #25 - 에러 핸들링·로깅 체계 잡기」)
또 하나, 3.15에서만 되는 문법이라는 점도 기억하세요. 3.14 이하에서 돌려야 하는 코드에 lazy 를 적으면 그냥 문법 에러입니다. 두 버전을 다 지원해야 하면 __lazy_modules__ 를 쓰는 방법이 있어요.
# 3.15에서는 지연, 그 이하에서는 그냥 무시되는 방식
__lazy_modules__ = ["json", "pathlib"]
import json # 3.15에서 lazy로 동작📌 마이그레이션 체크리스트 (복붙 OK)
정식 전에 이 순서대로 한 번씩만 돌려보세요.
# 1) 3.15rc2 설치 후 기존 버전과 공존 확인
py --list
# 2) 기본 인코딩에 의존하는 코드 전부 찾아내기
py -3.15 -X warn_default_encoding my_script.py
# 3) 테스트가 있다면 경고를 켜고 전체 실행
py -3.15 -X warn_default_encoding -m pytest
# 4) 주요 라이브러리 3.15 대응 여부 확인
py -3.15 -m pip install -r requirements.txt그리고 아래 항목을 눈으로 체크해보세요.
- open() 에 encoding= 을 안 적은 곳이 남아 있나요?
- 과거에 cp949로 저장해둔 파일을 읽는 코드가 있나요? → encoding="cp949" 명시
- 엑셀로 열 CSV를 만드나요? → encoding="utf-8-sig"
- subprocess.run(..., text=True) 로 윈도우 명령 출력을 받나요? → 한글 출력이 깨지는지 확인
- requirements.txt 의 라이브러리들이 3.15용 휠을 올렸나요? (C 확장이 있는 패키지는 새 버전 대응이 늦을 수 있어요)
- 배포 서버·깃허브 액션의 파이썬 버전을 언제 올릴지 정했나요?

📌 자주 나는 에러
1. UnicodeDecodeError: 'utf-8' codec can't decode byte 0xb0 in position 0
- 원인 : 3.15가 UTF-8로 읽으려 했는데, 그 파일이 실은 cp949로 저장된 한글 파일이라서요. 3.14에서는 멀쩡했던 코드가 3.15에서 처음 터지는 대표 케이스입니다.
- 해결 : open(경로, encoding="cp949") 로 명시하세요. 파일 자체를 UTF-8로 다시 저장해두는 게 더 깔끔합니다.
2. UnicodeDecodeError: 'cp949' codec can't decode byte ...
- 원인 : 위와 정반대. 3.14 이하 윈도우에서 UTF-8 파일을 인코딩 없이 연 경우예요.
- 해결 : encoding="utf-8" 명시. 이 에러가 자주 난다면 3.15로 올리는 것만으로도 상당수 사라집니다.
3. SyntaxError: lazy import is only allowed at module level
- 원인 : lazy 를 함수 안이나 try 블록 안에서 썼어요.
- 해결 : 파일 최상단으로 옮기세요. 조건부로 불러와야 하면 예전처럼 함수 안에서 일반 import 를 쓰면 됩니다.
4. 프로그램은 잘 도는데 결과 파일의 한글만 깨짐 (에러 없음)
- 원인 : 읽기·쓰기 인코딩이 서로 다른데 예외는 안 난 경우. 제일 찾기 어려운 유형이에요.
- 해결 : 읽기/쓰기 양쪽에 인코딩을 전부 명시하고, -X warn_default_encoding 으로 빠진 곳이 없는지 훑으세요.
5. ERROR: Could not build wheels for ... (3.15 설치 중)
- 원인 : 해당 라이브러리가 아직 3.15용 빌드를 안 올렸어요. 정식 직후 몇 주간 흔한 일입니다.
- 해결 : 급하지 않으면 기다리세요. 업무용 환경은 정식 나오자마자 올리지 말고 주요 의존성이 3.15 휠을 올린 뒤에 옮기는 게 안전합니다.
📌 마무리
오늘 요약할게요 🙌
- 10월 1일 파이썬 3.15 정식 출시. 오늘 기준 D-13
- PEP 686 : open() 기본 인코딩이 OS와 무관하게 UTF-8로 통일 → 한국 윈도우 사용자에게 제일 큰 변화
- 되돌리려면 encoding="locale", PYTHONUTF8=0, -X utf8=0 — 다만 임시방편
- PEP 810 : lazy import 로 시작 속도 개선. 단 모듈 최상단에서만, 그리고 3.15 전용 문법
- 지금 할 일은 딱 하나 — -X warn_default_encoding 으로 인코딩 안 적은 곳 찾아서 채워넣기
다음 편에서는 정식 출시에 맞춰 3.15로 실제 이사하는 과정을 다뤄볼까 해요. 가상환경 새로 만들고, 라이브러리 호환성 확인하고, 자동화 스크립트를 무중단으로 옮기는 순서까지요. 기대해주세요! 💬
📚 관련 글
- 파이썬 3.15 새 기능 정리 - lazy import·frozendict·UTF-8 기본값 (베타 4 기준)
- 파이썬 3.15 rc1 출시! lazy import 확정 - 베타4에서 달라진 점 총정리
- 파이썬 3.15 rc2 출시! 정식(10/1) 전 마지막 점검 - rc1에서 달라진 점 총정리
- 파이썬 3.15 정식 D-13 마이그레이션 체크리스트 ← 지금 이 글!
- 파이썬 가상환경(venv) 완벽 정리 - 만들기부터 활성화·삭제까지
