pip install 오류 error: externally-managed-environment 해결 방법 (우분투 24.04·데비안 12·맥 Homebrew)

이 글의 핵심

우분투 24.04나 데비안 12, 맥 Homebrew에서 pip install을 하면 error: externally-managed-environment가 뜨며 설치가 거부됩니다. 고장이 아니라 PEP 668에 따라 운영체제가 관리하는 Python을 보호하는 장치입니다. 이 글은 오류가 나는 원리와 EXTERNALLY-MANAGED 파일을 먼저 설명하고, 라이브러리는 venv, 명령줄 도구는 pipx, 시스템 연동은 apt 패키지라는 해결 순서와 각각의 대가, --break-system-packages를 써도 되는 경우와 안 되는 경우, Docker 이미지·cron·VS Code·sudo pip에서 같은 오류가 다시 나는 이유를 정리합니다.

이 오류가 나는 환경

우분투를 24.04로 새로 설치했거나, 맥에서 Homebrew로 Python을 깔고 나서 예전처럼 pip install requests를 입력하면 설치가 진행되지 않고 긴 오류가 출력됩니다. 예전 블로그 글이나 README에 적힌 명령을 그대로 따라 했는데 첫 줄부터 막히니 당황스럽지만, 이것은 pip가 고장 난 것이 아니라 운영체제가 의도적으로 막아 둔 것입니다.

이 글의 기준 환경은 다음과 같습니다.

  • 우분투 24.04 LTS(기본 Python 3.12), 우분투 23.04 이상 전반
  • 데비안 12 bookworm(기본 Python 3.11)
  • macOS + Homebrew로 설치한 Python(brew install python)

우분투 22.04, 데비안 11 이하에는 이 제한이 없습니다. 그래서 같은 명령이 22.04 서버에서는 되고 24.04 노트북에서는 안 되는 일이 생깁니다. Python 설치, pip 기본 사용법, venv를 활성화하는 방법 자체는 Python 개발 환경 설정에서 다뤘으므로, 여기서는 이 오류가 왜 나고 상황별로 무엇을 골라야 하는지에 집중합니다.

터미널에 출력되는 오류 원문

우분투 24.04에서 pip install requests(또는 pip3 install, python3 -m pip install)를 실행하면 다음과 같이 출력됩니다.

error: externally-managed-environment

× This environment is externally managed
╰─> To install Python packages system-wide, try apt install
    python3-xyz, where xyz is the package you are trying to
    install.

    If you wish to install a non-Debian-packaged Python package,
    create a virtual environment using python3 -m venv path/to/venv.
    Then use path/to/venv/bin/python and path/to/venv/bin/pip. Make
    sure you have python3-full installed.

    If you wish to install a non-Debian packaged Python application,
    it may be easiest to use pipx install xyz, which will manage a
    virtual environment for you. Make sure you have pipx installed.

    See /usr/share/doc/python3.12/README.venv for more information.

note: If you believe this is a mistake, please contact your Python installation or OS distribution provider. You can override this, at the risk of breaking your Python installation or OS, by passing --break-system-packages.
hint: See PEP 668 for the detailed specification.

데비안 12에서는 마지막 경로가 python3.11로 바뀌는 정도의 차이만 있습니다. 맥 Homebrew에서는 같은 첫 줄 뒤에 Homebrew용 안내문이 붙습니다(버전에 따라 문구가 조금씩 다릅니다).

error: externally-managed-environment

× This environment is externally managed
╰─> To install Python packages system-wide, try brew install
    xyz, where xyz is the package you are trying to
    install.

    If you wish to install a Python library that isn't in Homebrew,
    use a virtual environment:

    python3 -m venv path/to/venv
    source path/to/venv/bin/activate
    python3 -m pip install xyz

    If you wish to install a Python application that isn't in Homebrew,
    it may be easiest to use 'pipx install xyz', which will manage a
    virtual environment for you. You can install pipx with

    brew install pipx

오류문을 끝까지 읽어 보면 해결책이 이미 들어 있습니다. 라이브러리는 가상환경에, 애플리케이션(명령줄 도구)은 pipx로, 시스템 전체에 필요하면 apt나 brew로 설치하라는 것입니다. 이 글의 해결 순서도 이 안내를 따릅니다. 다만 각 방법이 언제 맞고 어떤 대가가 있는지는 오류문에 나오지 않으므로 그 부분을 채워 보겠습니다.

