본문으로 바로가기
Lab

파이썬(virtual env) 가상환경 만들기, venv와 uv로 직접 해 보니

파이썬 virtual env를 venv와 uv로 만들고, 켜고, 끄고, 지워 봤습니다. 맥 한 대에서 한 번 실행한 결과 venv는 1.1초, uv는 0.1초 걸렸습니다. 켜기 전후에 실행되는 파이썬 위치가 어떻게 바뀌는지, 가상환경인지 확인하는 방법까지 함께 정리했습니다.

·

프로젝트 폴더 안에 자기만의 파이썬과 패키지를 담은 투명한 상자가 시스템 패키지 선반과 분리돼 있는 개념 그림
파이썬(virtual env) 가상환경 만들기, venv와 uv로 직접 해 보니 표지 삽화

맥 한 대에서 python3 -m venv .venv로 파이썬 가상환경을 만드는 데는 1.1초, uv venv로 만드는 데는 0.1초가 걸렸습니다. 켜기 전에 /opt/homebrew/bin/python3를 가리키던 명령은 켠 뒤 ./.venv/bin/python으로 바뀌었고, 끄자 원래대로 돌아왔죠. virtual env를 처음 만들거나, AI 코딩 도구가 패키지를 어디에 설치하는지 헷갈렸다면 아래 8단계를 그대로 따라 해 보세요.

따라 할 순서 한눈에 보기

  1. 쓸 파이썬 버전을 확인합니다.
  2. 프로젝트 폴더에 .venv 가상환경을 만들고 pyvenv.cfg를 열어 봅니다.
  3. 가상환경을 켜고, 켜기 전후의 python 위치와 VIRTUAL_ENV 값을 비교합니다.
  4. 가상환경 안의 pip과 설치된 패키지 목록을 확인합니다.
  5. 지금 실행 중인 파이썬이 가상환경인지 코드로 확인합니다.
  6. deactivate로 끄고 원래 파이썬으로 돌아왔는지 봅니다.
  7. 같은 일을 uv venv로 해 봅니다.
  8. 다 쓴 가상환경은 폴더째 지웁니다.
파이썬 가상환경 만들고 켜고 끄기 순서를 단계별로 그린 도식
이번에 따라 한 순서. 켜면 python과 pip가 .venv 안의 것으로 바뀌었고, 끄면 원래대로 돌아왔다
도식출처: 직접 실행한 순서

단계로 들어가기 전에, 가상환경을 왜 따로 만드는지부터 짧게 짚고 넘어가겠습니다.

함께 읽을 글

파이썬 가상환경은 왜 쓰나요?

Python 공식 문서 venv에 따르면 가상환경은 이미 설치된 파이썬(기본 파이썬, base) 위에 만들어집니다. 기본적으로 그 기본 환경의 패키지와는 분리돼서, 가상환경에 직접 설치한 패키지만 보이죠. 그래서 가상환경 안에서 pip을 쓰면 따로 지정하지 않아도 패키지가 가상환경에 설치됩니다.

문서가 꼽는 성질은 AI 에이전트에게 작업을 맡길 때 그대로 규칙으로 쓸 수 있습니다.

  • 보통 프로젝트 폴더 안의 .venv나 venv라는 폴더에 둡니다.
  • Git 같은 버전 관리 시스템에 올리지 않습니다.
  • 언제든 지우고 처음부터 다시 만드는 일회용으로 보고, 프로젝트 코드는 그 안에 두지 않습니다.
  • 다른 곳으로 옮기거나 복사하지 않고, 새 위치에서 다시 만듭니다.

그럼 도구는 무엇을 써야 할까요? venv는 파이썬 3.3부터 표준 라이브러리에 들어 있고, 3.5부터는 가상환경을 만들 때 venv를 쓰도록 권합니다. 예전 도구인 pyvenv는 3.6에서 폐기 예고된 뒤 3.8에서 제거됐어요.

uv 문서는 파이썬 설치 자체의 환경을 고치지 않는 것을 모범 사례로 꼽습니다. 그리고 pip과 달리 uv는 기본적으로 가상환경을 쓰도록 요구한다고 설명하죠. virtualenv는 이 글의 근거 문서가 따로 다루지 않아서 여기서는 비교하지 않습니다.

