Dev Container에서 Python·Node 버전을 고정하지 않으면, 같은 저장소를 열어도 팀원마다 설치 결과와 실행 결과가 달라질 수 있습니다. Python은 3.11인데 다른 사람은 3.12를 쓰고, Node는 20과 22가 섞여 있으면 의존성 설치와 실행 로그가 서로 다르게 보이기 쉽습니다.


이럴 때 중요한 것은 설정 파일을 많이 늘리는 것이 아니라, 어느 파일에 어떤 역할을 맡길지 분리하는 것입니다. devcontainer.json은 개발환경의 선언을, Dockerfile은 기반 이미지와 공통 OS 패키지를 맡기면 버전 기준이 더 분명해지고 재빌드할 때도 덜 헷갈립니다.


Dev Container에서 Python과 Node 버전을 devcontainer.json과 Dockerfile로 고정하는 개념 이미지

팀마다 다른 Python·Node 버전 때문에 생기는 차이를 Dev Container 안에서 줄이는 기본 구조 예시


Dev Container에서 Python·Node 버전을 왜 먼저 고정해야 할까

Dev Container를 쓰고 있다고 해서 Python과 Node 버전이 자동으로 통일되는 것은 아닙니다. 베이스 이미지 태그, 언어 런타임 버전, 패키지 잠금 파일까지 함께 맞아야 같은 저장소를 열었을 때 비슷한 결과를 기대할 수 있습니다. 특히 latest 같은 느슨한 값을 두면 재빌드 시점에 따라 다른 환경이 올라올 수 있어 팀 작업에서는 불편이 커지기 쉽습니다.


문제가 생기는 지점은 보통 세 군데입니다

첫째는 베이스 이미지입니다. Debian 계열이 바뀌면 기본 패키지 버전과 시스템 라이브러리가 달라집니다. 둘째는 Python과 Node 런타임입니다. 셋째는 의존성 잠금 파일입니다. 컨테이너 버전만 맞고 package-lock.json, pnpm-lock.yaml, poetry.lock, uv.lock이 흔들리면 설치 결과가 다시 달라집니다.


고정 대상은 한 번에 다 보지 말고 나눠서 보면 덜 헷갈립니다

베이스 이미지 태그, 언어 런타임 버전, 패키지 잠금 파일 이 세 층으로 나누면 관리가 쉬워집니다. Dev Container에서는 보통 런타임 버전은 Feature 또는 이미지 태그로 고정하고, Dockerfile에는 팀 공용 OS 패키지만 남기는 구성이 가장 무난합니다.


가장 덜 헷갈리는 구조: devcontainer.json은 선언, Dockerfile은 기반 이미지와 공통 패키지

실무에서는 devcontainer.json에 Python·Node 버전을 적고, Dockerfile에는 베이스 이미지와 공통 패키지를 적는 구성이 가장 관리하기 쉽습니다. 이렇게 두면 누가 봐도 어디에서 버전을 바꾸는지 바로 보이고, 컨테이너를 다시 만들 때도 변경 지점이 분명합니다.


VS Code 공식 문서에서 devcontainer.json과 Dockerfile 역할을 설명하는 Dev Container 생성 안내 화면

공식 문서 기준으로 devcontainer.json과 Dockerfile의 역할 구분을 보여주는 화면 · 출처: Visual Studio Code 공식 문서(Create a Dev Container)


devcontainer.json 예시

아래 예시는 Python 3.12와 Node 22를 고정하고, Dockerfile을 함께 쓰는 기본 형태입니다. Feature 레퍼런스도 major 버전까지 적어 두면 나중에 호환성 범위가 더 분명해집니다.


{
  "name": "python-node-app",
  "build": {
    "dockerfile": "Dockerfile",
    "context": ".."
  },
  "features": {
    "ghcr.io/devcontainers/features/python:1": {
      "version": "3.12"
    },
    "ghcr.io/devcontainers/features/node:2": {
      "version": "22"
    }
  },
  "postCreateCommand": "python --version && node --version && npm --version",
  "remoteUser": "vscode",
  "customizations": {
    "vscode": {
      "extensions": [
        "ms-python.python",
        "ms-python.vscode-pylance",
        "dbaeumer.vscode-eslint",
        "esbenp.prettier-vscode"
      ]
    }
  }
}

핵심은 두 가지입니다. runtime version 값으로 Python 3.12와 Node 22를 바로 보이게 적고, feature reference에도 python:1, node:2처럼 범위를 고정하는 것입니다. 이렇게 두면 런타임 버전과 Feature 자체의 업그레이드 범위를 따로 관리할 수 있습니다.


Dockerfile 예시

Dockerfile에는 베이스 이미지를 고정하고, 팀 전체가 공통으로 쓰는 패키지만 넣는 편이 안전합니다. 언어 버전까지 여기저기 섞어 넣기보다, 역할을 나누는 쪽이 읽기 쉽고 수정도 빠릅니다.


ARG BASE_IMAGE="mcr.microsoft.com/devcontainers/base:2-bookworm"
FROM ${BASE_IMAGE}