PEP 668과 EXTERNALLY-MANAGED 파일

pip가 설치를 거부하는 조건

PEP 668은 “이 Python은 다른 패키지 관리자가 관리한다”는 표시를 남기는 방법을 정한 규격입니다. 표시 방법은 단순해서, Python 표준 라이브러리 디렉터리에 EXTERNALLY-MANAGED라는 파일을 하나 두는 것이 전부입니다. pip 23.0 이상은 설치를 시작하기 전에 두 가지를 확인합니다.

  1. 지금 실행 중인 Python의 표준 라이브러리 디렉터리에 EXTERNALLY-MANAGED 파일이 있는가
  2. 지금 가상환경 안에서 실행 중인가

파일이 있고 가상환경 밖이면 거부합니다. 가상환경 안이라면 파일이 있어도 설치를 허용합니다. 가상환경은 시스템 Python을 바탕으로 만들지만 패키지는 별도 디렉터리에 설치되므로 운영체제 쪽을 건드릴 일이 없기 때문입니다.

파일 위치는 직접 확인할 수 있습니다.

python3 -c "import sysconfig; print(sysconfig.get_path('stdlib'))"
# 우분투 24.04: /usr/lib/python3.12

cat /usr/lib/python3.12/EXTERNALLY-MANAGED

내용은 INI 형식이고, [externally-managed] 섹션의 Error 값이 앞에서 본 오류 안내문입니다. 즉 오류문은 pip가 만든 것이 아니라 배포판이 이 파일에 적어 둔 문장입니다. 우분투와 Homebrew의 문구가 서로 다른 이유도 여기에 있습니다.

지금 셸의 Python이 가상환경인지 헷갈릴 때는 다음 한 줄로 확인합니다. True가 나오면 가상환경 안입니다.

python3 -c "import sys; print(sys.prefix != sys.base_prefix, sys.prefix)"

배포판이 이 제한을 넣은 이유

우분투와 데비안에는 Python으로 작성된 시스템 도구가 많습니다. apt의 일부 기능, add-apt-repository, 네트워크·업데이트 관련 유틸리티, 데스크톱의 여러 구성 요소가 /usr/bin/python3와 apt로 설치된 Python 패키지(/usr/lib/python3/dist-packages)에 의존합니다. 이 패키지들은 배포판이 서로 맞는 버전끼리 묶어서 테스트한 조합입니다.

예전에는 sudo pip install로 같은 Python에 아무 패키지나 넣을 수 있었습니다. 예를 들어 어떤 프로젝트 때문에 sudo pip install --upgrade requests urllib3를 하면, 시스템 도구가 기대하던 버전과 다른 urllib3가 먼저 import되어 운영체제 도구가 엉뚱한 곳에서 ImportError나 AttributeError를 내며 멈추는 일이 생겼습니다. 이렇게 망가진 시스템은 원인을 찾기도 어렵습니다. 오류가 pip를 실행한 시점이 아니라 몇 주 뒤 전혀 상관없는 도구를 실행할 때 나타나기 때문입니다. 반대로 apt가 패키지를 업데이트하면서 pip로 넣어 둔 것을 덮어써 프로젝트가 깨지는 일도 있었습니다. 두 관리자가 한 디렉터리를 놓고 서로 모르는 상태로 파일을 쓰는 구조 자체가 문제였습니다.

PEP 668은 “시스템 Python은 배포판 패키지 관리자만 건드리고, 사용자의 패키지는 가상환경에 넣는다”는 경계를 pip 수준에서 강제합니다. Homebrew도 같은 이유입니다. brew upgrade가 Python을 교체하거나 Homebrew 포뮬러가 쓰는 Python 패키지를 관리하는데, 사용자가 pip로 섞어 넣으면 업그레이드 때 충돌이 납니다.

pip install —user도 막히는 이유