가상환경 만드는 방법

먼저 어떤 파이썬 위에 만들지 확인합니다. 이번 측정에서는 Homebrew로 설치된 python3가 3.15.0이었습니다.

$ python3 --version
Python 3.15.0

이제 현재 폴더에 .venv를 만들고, 생긴 파일과 설정 파일을 바로 열어 봅니다. 이 단계에 1.1초가 걸렸습니다.

$ python3 -m venv .venv && ls .venv && cat .venv/pyvenv.cfg
bin
include
lib
pyvenv.cfg
home = /opt/homebrew/opt/python@3.15/bin
include-system-site-packages = false
version = 3.15.0
executable = /opt/homebrew/Cellar/python@3.15/3.15.0_1/Frameworks/Python.framework/Versions/3.15/bin/python3.15
command = /opt/homebrew/opt/python@3.15/bin/python3.15 -m venv ./.venv

문서 설명대로 bin에는 파이썬 실행 파일의 복사본이나 심볼릭 링크가, lib 아래에는 패키지가 들어갈 site-packages가 생깁니다. pyvenv.cfg의 home은 명령을 실행한 파이썬 설치 위치를 가리키죠. include-system-site-packages는 --system-site-packages 옵션 없이 만들었기 때문에 false이고, 시스템 쪽 패키지에는 접근하지 않는다는 뜻입니다.

참고로 파이썬 3.13부터 venv는 Git용 .gitignore 파일도 기본으로 만들어 줍니다. 위 ls 출력에는 나오지 않았으니, 직접 확인하려면 숨김 파일까지 보이게 목록을 띄워 보세요.

가상환경을 켜면 무엇이 바뀌나요?

켜기 전과 켠 뒤에 python이 어디를 가리키는지 한 줄로 비교해 봤습니다.

$ echo "켜기 전: $(which python3)" && source .venv/bin/activate && echo "켠 뒤: $(which python)" && python --version && echo "VIRTUAL_ENV=$VIRTUAL_ENV"
켜기 전: /opt/homebrew/bin/python3
켠 뒤: ./.venv/bin/python
Python 3.15.0
VIRTUAL_ENV=./.venv

켜는 순간 python이 .venv 안의 실행 파일로 바뀌고, VIRTUAL_ENV에는 가상환경 경로가 들어갔습니다. 문서에 따르면 켜기는 가상환경의 bin 폴더를 PATH 맨 앞에 붙이는 일입니다. 그래서 python만 입력해도 가상환경의 인터프리터가 실행되는 거예요.

명령을 찾는 경로에서 가상환경의 bin 폴더가 맨 앞에 있고, 시스템 파이썬 폴더는 그 뒤에 있는 개념 그림
가상환경을 켜면 bin 폴더가 PATH 앞에 붙어, python을 찾을 때 가상환경 쪽이 먼저 걸리는 관계를 그린 개념 그림입니다.

그런데 주의할 점이 있습니다. 이 측정은 단계마다 새 셸에서 실행해서, 켠 상태가 다음 단계로 이어지지 않았어요. 그래서 4·5단계는 .venv/bin/pip, .venv/bin/python처럼 전체 경로로 불렀습니다.

평소처럼 터미널 창 하나에서 작업한다면 사정이 다릅니다. 켜기는 그 셸의 PATH를 바꾸는 일이라서, 같은 창에서 deactivate로 끌 때까지 유지되죠. 셸별로 켜는 명령은 문서에 이렇게 정리돼 있습니다.

플랫폼셸켜는 명령
POSIXbash/zshsource <venv>/bin/activate
POSIXfishsource <venv>/bin/activate.fish
POSIXcsh/tcshsource <venv>/bin/activate.csh
윈도우cmd.exe<venv>\Scripts\activate.bat
윈도우PowerShell<venv>\Scripts\Activate.ps1

AI 코딩 도구가 명령마다 새 셸을 띄우는 방식이라면, 켠 상태가 이어진다고 가정하지 않는 편이 좋습니다. 문서도 켜지 않고 그 환경 파이썬의 전체 경로만 지정해도 된다고 설명하니, 에이전트에게 .venv/bin/python처럼 경로를 적어 부르도록 지시해 보세요.