RUN apt-get update && export DEBIAN_FRONTEND=noninteractive \
    && apt-get -y install --no-install-recommends \
       build-essential \
       git \
       curl \
       ca-certificates \
    && apt-get clean \
    && rm -rf /var/lib/apt/lists/*

이 구조에서는 Dockerfile이 운영체제와 공통 도구를 담당하고, Python·Node는 devcontainer.json이 담당합니다. 버전을 더 세게 묶고 싶다면 베이스 이미지 태그를 2-bookworm보다 더 구체적인 minor 또는 patch 범위로 좁히고, Feature 레퍼런스도 더 구체적으로 적는 방식으로 조절하면 됩니다.


컨테이너 안에서는 정상이지만 VS Code가 여전히 다른 파이썬을 잡는다면 Python 인터프리터가 VSCode에서 안 잡힐 때 점검 순서를 함께 확인해 두는 편이 좋습니다. Dev Container 설정이 맞아도 기존 워크스페이스 캐시나 인터프리터 선택 상태 때문에 헷갈리는 경우가 있기 때문입니다.


버전 고정을 더 안정적으로 만드는 보조 장치

컨테이너 설정만 맞추고 끝내면 로컬 터미널, CI, 배포 스크립트에서 다시 버전 차이가 날 수 있습니다. Dev Container를 중심으로 두더라도 프로젝트 루트에 짧은 버전 파일과 잠금 파일을 같이 두면 흐름이 더 안정적입니다.


.nvmrc와 .python-version도 같이 두면 좋습니다

팀원이 컨테이너 밖에서 간단히 확인하거나 CI에서 버전 기준을 읽어 갈 때 도움이 됩니다. 컨테이너가 주 환경이더라도, 저장소 자체가 어떤 버전을 기대하는지 한 번 더 드러내 주는 역할을 합니다.


# .nvmrc
22

# .python-version
3.12

패키지 잠금 파일까지 같이 커밋해야 차이가 줄어듭니다

Node 프로젝트라면 package-lock.json 또는 pnpm-lock.yaml, Python 프로젝트라면 사용하는 도구에 맞는 잠금 파일을 함께 관리하는 편이 좋습니다. 컨테이너의 런타임 버전만 고정하고 의존성 해석 결과를 열어두면, 설치 시점마다 결과가 조금씩 달라질 수 있습니다.


무엇부터 확인하면 되는지 빠르게 정리

처음부터 복잡하게 바꾸기보다 아래 순서대로 보면 대부분의 혼선을 줄일 수 있습니다.


1. latest 같은 느슨한 값을 지우고, Python·Node 버전을 숫자로 적습니다.

2. devcontainer.json은 런타임 선언과 에디터 설정에 집중하고, Dockerfile은 베이스 이미지와 공통 패키지만 관리합니다.

3. 프로젝트 루트에 .nvmrc, .python-version, 잠금 파일을 같이 둡니다.

4. 설정을 바꾼 뒤에는 컨테이너를 재빌드하고, 터미널에서 python --version, node --version, npm --version으로 실제 적용 상태를 확인합니다.


언제 Dockerfile에서 직접 설치하는 쪽이 맞을까

기본적으로는 Feature 방식이 읽기 쉽고 빠릅니다. 다만 사내 검증 이미지를 반드시 써야 하거나, 오프라인 빌드 환경이거나, patch 단위까지 아주 엄격하게 통제해야 한다면 Dockerfile 또는 사내 베이스 이미지에서 직접 런타임을 묶는 쪽이 더 맞을 수 있습니다. 중요한 것은 한 저장소 안에서 기준을 하나로 통일하는 것입니다.



함께 보면 좋은 글
VSCode Dev Container 입문 2026: devcontainer.json·Docker·VS Code로 개발환경 통일하는 법
버전 고정 전에 Dev Container 기본 구조부터 정리해 두면 설정 파일의 역할이 훨씬 선명하게 보입니다.
Dev Container 기본 구조부터 먼저 정리하기

함께 보면 좋은 글
Dev Container vs Remote SSH 차이: 언제 무엇을 써야 할까
버전을 컨테이너에 묶을지, 서버 접속 중심으로 운영할지 판단이 필요한 경우 같이 보면 선택 기준이 정리됩니다.
Dev Container와 Remote SSH 선택 기준 확인하기

자주 묻는 질문

Q1. Dev Container에서 Python 버전과 Node 버전은 어디에 고정하나요?

가장 보기 쉬운 방법은 devcontainer.json에서 Python과 Node 런타임 버전을 선언하고, Dockerfile에서는 베이스 이미지와 공통 패키지만 관리하는 방식입니다. 이렇게 나누면 어느 파일에서 버전을 바꾸는지 바로 보이고, 팀원이 설정을 읽을 때도 역할이 분명해집니다.

Q2. devcontainer.json만 수정하면 되나요, Dockerfile도 같이 바꿔야 하나요?

Python과 Node 버전만 조정하는 경우에는 devcontainer.json 수정만으로 충분한 경우가 많습니다. 다만 기반 이미지 태그를 바꾸거나 시스템 패키지까지 함께 맞춰야 한다면 Dockerfile도 같이 확인하는 편이 좋습니다. 핵심은 런타임과 운영체제 레이어를 섞지 않는 것입니다.

Q3. 버전을 바꿨는데 VS Code가 예전 파이썬이나 Node를 계속 잡는 이유는 무엇인가요?

설정 파일만 바꾸고 컨테이너를 다시 빌드하지 않았거나, 워크스페이스에 이전 인터프리터 선택 정보가 남아 있는 경우가 많습니다. 버전을 바꾼 뒤에는 컨테이너를 재빌드하고, 터미널에서 실제 버전을 먼저 확인한 다음 VS Code 인터프리터 선택 상태도 함께 점검하는 편이 빠릅니다.

Q4. Dev Container에서 latest 태그를 쓰면 왜 문제가 되나요?

latest는 재빌드 시점에 따라 다른 이미지나 다른 내부 동작을 가져올 수 있어서 팀 환경을 일정하게 유지하기 어렵습니다. 개인 테스트에서는 편할 수 있지만, 협업 저장소에서는 Python·Node 버전과 베이스 이미지 태그를 숫자로 적어 두는 편이 훨씬 안정적입니다.


팀 개발에서 중요한 것은 최신 버전 사용 자체보다, 같은 저장소를 여는 모든 사람이 같은 결과를 얻는 구조를 먼저 만드는 일입니다.