Following system colour scheme Selected dark colour scheme Selected light colour scheme

Python 개선 제안 한국어 번역

PEP 686 – UTF-8 모드를 기본값으로 만들기

Author:
Inada Naoki <songofacandy at gmail.com>
Discussions-To:
Discourse thread
Status:
Final
Type:
Standards Track
Created:
18-Mar-2022
Python-Version:
3.15
Post-History:
18-Mar-2022, 31-Mar-2022
Resolution:
Discourse message

Table of Contents

번역·라이선스 안내

이 비공식 한국어 번역은 원문 Copyright 절의 Public Domain or CC0-1.0, whichever is more permissive 조건에 따라 제공합니다. 원저자와 공식 원문은 그대로 표시합니다. 수정되지 않은 기준 원문 · 공식 최신판

초록

이 PEP는 UTF-8 모드를 기본으로 활성화할 것을 제안합니다.

이 변경으로 Python은 파일, stdio, 파이프의 기본 인코딩으로 일관되게 UTF-8을 사용합니다.

동기

UTF-8은 사실상의 표준 텍스트 인코딩이 되었습니다.

  • Python 소스 파일의 기본 인코딩은 UTF-8입니다.
  • JSON, TOML, YAML은 UTF-8을 사용합니다.
  • Visual Studio Code와 Windows 메모장을 비롯한 대부분의 텍스트 편집기는 기본적으로 UTF-8을 사용합니다.
  • 인터넷의 대부분의 웹 사이트와 텍스트 데이터는 UTF-8을 사용합니다.
  • 그리고 Node.js, Go, Rust, Java를 비롯한 다른 많은 인기 프로그래밍 언어도 기본적으로 UTF-8을 사용합니다.

기본 인코딩을 UTF-8로 변경하면 Python이 이들과 더 쉽게 상호 운용할 수 있습니다.

또한 Unix를 사용하는 많은 Python 개발자는 기본 인코딩이 플랫폼에 따라 다르다는 사실을 잊곤 합니다. 이들은 UTF-8로 인코딩된 텍스트 파일(예: JSON, TOML, Markdown, Python 소스 파일)을 읽을 때 encoding="utf-8"을 지정하는 것을 빠뜨립니다. 일관되지 않은 기본 인코딩은 많은 버그를 일으킵니다.

명세

UTF-8 모드를 기본으로 활성화

Python은 Python 3.15부터 UTF-8 모드를 기본으로 활성화합니다.

사용자는 PYTHONUTF8=0 또는 -X utf8=0을 설정하여 여전히 UTF-8 모드를 비활성화할 수 있습니다.

locale.getencoding()

UTF-8 모드는 locale.getpreferredencoding(False)에 영향을 주므로, UTF-8 모드와 관계없이 로케일 인코딩을 얻을 수 있는 API가 필요합니다.

이 목적을 위해 locale.getencoding()이 추가됩니다. 이 함수도 로케일 인코딩을 반환하지만, UTF-8 모드는 무시합니다.

warn_default_encoding 옵션이 지정되면 locale.getpreferredencoding()은 open()과 마찬가지로 EncodingWarning을 발생시킵니다(PEP 597도 참고하십시오).

이 API는 Python 3.11에서 추가되었습니다.

encoding="locale" 옵션 수정

PEP 597은 TextIOWrapper에 encoding="locale" 옵션을 추가했습니다. 이 옵션은 로케일 인코딩을 명시적으로 지정하는 데 사용됩니다. 이 옵션이 지정되면 TextIOWrapper는 기본 텍스트 인코딩과 관계없이 로케일 인코딩을 사용해야 합니다.

하지만 현재 TextIOWrapper는 UTF-8 모드에서 encoding="locale"이 지정되더라도 "UTF-8"을 사용합니다. 이 동작은 PEP 597의 동기와 일치하지 않습니다. 이는 Python이 기본 텍스트 인코딩을 변경할 때 UTF-8 모드를 기본값으로 만들 것을 예상하지 못했기 때문입니다.

