1 개요
VS Code에서 .ipynb 파일을 열고 커널을 선택했을 때, 1 + 1 같은 단순 연산도 실행이 멈추고 커널 인디케이터가 “연결 중” 상태에서 벗어나지 않는 현상이 있다. 환경 점검(venv, ipykernel, pyzmq, kernelspec)을 모두 통과해도 증상이 지속된다면, 실행 중인 Python 프로세스가 두 개 이상 같은 ZMQ 소켓을 잡은 경우이다.
이 포스트는 해당 현상의 원인 분석 과정을 재현하고, 즉각 조치·근본 조치·재발 방지 패턴을 정리한다.
2 현상
VS Code에서 .ipynb 파일을 열고 data-standardization (Python 3.11.9) 커널을 선택했다. 첫 번째 셀은 물론이고 단순 연산도 실행이 멈추고 아무 반응이 없었다. 커널 인디케이터는 “연결 중” 상태에서 멈추거나, 실행 버튼을 눌러도 셀 번호가 바뀌지 않았다.
3 원인 분석 과정
환경이 정상인지 확인하는 표준 체크포인트를 하나씩 검토한다.
3.1 1단계 — venv 유효성 확인
가장 먼저 .venv 자체가 손상된 것이 아닌지 확인한다.
결과는 정상이었다. Python 3.11.9, .venv 경로도 유효(Valid: True).
3.2 2단계 — ipykernel 설치 여부 확인
커널이 응답하지 않는 흔한 원인 중 하나는 ipykernel이 venv에 없는 경우다.
7.2.0 출력. 설치돼 있다.
3.3 3단계 — ZMQ 확인
Jupyter 커널은 ZMQ(ZeroMQ) 소켓으로 VS Code와 통신한다. pyzmq가 없으면 커널이 시작 직후 종료된다.
27.1.0 출력. 이것도 정상이다.
3.4 4단계 — 등록된 kernelspec 확인
data-standardization C:\Users\...\jupyter\kernels\data-standardization
등록돼 있고, kernel.json을 확인하니 .venv\Scripts\python.exe를 정상적으로 가리키고 있었다.
3.5 5단계 — 실행 중인 프로세스 확인 ← 여기서 원인 발견
환경도 정상, 설치도 정상, kernelspec도 정상인데 커널이 동작하지 않는다면 현재 실행 중인 Python 프로세스를 확인해야 한다.
Get-WmiObject Win32_Process |
Where-Object { $_.Name -like "*python*" } |
Select-Object ProcessId, CommandLine출력에서 이상한 점이 보였다.
PID 26484 .venv\Scripts\python.exe -m ipykernel_launcher
--f=kernel-v323f838...json
PID 26096 .pyenv\versions\3.11.9\python.exe -m ipykernel_launcher
--f=kernel-v323f838...json ← 같은 connection file!
두 개의 Python 프로세스가 완전히 동일한 connection file(= 동일한 ZMQ 포트)로 ipykernel_launcher를 실행 중이었다.
4 근본 원인
4.1 ZMQ 소켓 충돌 메커니즘
ZMQ 소켓은 한 프로세스만 바인딩(bind)할 수 있다. 먼저 뜬 프로세스가 포트를 선점하면, 나중에 뜬 프로세스는 바인딩에 실패하거나 충돌이 발생한다. VS Code는 어느 커널에 연결해야 할지 모르는 상태가 되어 메시지를 보내도 응답이 오지 않는다.
VS Code
├── Jupyter 확장 ──→ kernel.json → .venv\python.exe ─┐
│ ├─ 같은 ZMQ 포트 충돌
└── Python 확장 ──→ pyenv\3.11.9\python.exe ─┘
4.2 원인 1 — .vscode/settings.json이 없었다
워크스페이스에 인터프리터가 명시적으로 지정돼 있지 않으면 VS Code의 Python 확장과 Jupyter 확장이 각자 독립적으로 “어떤 Python을 쓸까”를 결정한다.
- Jupyter 확장 → 등록된 kernelspec
data-standardization을 보고.venv\python.exe로 커널 실행 - Python 확장 → pyenv에 설치된
3.11.9를 “Python 3.11.9” 인터프리터로 인식, VS Code UI에서 선택한 “data-standardization (Python 3.11.9)” 항목이 이 인터프리터로도 연결되어 두 번째 커널 프로세스 추가 실행
두 프로세스가 같은 connection file을 공유하며 같은 포트를 두고 경쟁한다.
4.3 원인 2 — pyenv base Python과 .venv Python의 버전이 동일하다
.venv는 pyenv 3.11.9로 만들어진 가상환경이다. VS Code 입장에서 Python 3.11.9 인터프리터가 두 개 보인다.
pyenv\versions\3.11.9\python.exe ← Python 3.11.9 (base)
.venv\Scripts\python.exe ← Python 3.11.9 (venv)
버전이 같으니 UI에서 구분이 어렵고, 각 확장이 자기 기준으로 하나씩 골라 실행한다.
4.4 원인 3 — 두 확장이 같은 connection file을 공유한다
VS Code Jupyter 확장은 커널 시작 시 ZMQ 포트 정보가 담긴 connection file을 생성하고 그 경로를 --f=<파일> 인자로 넘긴다. 두 프로세스에게 같은 파일을 줬기 때문에 ZMQ 소켓을 먼저 bind()한 프로세스가 포트를 선점하고 나머지는 충돌한다.
4.5 연쇄 구조
.vscode/settings.json 없음
→ 두 확장이 각자 인터프리터 결정
→ 우연히 같은 버전(3.11.9)짜리 두 Python 선택
→ 같은 connection file로 두 커널 동시 실행
→ ZMQ 포트 충돌 → 무응답
5 해결 방법
5.1 즉각 조치 — 충돌 프로세스 종료
# Process ID는 Get-WmiObject Win32_Process 출력에서 확인한 값을 사용한다
Stop-Process -Id 26096 -Force # pyenv base Python 커널
Stop-Process -Id 26484 -Force # venv 커널 (중복)이후 VS Code에서 Ctrl+Shift+P → Jupyter: Restart Kernel.
Linux/macOS에서는 다음 명령으로 connection file이 겹치는 프로세스를 확인한다.
5.2 근본 조치 — .vscode/settings.json으로 인터프리터 고정
.vscode/settings.json에 아래를 추가하면 VS Code가 이 워크스페이스에서 항상 .venv만 사용한다.
{
"python.defaultInterpreterPath": "${workspaceFolder}\\.venv\\Scripts\\python.exe",
"python.terminal.activateEnvironment": true,
"jupyter.kernels.filter": [
{
"path": "${workspaceFolder}\\.venv\\Scripts\\python.exe",
"type": "pythonEnvironment"
}
]
}| 설정 키 | 역할 |
|---|---|
python.defaultInterpreterPath |
Python 확장이 pyenv base Python을 선택하지 않도록 .venv로 고정 |
jupyter.kernels.filter |
Jupyter 커널 선택 목록을 .venv로 제한하여 중복 커널 항목 제거 |
python.defaultInterpreterPath는 “힌트”일 뿐이다. VS Code Python 확장의 내부 인터프리터 선택 캐시가 비어있으면 이 힌트를 무시하고 pyenv Python을 다시 끌어온다.
반드시 Python: Select Interpreter로 한 번 명시적으로 선택해야 VS Code 워크스페이스 상태에 .venv가 확정 인터프리터로 캐싱된다.
Ctrl+Shift+P → Python: Select Interpreter → .venv\Scripts\python.exe (Poetry) 선택
이 선택이 이루어지면 Python 확장 레지스트리에 .venv가 포함되고, Jupyter 확장이 .venv 기반 커널을 valid로 인식하여 pyenv Python이 더 이상 개입하지 않는다.
6 재발 방지 패턴
6.1 Python: Select Interpreter vs 노트북 커널 선택의 차이
두 개념은 역할이 다르다. 순서가 있다 — Python: Select Interpreter를 먼저 해야 Jupyter 커널 선택이 제대로 작동한다.
| Python: Select Interpreter | 노트북 커널 선택 | |
|---|---|---|
| 위치 | Command Palette | 노트북 우측 상단 |
| 역할 | 린팅, IntelliSense, 터미널, 디버깅에 쓸 Python 결정 | 노트북 셀을 실행할 커널 결정 |
| 영향 범위 | 워크스페이스 전체 Python 기능 | 해당 노트북 파일만 |
| Jupyter와의 관계 | Jupyter 확장이 이 선택을 참조해 커널 valid 여부 판단 | 실제 실행 커널 |
python.defaultInterpreterPath(settings.json) → 힌트, 재시작 시 무시될 수 있다
Python: Select Interpreter(UI 선택) → 캐싱, 이후 시작에도 유지된다
프로젝트를 처음 열거나 Reload Window 후에는 항상 Python: Select Interpreter로 .venv를 한 번 선택해두는 것이 두 커널 모두 안정적으로 쓰는 방법이다.
6.2 다중 프로젝트 설정
.vscode/settings.json은 해당 프로젝트 폴더에만 적용된다. ${workspaceFolder}가 각 프로젝트 루트를 가리키므로 다른 프로젝트에는 영향을 주지 않는다.
project-a/
.vscode/settings.json ← project-a에만 적용
project-b/
.vscode/settings.json ← 따로 만들어야 함
pyenv + poetry 조합으로 프로젝트가 여러 개라면 각 프로젝트에 최소한 아래를 추가해두는 것을 권장한다.
{
"python.defaultInterpreterPath": "${workspaceFolder}\\.venv\\Scripts\\python.exe",
"python.terminal.activateEnvironment": true
}conda 기반 프로젝트는 ${workspaceFolder}가 아닌 conda env의 절대 경로를 직접 지정한다.
{
"python.defaultInterpreterPath": "C:\\Users\\<user>\\AppData\\Local\\miniconda3\\envs\\<env-name>\\python.exe"
}7 Python Environment vs Jupyter Kernel (kernelspec) 비교
VS Code에서 노트북 커널을 선택할 때 두 종류가 나타난다. 이 차이를 이해하면 위의 충돌 원인이 명확해진다.
7.1 Python Environment
VS Code Python 확장이 시스템에서 Python 인터프리터를 자동 탐지해서 보여주는 것이다.
7.2 Jupyter Kernel (kernelspec)
ipykernel install로 직접 등록한 커널이다.
.venv\Scripts\python.exe -m ipykernel install ^
--user ^
--name data-standardization ^
--display-name "data-standardization"등록 위치: %APPDATA%\jupyter\kernels\<name>\kernel.json
7.3 비교
| Python Environment | Jupyter Kernel | |
|---|---|---|
| 발견 방식 | VS Code 자동 탐지 | ipykernel install로 등록 |
| 표시 이름 | 폴더명 (.venv) |
직접 지정 (data-standardization) |
| 등록 위치 | VS Code 내부 | %APPDATA%\jupyter\kernels\ |
| 호환성 | VS Code 전용 | 모든 Jupyter 클라이언트 |
| 실행 옵션 제어 | 불가 | kernel.json으로 가능 |
둘 다 같은 .venv\Scripts\python.exe를 실행하므로 동작은 동일하다. 그러나 두 경로가 별개로 존재하기 때문에 설정 없이 두 확장이 각자 하나씩 실행하면 충돌이 발생한다.
7.4 커널 선택 방법
노트북 우측 상단 커널 버튼 클릭 → Select Another Kernel... → Jupyter Kernel... → data-standardization 선택.
Python Environment 경로로 들어가면 .venv 이름으로 보이고, Jupyter Kernel 경로로 들어가면 등록한 이름(data-standardization)으로 보인다.
8 진단 체크리스트
| 체크 포인트 | 확인 명령 | 기대 결과 |
|---|---|---|
.venv 유효성 |
poetry env info |
Valid: True |
ipykernel 설치 |
.venv/Scripts/python.exe -c "import ipykernel" |
버전 출력 |
pyzmq 설치 |
.venv/Scripts/python.exe -c "import zmq" |
버전 출력 |
| kernelspec 등록 | jupyter kernelspec list |
올바른 .venv 경로 |
| 실행 중 프로세스 | Get-WmiObject Win32_Process \| Where { $_.Name -like "*python*" } |
connection file 중복 없음 |
.vscode/settings.json |
파일 존재 여부 확인 | python.defaultInterpreterPath 설정됨 |
| Python: Select Interpreter | VS Code Command Palette | .venv 선택 완료 |
환경이 전부 정상인데 커널이 응답하지 않는다면, 설치 여부보다 지금 실행 중인 프로세스를 먼저 본다. connection file이 겹치는 프로세스가 있는지 확인하는 것이 진단의 핵심이다.
9 관련 주제
선행 지식
- VS Code Introduction & Installation — VS Code 기본 설치
- Poetry - 가상환경 관리 — pyenv + poetry venv 구성
관련 문제
- Conda - Wrapper Failure — conda 환경 인식 실패 패턴
- VM - 가상환경 설치 — 원격 VM에서의 동일 문제
- 커널 연결은 됐는데 셀이 실행 안 되는 문제 — Python 확장 인터프리터 디스커버리 실패 시나리오
- settings.json 설정 충돌로 인한 커널 연결 실패 — kernelCreationMode + preferredKernelSpec 충돌
관련 개념
- ZMQ(ZeroMQ) — 메시지 패싱 라이브러리. Jupyter 커널-클라이언트 통신 기반
- Python: Jupyter 통신 프로토콜 — 커널 lifecycle 심층 (미작성)