예전에는 sudo 없이 pip install --user를 하면 홈 디렉터리(~/.local/lib/python3.x/site-packages)에 설치되어 시스템을 건드리지 않는다고 여겨졌습니다. 그러나 이 디렉터리는 시스템 Python이 import 경로에 자동으로 추가하는 곳입니다. 같은 사용자 계정으로 실행되는 시스템 도구(예: 데스크톱 세션에서 도는 Python 기반 프로그램)도 이 경로의 패키지를 먼저 불러오므로, 버전 충돌 문제가 그대로 남습니다. 그래서 PEP 668은 --user 설치도 거부하도록 정했고, 실제로 우분투 24.04에서 pip install --user requests를 해도 같은 externally-managed-environment 오류가 납니다. 예전 글에서 “--user를 붙이면 된다”는 해결책을 보았다면, 지금은 통하지 않는 방법입니다.

상황별 해결 순서

어떤 방법을 골라야 할지는 “무엇을 설치하려는가”로 갈립니다.

flowchart TD
    A["pip install 이 externally-managed-environment 로 실패"] --> B{"무엇을 설치하려는가?"}
    B -->|"프로젝트에서 import 할 라이브러리"| C["프로젝트 폴더에 python3 -m venv .venv"]
    B -->|"터미널에서 실행할 명령줄 도구"| D["pipx install 도구이름"]
    B -->|"ROS 2 등 apt 패키지와 함께 쓸 모듈"| E["sudo apt install python3-모듈이름"]
    C --> F{"apt 모듈도 같이 import 해야 하는가?"}
    F -->|"예"| G["venv 를 --system-site-packages 로 생성"]
    F -->|"아니오"| H["venv 안에서 pip install"]

프로젝트 라이브러리는 venv에

가장 먼저 고려할 방법입니다. 프로젝트 폴더에서 가상환경을 만들고 그 안에 설치합니다.

cd ~/myproject
python3 -m venv .venv
source .venv/bin/activate
pip install requests

활성화한 뒤에는 pip가 .venv/bin/pip를 가리키므로 오류가 나지 않습니다. 대가는 프로젝트마다 패키지가 중복 설치되어 디스크를 조금 더 쓰고, 새 터미널을 열 때마다 활성화해야 한다는 점입니다. 그 대신 프로젝트끼리 버전이 섞이지 않고, 문제가 생기면 .venv 폴더를 지우고 다시 만들면 끝나므로 시스템을 망가뜨릴 위험이 없습니다.

venv 생성이 ensurepip 오류로 실패할 때

우분투와 데비안에서 처음 venv를 만들 때 자주 만나는 두 번째 오류입니다.

The virtual environment was not successfully created because ensurepip is not
available.  On Debian/Ubuntu systems, you need to install the python3-venv
package using the following command.

    apt install python3.12-venv

You may need to use sudo with that command.  After installing the python3-venv
package, recreate your virtual environment.

Failing command: /home/user/myproject/.venv/bin/python3

데비안 계열은 Python 표준 라이브러리 중 venv와 ensurepip가 쓰는 부분을 별도 패키지로 떼어 놓았기 때문에, 기본 설치 상태에서는 venv 안에 pip를 넣지 못합니다. 다음처럼 설치하면 됩니다.

sudo apt install python3-venv
# 또는 venv, 개발용 헤더, IDLE 등을 한꺼번에
sudo apt install python3-full

python3-venv는 배포판 기본 Python 버전에 맞는 패키지(24.04에서는 python3.12-venv)를 끌어옵니다. 여기서 놓치기 쉬운 점은, 실패한 명령이 .venv 폴더를 반쯤 만들어 놓고 끝난다는 것입니다. 폴더가 있으니 source .venv/bin/activate는 되는 것처럼 보이지만 그 안에 pip가 없어서, pip를 입력하면 다시 시스템 pip가 실행되어 같은 externally-managed-environment 오류가 납니다. 패키지를 설치한 다음에는 반드시 rm -rf .venv로 지우고 다시 만드십시오.

참고로 venv 안에서 쓸 pip는 venv가 따로 넣어 주므로, venv만 쓸 계획이라면 시스템에 python3-pip를 설치하지 않아도 됩니다.

명령줄 도구는 pipx로

black, httpie, yt-dlp, poetry처럼 특정 프로젝트에서 import하는 것이 아니라 터미널 어디서든 명령으로 실행하고 싶은 도구는 pipx가 맞습니다. pipx는 도구마다 전용 가상환경을 만들고, 실행 파일만 ~/.local/bin에 연결해 줍니다.

