- Published on
Python 3.12에서 pkg_resources 에러 근본 해결
- Authors
- Name
- 스타차일드
- https://x.com/ETFBITX
서버나 로컬에서 Python 3.12로 업그레이드한 뒤 갑자기 pkg_resources 에러가 터지는 경우가 많습니다. 표면적으로는 ModuleNotFoundError: No module named 'pkg_resources' 혹은 ImportError 형태지만, 실제 원인은 대부분 오래된 의존성(특히 setuptools에 묶인 레거시 API), 빌드 백엔드/패키징 표준(PEP 517/518) 전환, 환경 격리 실패(전역 site-packages 오염) 같은 구조적인 문제입니다.
이 글에서는 “일단 setuptools 설치해서 끝” 같은 임시 처방이 아니라, Python 3.12에서 왜 이 문제가 더 자주 보이는지와 함께 **근본 해결 루트(의존성 제거/대체/고정/빌드 방식 정리)**를 단계별로 정리합니다.
1) 대표 증상과 에러 패턴
Python 3.12에서 흔히 만나는 메시지는 다음과 같습니다.
ModuleNotFoundError: No module named 'pkg_resources'ImportError: cannot import name '...' from pkg_resourcespkg_resources.DistributionNotFound(런타임에 패키지 메타데이터 탐색 실패)- 특정 라이브러리 import 시 내부적으로
pkg_resources를 호출하다가 실패
중요한 포인트는 pkg_resources가 표준 라이브러리(stdlib)가 아니라는 점입니다. 이는 setuptools 패키지에 포함된 레거시 유틸리티이며, Python 자체가 보장하지 않습니다.
2) 왜 Python 3.12에서 더 자주 터지나?
2.1 pkg_resources는 “레거시”이고, 대체재가 표준화됨
최근 파이썬 생태계는 패키지 메타데이터 접근을 pkg_resources 대신 아래로 옮기고 있습니다.
importlib.metadata(Python 3.8+ stdlib)packaging(버전 비교/스펙 파싱)
즉, 새로운 라이브러리들은 굳이 pkg_resources에 의존하지 않는데, 오래된 라이브러리/사내 코드가 여전히 pkg_resources를 쓰고 있으면 Python 업그레이드 시점에 충돌이 표면화됩니다.
2.2 setuptools가 기본으로 “항상” 깔려있다는 가정이 깨짐
과거에는 많은 환경에서 setuptools가 사실상 기본처럼 존재했습니다. 하지만 컨테이너/슬림 이미지/빌드 최적화가 일반화되면서
- 런타임 이미지에서 빌드 도구(setuptools, wheel)를 제거
- 최소 설치로 운영
같은 패턴이 늘었고, 그 결과 pkg_resources가 없는 런타임이 흔해졌습니다.
2.3 빌드 백엔드(PEP 517) 전환으로 설치 경로가 달라짐
pip는 PEP 517/518 기반 빌드가 기본이 되었고, 예전처럼 setup.py에 기대던 흐름이 바뀌었습니다. 오래된 패키지가 빌드/설치 시점에 setuptools 동작을 전제하면 Python 3.12에서 더 쉽게 문제를 드러냅니다.
3) 가장 먼저 해야 할 진단: “누가 pkg_resources를 요구하나?”
근본 해결의 시작은 범인 패키지를 찾는 것입니다.
3.1 실행 시 import 트레이스에서 역추적
에러 로그에 pkg_resources를 import한 모듈 경로가 보입니다. 그 파일이 속한 패키지가 1차 후보입니다.
3.2 설치된 패키지에서 문자열 검색(빠르고 확실)
가상환경에서 site-packages를 대상으로 pkg_resources 사용처를 검색합니다.
python -c "import site; print('\n'.join(site.getsitepackages()))"
# venv라면 보통 .venv/lib/python3.12/site-packages
grep -R "import pkg_resources" -n .venv/lib/python3.12/site-packages | head
Windows라면 PowerShell에서:
Select-String -Path .venv\Lib\site-packages\*.py -Pattern "import pkg_resources" -Recurse | Select-Object -First 20
3.3 의존성 트리로 “간접 의존” 확인
직접 설치한 적 없는 패키지가 끌고 들어오는 경우가 많습니다.
python -m pip install -U pip
python -m pip install pipdeptree
pipdeptree | grep -n "setuptools\|pkg_resources" -n
4) 근본 해결 전략 4가지 (우선순위 순)
여기서부터가 핵심입니다. 가능하면 1→2→3 순으로 해결하는 것을 권합니다.
4.1 (최우선) pkg_resources 의존을 제거하고 표준 API로 교체
사내 코드나 유지보수 가능한 코드라면, pkg_resources 사용을 없애는 것이 가장 확실합니다.
4.1.1 버전/메타데이터 조회 대체
기존 코드:
import pkg_resources
version = pkg_resources.get_distribution("requests").version
대체 코드(Python 3.12 권장):
from importlib.metadata import version, PackageNotFoundError
try:
ver = version("requests")
except PackageNotFoundError:
ver = None
print(ver)
4.1.2 엔트리포인트 로딩 대체
기존 코드(레거시):
import pkg_resources
for ep in pkg_resources.iter_entry_points("my.plugins"):
plugin = ep.load()
대체 코드:
from importlib.metadata import entry_points
# Python 3.10+ 스타일
for ep in entry_points(group="my.plugins"):
plugin = ep.load()
이 방식은 setuptools에 기대지 않고도 동작하며, Python 3.12에서 훨씬 안정적입니다.
4.2 (차선) 범인 라이브러리 업데이트 또는 교체
대부분의 오픈소스는 이미 importlib.metadata로 넘어갔거나, 최소한 Python 3.12 호환 버전을 제공합니다.
pip install -U <problem-package>- 릴리즈 노트에서 “drop pkg_resources” / “use importlib.metadata” 같은 변경 확인
만약 특정 패키지가 장기간 방치되어 있고 pkg_resources에 강하게 묶여 있다면, 대체 라이브러리를 찾거나 포크 후 패치하는 편이 장기적으로 낫습니다.
4.3 (조건부) setuptools를 “명시적으로” 런타임 의존성으로 고정
업데이트/교체가 불가능하고, 당장 서비스가 돌아가야 한다면 setuptools를 설치하는 것이 현실적인 처방일 수 있습니다. 다만 이 경우도 명시적으로 고정해야 재발을 막습니다.
python -m pip install "setuptools>=68"
python -c "import pkg_resources; print(pkg_resources.__version__)"
그리고 requirements.txt 혹은 pyproject.toml에 명시합니다.
requirements.txt 예:
setuptools>=68
단, 이 방법은 “근본 해결”이라기보다 레거시 의존을 유지하는 비용을 받아들이는 선택입니다.
4.4 (운영 관점) 빌드/런타임 이미지를 분리해 setuptools 유무를 통제
컨테이너 환경에서 특히 많이 발생합니다.
- 빌드 단계: wheel 빌드에 setuptools 필요
- 런타임 단계: 최소 이미지로 setuptools 제거
그런데 런타임에서 레거시 코드가 pkg_resources를 import하면 폭발합니다. 해결은 두 가지 중 하나입니다.
- 런타임에 setuptools 포함(명시적)
- 런타임 코드에서 pkg_resources 제거(권장)
멀티스테이지 Dockerfile 예시:
# build stage
FROM python:3.12-slim AS build
WORKDIR /app
COPY pyproject.toml poetry.lock ./
RUN pip install -U pip setuptools wheel
# 여기서 wheel 빌드/의존 설치 수행
# runtime stage
FROM python:3.12-slim
WORKDIR /app
COPY /usr/local /usr/local
COPY . .
CMD ["python", "-m", "myapp"]
이때 런타임에서 pkg_resources가 필요하다면, build에서 설치된 것이 runtime에 포함되도록 구성해야 합니다(위 예시는 /usr/local을 통째로 복사하므로 포함됨). 반대로 런타임을 더 줄이려고 /usr/local 일부만 복사하면 setuptools가 빠질 수 있습니다.
5) 자주 나오는 케이스별 처방전
5.1 ModuleNotFoundError: No module named 'pkg_resources'
- 범인 패키지/코드가
pkg_resources를 import - 해결 우선순위
- 코드에서
importlib.metadata로 교체 - 문제 패키지 업데이트
- 불가피하면
setuptools런타임 설치
- 코드에서
5.2 pkg_resources.DistributionNotFound 또는 버전 조회 실패
이 경우는 pkg_resources 자체가 있고 없고의 문제가 아니라, 패키지 메타데이터가 환경에 없거나 꼬인 상태일 수 있습니다.
- 가상환경 재생성 권장
rm -rf .venv
python -m venv .venv
source .venv/bin/activate
python -m pip install -U pip
pip install -r requirements.txt
- 컨테이너라면 이미지 레이어 캐시로 인해 의존성이 꼬였는지 확인
5.3 “로컬은 되는데 CI/서버만 실패”
대부분 CI/서버가 더 슬림한 환경이라 setuptools가 없거나, lockfile이 다르게 해석된 경우입니다.
python -m pip freeze결과 비교pip config list확인(사내 미러/인덱스 차이)python -c "import sys; print(sys.executable); import site; print(site.getsitepackages())"로 실제 실행 파이썬 경로 확인
비슷한 맥락으로 런타임 환경 차이에서 생기는 문제는 비동기 경고/네트워크 오류와도 닮았습니다. 운영에서 원인 분리를 위한 체크리스트가 필요하다면 Python asyncio Task was destroyed but it is pending 경고 원인 5가지와 완벽 해결법처럼 “재현 조건/환경 차이”를 먼저 고정하는 접근이 효과적입니다.
6) 재발 방지: 패키징 표준에 맞춘 프로젝트 정리
6.1 pyproject.toml에 빌드 시스템 명시
레거시 setup.py만 있는 프로젝트는 빌드 과정에서 setuptools에 암묵적으로 의존하기 쉽습니다. 최소한의 pyproject.toml을 두어 빌드 백엔드를 명확히 합니다.
[build-system]
requires = ["setuptools>=68", "wheel"]
build-backend = "setuptools.build_meta"
이렇게 하면 “빌드 단계에서 setuptools가 필요하다”는 사실이 선언되고, CI에서도 일관되게 동작합니다.
6.2 런타임 의존성과 빌드 의존성 분리
- 런타임에서
pkg_resources가 필요 없다면: 코드에서 제거 - 필요하다면:
requirements.txt에 setuptools 포함(명시)
6.3 최소 지원 버전 정책을 문서화
Python 3.12로 올리면서 생기는 문제는 대개 “숨은 레거시 의존”이 원인입니다. 다음을 문서화하면 팀 단위로 재발이 줄어듭니다.
- 지원 Python 버전 범위
- 빌드 도구 버전(pip/setuptools/wheel)
- lockfile(또는 constraints) 운영 방식
네트워크/인증서 같은 환경 이슈가 얽히면 증상이 더 복잡해질 수 있는데, 그때는 별도 원인 분리가 필요합니다. 예를 들어 TLS 문제로 설치가 실패해 setuptools가 누락되는 경우도 있으니, 설치 단계에서 CERTIFICATE_VERIFY_FAILED가 있었다면 Python SSL CERTIFICATE_VERIFY_FAILED 10분 해결도 함께 확인해보는 것이 좋습니다.
7) 결론: “setuptools 설치”는 마지막 카드로 두자
Python 3.12에서의 pkg_resources 에러는 단순히 모듈 하나가 없어서가 아니라, 레거시 패키징 관성이 업그레이드 시점에 드러난 경우가 대부분입니다. 가장 안정적인 근본 해결은 다음 순서입니다.
- 우리 코드에서
pkg_resources제거 →importlib.metadata로 교체 - 범인 라이브러리 업데이트/대체
- 불가피한 경우에만 setuptools를 런타임 의존성으로 명시
- 빌드/런타임 분리 환경에서는 이미지 구성으로 setuptools 포함 여부를 의도적으로 통제
이렇게 정리하면 Python 3.12뿐 아니라 이후 버전에서도 패키징 관련 장애를 크게 줄일 수 있습니다.