이 불일치는 UTF-8 모드를 기본값으로 만들기 전에 수정되어야 합니다. TextIOWrapper는 UTF-8 모드에서도 encoding="locale"이 전달되면 로케일 인코딩을 사용해야 합니다.

이 문제는 Python 3.11에서 수정되었습니다.

하위 호환성

대부분의 Unix 시스템은 UTF-8 로케일을 사용하며, Python은 로케일이 C 또는 POSIX일 때 UTF-8 모드를 활성화합니다. 따라서 이 변경은 주로 Windows 사용자에게 영향을 줍니다.

Python 프로그램이 기본 인코딩에 의존하는 경우, 이 변경으로 인해 UnicodeError, 문자 깨짐(mojibake), 심지어는 소리 없는 데이터 손상까지 발생할 수 있습니다. 따라서 이 변경은 널리 알려야 합니다.

이 하위 호환성 문제를 해결하기 위한 지침은 다음과 같습니다:

  1. UTF-8 모드를 비활성화하십시오.
  2. EncodingWarning (PEP 597)을 사용하여 UTF-8 모드가 영향을 미치는 모든 곳을 찾으십시오.
    • encoding 옵션을 생략했다면 encoding="utf-8" 또는 encoding="locale"을 사용하는 것을 고려하십시오.
    • locale.getpreferredencoding()을 사용한다면 "utf-8" 또는 locale.getencoding()을 사용하는 것을 고려하십시오.
  3. UTF-8 모드에서 애플리케이션을 테스트하십시오.

선행 사례

  • Ruby는 Ruby 3.0(2020)에서 Windows의 기본 external_encoding을 UTF-8로 변경했습니다.
  • Java는 JDK 18(2022)에서 기본 텍스트 인코딩을 UTF-8로 변경했습니다.

Ruby와 Java 모두 하위 호환성을 위한 옵션을 제공합니다. 두 언어 모두 기본 인코딩 사용에 대해 Python의 PEP 597의 EncodingWarning과 같은 경고를 제공하지 않습니다.

거부된 대안

암시적 인코딩 폐지

기본 인코딩 사용을 폐지하는 방안이 검토되었습니다.

하지만 기본 인코딩이 ASCII 텍스트만 읽거나 쓰는 데 사용되는 경우가 많습니다. 또한 이러한 경고는 Unix에서 실행되는, 크로스 플랫폼이 아닌 애플리케이션에는 유용하지 않습니다.

따라서 사용자에게 모든 곳에서 encoding을 지정하도록 강제하는 것은 너무 큰 부담입니다. DeprecationWarning을 대량으로 발생시키면 사용자가 경고를 무시하게 될 것입니다.

PEP 387은 하위 호환되지 않는 변경에 대해 경고를 추가할 것을 요구합니다. 하지만 DeprecationWarning을 사용할 것까지 요구하지는 않습니다. 따라서 선택적인 EncodingWarning을 사용하는 것은 PEP 387을 위반하지 않습니다.

Java 역시 JEP 400에서 이 아이디어를 거부했습니다.

PIPE에 PYTHONIOENCODING 사용

하위 호환성 문제를 완화하기 위해 subprocess 모듈에서 PYTHONIOENCODING을 PIPE의 기본 인코딩으로 사용하는 방안이 검토되었습니다.

이 방안을 따르면 사용자는 UTF-8 모드에서도 subprocess.Popen(text=True)에 레거시 인코딩을 사용할 수 있습니다.

하지만 이 아이디어는 “기본 인코딩”을 복잡하게 만듭니다. 그리고 이 아이디어는 하위 호환성도 깨뜨립니다.

따라서 이 아이디어는 기각되었습니다. 사용자는 text=True를 encoding="utf-8" 또는 encoding="locale"로 바꿀 때까지 UTF-8 모드를 비활성화할 수 있습니다.

교육 방법

신규 사용자에게는 이 변경으로 가르쳐야 할 내용이 줄어듭니다. 사용자는 처음 1년 동안 텍스트 인코딩에 대해 배울 필요가 없습니다. UTF-8이 아닌 텍스트 파일을 사용해야 할 때 배우면 됩니다.

기존 사용자는 Backward Compatibility 절을 참조하십시오.

참고 문헌