가상환경 안에는 무엇이 설치돼 있나요?

막 만든 가상환경에 무엇이 들어 있는지 볼 차례입니다. 이 단계는 0.5초 걸렸습니다.

$ .venv/bin/pip --version && .venv/bin/pip list --disable-pip-version-check
pip 26.2.2 from ./.venv/lib/python3.15/site-packages/pip (python 3.15)
Package Version
------- -------
pip     26.2.2

pip은 가상환경 안의 site-packages에서 실행됐고, 패키지는 pip 하나뿐이었습니다. 문서에 따르면 --without-pip 옵션을 주지 않는 한 venv가 ensurepip로 pip을 넣어 줍니다. 그러니 에이전트가 코드를 돌리다 모듈을 못 찾는다고 하면, 먼저 이 목록에 그 패키지가 있는지부터 확인하는 편이 빠릅니다.

지금 가상환경인지 코드로 확인하는 법

그렇다면 실행 중인 코드가 가상환경 안에서 도는지는 어떻게 알 수 있을까요? 문서는 sys.prefix와 sys.base_prefix가 다른지만 보면 충분하다고 말합니다.

$ .venv/bin/python -c 'import sys; print(sys.prefix); print(sys.base_prefix); print("가상환경:", sys.prefix != sys.base_prefix)'
./.venv
/opt/homebrew/opt/python@3.15/Frameworks/Python.framework/Versions/3.15
가상환경: True

sys.prefix는 가상환경 폴더를, sys.base_prefix는 가상환경을 만든 기본 파이썬을 가리켰습니다. 반대로 VIRTUAL_ENV 값으로는 판단할 수 없다고 문서는 분명히 적고 있어요. 켜지 않고 전체 경로로 실행해도 가상환경은 쓰이기 때문이죠.

which python, VIRTUAL_ENV, sys.prefix 비교 세 방법의 측정값과 판단 가능 여부를 비교한 표
문서 기준으로 가상환경 여부를 판단할 수 있는 방법은 sys.prefix 비교입니다.
도식출처: 근거를 바탕으로 코드가 그린 도식

가상환경을 끄는 방법

끌 때는 같은 셸에서 deactivate를 입력합니다. 이번에도 새 셸이라 한 줄 안에서 켰다가 바로 껐습니다.

$ source .venv/bin/activate && deactivate && echo "끈 뒤: $(which python3)" && echo "VIRTUAL_ENV=${VIRTUAL_ENV:-없음}"
끈 뒤: /opt/homebrew/bin/python3
VIRTUAL_ENV=없음

python3는 다시 Homebrew 쪽을 가리켰고, VIRTUAL_ENV는 비었습니다. 끄는 정확한 방식은 플랫폼마다 다른 내부 구현이라고 문서는 설명합니다.

uv로 만들 때 달라지는 점

이번에는 같은 일을 uv로 해 봤습니다. 캐시를 임시 폴더에 두려고 --cache-dir를 붙였고, 0.1초가 걸렸습니다.

$ uv venv uv-env --cache-dir ./uv-cache && uv-env/bin/python --version && ls uv-env/bin | head -8
Using CPython 3.12.14
Creating virtual environment at: uv-env
Activate with: source uv-env/bin/activate
Python 3.12.14
activate
activate.bat
activate.csh
activate.fish
activate.nu
activate.ps1
activate.xsh
activate_this.py

눈여겨볼 점은 파이썬 버전입니다. venv는 3.15.0 위에 만들어졌지만, uv는 CPython 3.12.14를 골랐어요. 왜 이 버전을 골랐는지는 이 기록만으로는 알 수 없습니다. 버전을 정하고 싶다면 문서대로 직접 요청하면 됩니다. 요청한 버전이 시스템에 없으면 uv가 내려받습니다.

$ uv venv --python 3.11

이름 없이 uv venv만 실행하면 .venv에 만들어지고, 이 기본 이름을 쓰면 이후 명령이 알아서 그 가상환경을 찾아 씁니다.

$ uv venv

$ # Install a package in the new virtual environment
$ uv pip install ruff