# 우분투/데비안
sudo apt install pipx
pipx ensurepath   # ~/.local/bin 을 PATH에 추가 (새 터미널부터 적용)

# macOS
brew install pipx
pipx ensurepath

pipx install httpie
http --version

도구 하나를 venv로 직접 관리해도 되지만, 그러면 매번 그 venv를 활성화하거나 실행 파일 경로를 직접 연결해야 합니다. pipx는 이 반복 작업을 대신해 주는 도구입니다. 대가는 도구가 각자 독립된 환경에 있다는 점에서 생깁니다. 예를 들어 pipx로 설치한 black은 프로젝트 venv에 설치된 패키지를 보지 못합니다. 대부분의 포매터나 다운로더에는 문제가 없지만, 플러그인을 추가해야 하는 도구라면 pipx inject 도구이름 플러그인이름으로 그 도구의 환경에 넣어야 합니다. 프로젝트 코드를 import해서 동작하는 도구(pytest, mypy 등)는 pipx보다 프로젝트 venv에 설치하는 편이 자연스럽습니다.

설치 후 command not found가 나면 대부분 pipx ensurepath 후 터미널을 새로 열지 않은 경우입니다.

시스템과 같이 써야 하면 apt의 python3-xxx

오류문 첫 번째 안내대로 sudo apt install python3-requests, python3-numpy, python3-yaml처럼 배포판이 패키징한 모듈을 설치하는 방법도 있습니다. apt가 관리하므로 시스템 Python에서 바로 import되고, 보안 업데이트도 apt upgrade로 같이 받습니다.

대가는 버전입니다. 배포판 출시 시점의 버전으로 고정되어, 우분투 24.04 LTS가 지원되는 동안 새 기능이 담긴 메이저 버전으로 올라가지 않습니다. 패키지 이름도 PyPI 이름과 항상 같지는 않아서 apt search python3- 로 찾아봐야 하고, PyPI에는 있지만 apt에는 없는 패키지도 많습니다. 그래서 이 방법은 “시스템의 다른 부분과 함께 동작해야 하는 경우”에 맞습니다. ROS 2처럼 apt로 설치되는 프레임워크가 python3-* 패키지에 의존하는 경우가 대표적입니다. ROS 2 Jazzy 설치 환경에서 추가 모듈이 필요하면 먼저 apt에 python3- 이름으로 있는지 찾아보는 것이 충돌이 가장 적습니다.

apt 모듈과 PyPI 패키지를 둘 다 써야 할 때는 둘을 섞는 venv를 만들 수 있습니다.

python3 -m venv --system-site-packages .venv
source .venv/bin/activate
pip install 필요한패키지

--system-site-packages를 주면 venv가 시스템의 apt 패키지를 import할 수 있고, 새로 설치하는 패키지는 venv 안에만 들어갑니다. 같은 패키지를 venv에 다른 버전으로 설치하면 venv 쪽이 우선합니다. rclpy, python3-gi(GTK 바인딩)처럼 PyPI에서 설치하기 어려운 모듈을 쓰는 프로젝트에 유용하지만, 시스템 패키지 버전이 바뀌면 venv의 동작도 따라 바뀐다는 점은 기억해야 합니다.

uv를 쓰는 경우

uv는 venv 생성과 패키지 설치, 명령줄 도구 설치를 한 도구로 처리합니다. 이미 uv를 쓰고 있다면 같은 원칙을 uv 명령으로 따르면 됩니다.

uv venv                 # 현재 폴더에 .venv 생성
source .venv/bin/activate
uv pip install requests

uv tool install ruff    # pipx install 과 같은 역할

uv도 PEP 668을 따르므로, uv pip install --system으로 시스템 Python에 설치하려 하면 같은 이유로 거부합니다. 즉 uv는 이 제한을 우회하는 도구가 아니라 venv와 pipx 방식을 더 빠르게 처리하는 대안입니다. 이 오류 하나 때문에 도구를 바꿀 필요는 없고, pip·uv·Poetry 중 무엇을 고를지는 pip·uv·Poetry 비교의 기준으로 판단하면 됩니다.

—break-system-packages를 써도 되는 경우

이 옵션이 실제로 하는 일

오류문의 마지막 줄이 안내하는 --break-system-packages는 마커 파일 검사를 건너뛰고 예전처럼 설치하게 하는 옵션입니다. 같은 효과를 내는 방법이 세 가지 있습니다.

pip install --break-system-packages requests

# 환경 변수
PIP_BREAK_SYSTEM_PACKAGES=1 pip install requests

# pip 설정 파일(~/.config/pip/pip.conf)에 영구 적용
# [global]
# break-system-packages = true

이름이 과장된 것처럼 보이지만 실제 위험을 정확히 말하고 있습니다. 데비안 계열의 pip는 sudo로 실행하면 /usr/local/lib/python3.12/dist-packages에, sudo 없이 실행하면 ~/.local/lib/python3.12/site-packages에 설치합니다. apt의 파일(/usr/lib/python3/dist-packages)을 직접 덮어쓰지는 않지만, 두 경로 모두 import 순서에서 apt 경로보다 앞에 있습니다. 그래서 같은 이름의 패키지를 다른 버전으로 넣으면 시스템 도구가 그 버전을 불러오게 되고, 앞에서 설명한 충돌이 그대로 재현됩니다. 덧붙여 apt로 설치된 패키지를 pip로 업그레이드하려 하면 다음과 비슷한 오류로 실패하기도 합니다(pip 버전에 따라 문구가 조금 다릅니다).

error: uninstall-no-record-file

× Cannot uninstall packaging 24.0
╰─> The package's contents are unknown: no RECORD file was found for packaging.

hint: The package was installed by debian. You should check if it can uninstall the package.

pip는 apt가 설치한 패키지의 파일 목록을 모르기 때문에 지울 수 없다는 뜻입니다. 이런 오류가 나오면 그 패키지는 시스템 도구가 쓰고 있을 가능성이 높으니, 옵션을 더 붙여 밀어붙이지 말고 venv로 돌아가는 편이 안전합니다.

허용할 만한 경우와 피해야 할 경우

이 옵션이 합리적인 상황은 “망가져도 버리고 다시 만들면 되는 환경”입니다.

  • 한 가지 애플리케이션만 돌리는 Docker 컨테이너, CI 러너처럼 실행할 때마다 새로 만들어지는 환경
  • 그 안에서 apt로 설치한 Python 도구가 거의 없거나, 망가져도 이미지 빌드 단계에서 드러나는 경우

반대로 개발자 노트북, 여러 서비스가 함께 도는 서버, 데스크톱 우분투에서는 쓰지 않는 것이 좋습니다. 특히 pip 설정 파일에 break-system-packages = true를 영구 설정하면, 나중에 무심코 실행한 sudo pip install까지 모두 통과하므로 보호 장치를 통째로 꺼 버리는 셈입니다. 컨테이너에서도 뒤에서 설명할 venv 방식이 크게 번거롭지 않으므로, 이 옵션은 기존 Dockerfile을 급히 살려야 할 때의 임시방편 정도로 보는 편이 좋습니다.

한 가지 함정이 더 있습니다. 이 옵션은 pip 23.0.1부터 존재합니다. 우분투 22.04의 apt pip(22.0.x)에 이 옵션을 넘기면 no such option: --break-system-packages로 실패합니다. 여러 버전의 베이스 이미지를 같은 스크립트로 빌드한다면 옵션 대신 PIP_BREAK_SYSTEM_PACKAGES=1 환경 변수를 쓰는 편이 안전합니다. 예전 pip는 모르는 환경 변수를 그냥 무시하기 때문입니다.

EXTERNALLY-MANAGED 파일 삭제는 권하지 않는다

검색하다 보면 sudo rm /usr/lib/python3.12/EXTERNALLY-MANAGED로 해결하라는 글도 보입니다. 파일을 지우면 실제로 오류는 사라지지만 --break-system-packages를 영구 적용한 것과 같은 결과가 되고, 그 사실이 어디에도 기록되지 않는다는 점에서 더 나쁩니다. 몇 달 뒤 시스템 도구가 이상하게 동작할 때 원인이 이 삭제라는 것을 떠올리기 어렵습니다. 또 이 파일은 배포판의 Python 패키지에 포함된 파일이라, Python 패키지가 업데이트되면 다시 생길 수 있습니다. 그러면 잘 되던 스크립트가 어느 날 갑자기 같은 오류로 멈춥니다. 해결이 아니라 문제를 뒤로 미루는 방법입니다.