uv가 가상환경을 찾는 순서는 이렇습니다. 에이전트가 엉뚱한 곳에 패키지를 설치했다면 이 순서부터 따져 보세요.

  1. VIRTUAL_ENV 환경 변수로 켜진 가상환경
  2. CONDA_PREFIX로 켜진 Conda 환경
  3. 현재 폴더 또는 가장 가까운 상위 폴더의 .venv

그렇다면 uv가 venv보다 빠르다고 봐도 될까요? 이번 결과는 1.1초와 0.1초지만 한 번씩만 실행했고, 쓴 파이썬 버전도 달랐습니다. 이 측정만으로는 원인을 가를 수 없으니, 같은 파이썬 버전을 지정해 여러 번 돌려 보며 직접 비교해 보세요.

가상환경 지우는 방법

다 쓴 가상환경은 폴더를 지우면 끝입니다. 캐시 폴더까지 함께 지우는 데 0.1초가 걸렸고, 폴더에는 아무것도 남지 않았습니다.

$ rm -rf .venv uv-env uv-cache && ls -a
.
..

문서는 가상환경을 옮길 수 없는 것으로 봅니다. 설치된 스크립트의 첫 줄(shebang)에 그 환경 인터프리터의 절대 경로가 들어가기 때문이죠. 그래서 상위 폴더를 옮겼다면 새 위치에서 다시 만들어야 합니다. 쉽게 다시 만들 수 있도록 requirements.txt를 두고, 그 환경의 pip으로 pip install -r requirements.txt를 실행하면 됩니다.

자주 하는 실수

  • 켠 상태가 다음 명령에도 이어진다고 가정합니다. 명령마다 새 셸이 뜨면 켜기는 이어지지 않습니다. 이럴 때는 .venv/bin/python처럼 전체 경로로 부르세요.
  • VIRTUAL_ENV로 가상환경 여부를 판단합니다. 문서는 sys.prefix != sys.base_prefix를 보라고 합니다.
  • 가상환경 폴더를 옮기거나 복사합니다. 옮긴 곳에서 다시 만드는 것이 문서가 권하는 방법입니다.
  • 가상환경을 Git에 올립니다. 버전 관리에 넣지 않는 것이 원칙입니다.
  • 윈도우 PowerShell에서 Activate.ps1이 막힙니다. 문서는 현재 사용자의 실행 정책을 이렇게 바꾸라고 안내합니다.
PS C:\> Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Scope CurrentUser

커밋 전에 확인할 것

  • 가상환경 폴더 이름이 .venv 또는 venv인가요?
  • 가상환경 폴더가 Git 추적 대상에서 빠져 있나요?
  • requirements.txt처럼 가상환경을 다시 만들 수단이 있나요?
  • 코드에서 sys.prefix와 sys.base_prefix가 서로 다르게 나오나요?
  • uv를 쓴다면 의도한 파이썬 버전으로 만들어졌나요?

측정 조건과 한계

측정은 2026-10-27에 맥 한 대(darwin arm64)에서 했습니다. 새 임시 폴더에서 명령을 적힌 순서대로 한 번씩 실행했고, 이 컴퓨터에 Homebrew로 설치돼 있던 python3와 uv를 썼습니다. 단계마다 새 셸에서 실행했고, 모든 단계의 종료 코드는 0이었습니다.

단계걸린 시간
1. 파이썬 버전0.0초
2. 가상환경 만들기1.1초
3. 켜기 전과 켠 뒤0.0초
4. 가상환경 안의 pip0.5초
5. 가상환경인지 코드로 보기0.0초
6. 끄기0.0초
7. uv로 만들기0.1초
8. 지우기0.1초
가상환경 만들기 1.1초, pip 확인 0.5초, uv로 만들기 0.1초, 지우기 0.1초를 나타낸 막대그래프
0.0초로 기록된 단계는 뺐습니다. 한 번만 실행한 값이고, venv와 uv는 서로 다른 파이썬 버전으로 만들어졌습니다.
도식출처: 근거를 바탕으로 코드가 그린 도식

한 번만 실행한 값이라 실행할 때마다 달라질 수 있고, venv와 uv는 서로 다른 파이썬 버전(3.15.0과 3.12.14)으로 만들어졌습니다. 따라서 이 표는 두 도구의 속도를 가르는 기준보다는, 각 단계가 무엇을 출력하는지 확인하는 기록으로 읽어 주세요.

참고 자료