Docker 이미지에서 이 오류가 나는 경우

공식 python 이미지와 ubuntu 베이스 이미지의 차이

FROM python:3.12, python:3.12-slim 같은 Docker 공식 Python 이미지에서는 이 오류가 나지 않습니다. 이 이미지들은 Python을 소스에서 빌드해 /usr/local에 설치하고, 데비안의 apt Python과는 별개라서 EXTERNALLY-MANAGED 파일이 없습니다. root로 pip를 실행하면 WARNING: Running pip as the 'root' user can result in broken permissions... 경고는 출력되지만 설치는 됩니다.

반면 FROM ubuntu:24.04나 FROM debian:bookworm에서 apt install python3-pip 후 pip install을 하면 데스크톱과 똑같이 실패합니다. 우분투 22.04 기반 Dockerfile의 베이스 태그만 24.04로 올렸을 때 빌드가 깨지는 가장 흔한 원인입니다.

Dockerfile에서 venv를 쓰는 방법

ubuntu나 debian 베이스를 유지해야 한다면(CUDA 이미지, ROS 이미지 등 상위 이미지가 우분투 기반인 경우가 많습니다) venv를 만들고 PATH 앞에 두는 방법이 가장 깔끔합니다.

FROM ubuntu:24.04

RUN apt-get update \
 && apt-get install -y --no-install-recommends python3 python3-venv \
 && rm -rf /var/lib/apt/lists/*

RUN python3 -m venv /opt/venv
ENV PATH="/opt/venv/bin:$PATH"

COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt

COPY . /app
WORKDIR /app
CMD ["python", "main.py"]

ENV PATH가 핵심입니다. Dockerfile의 각 RUN은 새 셸에서 실행되므로 source /opt/venv/bin/activate는 다음 줄까지 유지되지 않습니다. 반면 PATH를 바꿔 두면 이후의 모든 RUN과 CMD에서 python과 pip가 venv의 것을 가리킵니다. 이 방식은 멀티스테이지 빌드에서 /opt/venv 폴더만 런타임 스테이지로 복사하기에도 좋습니다. 다만 venv는 만든 Python의 경로를 기억하므로, 빌드 스테이지와 런타임 스테이지의 베이스 이미지와 Python 경로가 같아야 합니다.

cron·systemd·스크립트에서 venv Python 지정하기

venv를 만들어 패키지를 설치했는데 cron으로 돌린 스크립트만 ModuleNotFoundError를 내는 경우가 많습니다. cron은 로그인 셸이 아니어서 source .venv/bin/activate를 한 적이 없고, PATH도 최소한으로 설정된 상태라 python3가 /usr/bin/python3, 즉 시스템 Python을 가리키기 때문입니다.

해결책은 활성화가 아니라 venv의 Python을 절대 경로로 지정하는 것입니다. 활성화는 결국 PATH 맨 앞에 .venv/bin을 끼워 넣는 일일 뿐이라서, 그 안의 python을 직접 실행하면 활성화한 것과 같은 패키지를 씁니다.

# crontab -e
*/10 * * * * /home/user/myproject/.venv/bin/python /home/user/myproject/job.py >> /home/user/myproject/job.log 2>&1

systemd 서비스도 마찬가지입니다.

[Service]
WorkingDirectory=/home/user/myproject
ExecStart=/home/user/myproject/.venv/bin/python /home/user/myproject/app.py

스크립트 파일에 실행 권한을 주고 바로 실행하고 싶다면 첫 줄 shebang에 venv Python을 적어도 됩니다.

#!/home/user/myproject/.venv/bin/python
import requests

같은 원리로 pip도 .venv/bin/pip install ...처럼 경로를 주면 활성화 없이 venv에 설치됩니다. 자동화 스크립트에서 “활성화했는지”를 신경 쓰지 않아도 되므로 오히려 더 확실한 방법입니다.

VS Code 터미널과 Jupyter에서 같은 오류가 날 때

venv를 만들었는데 VS Code 통합 터미널에서 pip install을 하면 여전히 오류가 난다면, VS Code가 시스템 Python을 인터프리터로 잡고 있는 경우가 대부분입니다. Ctrl+Shift+P(맥은 Cmd+Shift+P) → Python: Select Interpreter에서 프로젝트의 ./.venv/bin/python을 고르고, 터미널을 새로 열면 자동으로 활성화된 상태로 시작합니다. 상태 표시줄 오른쪽 아래의 Python 버전 옆에 .venv가 보이는지 확인하면 됩니다. 인터프리터 선택 화면 자체는 환경 설정 글의 VS Code 부분에 정리되어 있습니다.

Jupyter 노트북에서 %pip install이나 !pip install이 이 오류를 낸다면 노트북 커널이 시스템 Python이라는 뜻입니다. 노트북 오른쪽 위의 커널 선택에서 venv의 Python을 고르십시오. venv가 목록에 없으면 그 venv 안에 ipykernel을 설치하면 나타납니다.

sudo pip가 문제를 더 키우는 이유

오류가 나니 권한 문제라고 생각해 sudo pip install을 시도하는 경우가 있는데, 이 오류는 권한이 아니라 마커 파일 때문이므로 sudo를 붙여도 똑같이 거부됩니다. 여기에 --break-system-packages까지 붙이면 root 권한으로 /usr/local/lib/python3.x/dist-packages에 설치되어, 그 컴퓨터의 모든 사용자와 root로 실행되는 시스템 도구가 그 패키지를 불러오게 됩니다. 앞서 말한 충돌 범위가 가장 넓어지는 조합입니다.

또 하나 헷갈리는 상황이 있습니다. venv를 활성화한 상태에서 sudo pip install을 하면 오류가 다시 납니다. sudo는 보안을 위해 PATH를 secure_path에 설정된 값으로 바꾸기 때문에, sudo 안에서 실행된 pip는 venv가 아니라 /usr/bin/pip입니다. venv는 사용자 계정 소유의 폴더라 sudo가 필요 없으니, 그냥 pip install을 하면 됩니다. sudo로 venv에 설치하면 일부 파일이 root 소유가 되어 나중에 sudo 없이 설치할 때 Permission denied가 나는 문제도 생깁니다.

어느 pip가 실행되는지 의심스러울 때는 다음으로 확인합니다.

which pip
python3 -m pip --version   # 출력 끝의 (python 3.12)와 경로로 어느 환경인지 확인

pip 명령 대신 python -m pip를 쓰는 습관을 들이면, 지금 고른 Python과 pip가 어긋나는 일이 줄어듭니다.

이미 —break-system-packages로 설치했다면 되돌리는 방법

이 글을 읽기 전에 이미 sudo pip install --break-system-packages나 마커 파일 삭제로 여러 패키지를 설치했다면, 먼저 어떤 패키지가 apt 바깥에 설치되었는지 확인합니다.

ls /usr/local/lib/python3.12/dist-packages/
ls ~/.local/lib/python3.12/site-packages/

여기에 있는 것은 apt가 아니라 pip가 넣은 패키지입니다. 시스템 도구가 이상하게 동작하고 있다면 이 패키지들이 원인일 가능성이 높으므로, 필요한 것은 프로젝트 venv의 requirements.txt에 옮겨 적은 뒤 지웁니다.

# /usr/local 쪽(sudo로 설치한 것)
sudo pip uninstall --break-system-packages 패키지이름

# 홈 디렉터리 쪽(--user로 설치한 것)
pip uninstall --break-system-packages 패키지이름

제거할 때도 마커 파일 검사가 동작하므로 같은 옵션이 필요합니다. 마커 파일을 지웠다면 sudo apt install --reinstall libpython3.12-stdlib처럼 그 파일을 포함한 패키지를 다시 설치해 복구할 수 있습니다. 어느 패키지에 포함되어 있는지는 dpkg -S EXTERNALLY-MANAGED로 확인할 수 있으니, 복구 전에 실제 패키지 이름을 확인하십시오. apt 패키지 중 pip 때문에 꼬인 것이 있다면 sudo apt install --reinstall python3-패키지이름으로 원래 파일을 되돌립니다. 정리가 끝난 뒤 pip install 하나를 시험해서 다시 externally-managed-environment가 나오면, 보호 장치가 정상으로 돌아온 것입니다.