Claude Code Devcontainer 실전 가이드: 재현 가능한 개발 환경
Claude Code를 devcontainer에서 안정적으로 쓰는 방법. Dockerfile, postCreateCommand, 권한, 비밀, volume, port-forward를 정리합니다.
Claude Code를 팀 개발에 쓰기 시작하면 환경 차이가 곧 품질 차이가 됩니다. 한 사람은 Node.js 22를 쓰고, 다른 사람은 Node.js 20을 쓰며, 누군가는 로컬 PostgreSQL을 실행하고 누군가는 Docker만 사용합니다. 이 상태에서 Claude Code에게 버그 수정을 맡기면, 코드 문제보다 환경 설명에 더 많은 시간을 쓰게 됩니다.
Dev Container는 이런 차이를 저장소 안의 설정으로 고정하는 방법입니다. 개발 컨테이너 안에서 터미널, 언어 서버, 테스트, PostgreSQL 클라이언트, Redis 도구, Claude Code CLI를 실행하면 팀원과 Codespaces 환경이 같은 출발점에 서게 됩니다. 이 글에서는 Next.js + TypeScript 예제를 기준으로 .devcontainer/devcontainer.json, Dockerfile, docker-compose.yml, postCreateCommand 스크립트, 권한과 비밀 정보 관리까지 실무적으로 정리합니다.
2026년 6월 2일 기준으로 Claude Code 공식 문서에는 Development containers 페이지가 있으며, Dev Container Feature, 인증 정보 보존, 네트워크 제한, 권한 프롬프트 우회 위험을 설명합니다. Dev Container 자체는 VS Code Dev Containers 문서와 Dev Container Specification도 함께 확인하는 것이 좋습니다.
Claude Code에 devcontainer가 필요한 이유
Claude Code는 파일을 읽고, 코드를 수정하고, 명령을 실행하고, 테스트 결과를 바탕으로 다음 수정을 제안합니다. 그렇기 때문에 실행 환경이 흐릿하면 위험합니다. 호스트 PC에 남아 있던 오래된 전역 패키지, 다른 프로젝트의 환경 변수, 개인 클라우드 자격 증명, 버전이 다른 데이터베이스를 Claude Code가 보게 될 수 있습니다.
devcontainer를 쓰면 Claude Code가 다루는 표면을 저장소에서 리뷰할 수 있습니다. Node.js 버전, OS 패키지, Claude Code CLI 버전, VS Code 확장, postCreateCommand, volume, port-forward가 모두 코드가 됩니다. Claude Code가 npm test를 실행했다고 말할 때, 리뷰어는 어떤 환경에서 실행됐는지 추측하지 않아도 됩니다.
flowchart LR
Host["호스트 PC"] --> Editor["VS Code / Cursor"]
Editor --> Container["Dev Container"]
Container --> Claude["Claude Code CLI"]
Container --> Tools["Node.js / npm / psql / redis-cli"]
Container --> Services["PostgreSQL / Redis"]
Container --> Repo["마운트된 저장소"]
Claude --> Repo
Claude --> Tools
유스케이스는 세 가지가 특히 큽니다. 신규 합류자는 Docker와 에디터 확장만 준비하면 같은 개발 환경을 받습니다. 원격 개발에서는 로컬, Codespaces, 사내 VM이 같은 .devcontainer를 공유합니다. AI 디버깅에서는 Claude Code가 실행한 lint, typecheck, test 결과를 팀 전체가 재현할 수 있습니다.
구성 파일
아래 구성은 애플리케이션, 의존 서비스, Claude Code 설정 저장소를 분리합니다. 모든 것을 호스트 홈 디렉터리에서 그대로 가져오는 방식은 피합니다.
| 파일 | 역할 | 리뷰 포인트 |
|---|---|---|
.devcontainer/devcontainer.json | 에디터가 읽는 devcontainer 진입점 | remoteUser, port, mount, lifecycle command |
.devcontainer/Dockerfile | 개발 도구와 Claude Code 설치 | CLI 버전, non-root 사용자, OS 패키지 |
.devcontainer/docker-compose.yml | app, PostgreSQL, Redis 실행 | volume, healthcheck, 포트 공개 여부 |
.devcontainer/post-create.sh | 컨테이너 생성 후 초기화 | lockfile 우선, Prisma 생성, 실패 처리 |
.claude/settings.json | Claude Code 권한 규칙 | .env, secrets, git push, 위험한 Docker 명령 |
devcontainer.json 예시
.devcontainer/devcontainer.json을 만듭니다. JSON 파일에는 주석을 넣지 않는 것이 안전합니다.
{
"name": "claude-code-next-dev",
"dockerComposeFile": "docker-compose.yml",
"service": "app",
"workspaceFolder": "/workspaces/app",
"remoteUser": "node",
"shutdownAction": "stopCompose",
"waitFor": "postCreateCommand",
"postCreateCommand": "bash .devcontainer/post-create.sh",
"postStartCommand": "git config --global --add safe.directory /workspaces/app || true",
"forwardPorts": [3000, 5432, 6379],
"portsAttributes": {
"3000": { "label": "Next.js", "onAutoForward": "notify" },
"5432": { "label": "PostgreSQL", "onAutoForward": "silent" },
"6379": { "label": "Redis", "onAutoForward": "silent" }
},
"mounts": [
"source=claude-code-config-${devcontainerId},target=/home/node/.claude,type=volume"
],
"containerEnv": {
"NODE_ENV": "development",
"DISABLE_AUTOUPDATER": "1",
"CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC": "1"
},
"customizations": {
"vscode": {
"extensions": [
"anthropic.claude-code",
"dbaeumer.vscode-eslint",
"esbenp.prettier-vscode",
"ms-azuretools.vscode-docker"
],
"settings": {
"editor.formatOnSave": true,
"editor.defaultFormatter": "esbenp.prettier-vscode",
"typescript.tsdk": "node_modules/typescript/lib",
"terminal.integrated.defaultProfile.linux": "bash"
}
}
}
}
remoteUser는 node처럼 non-root 사용자로 둡니다. 컨테이너라고 해서 완전히 안전한 것은 아닙니다. 작업 디렉터리는 호스트 저장소가 bind mount된 것이므로, Claude Code가 파일을 삭제하면 호스트에서도 삭제됩니다. /home/node/.claude는 프로젝트 단위 named volume으로 유지해 재빌드 후 설정을 보존하되, 호스트의 전체 홈 디렉터리는 노출하지 않습니다.
Docker Compose와 Dockerfile
.devcontainer/docker-compose.yml은 app, DB, Redis를 같은 네트워크에서 실행합니다. DB와 Redis는 기본적으로 외부에 publish하지 않고, 에디터의 forwardPorts로 접근합니다.
services:
app:
build:
context: ..
dockerfile: .devcontainer/Dockerfile
command: sleep infinity
volumes:
- ..:/workspaces/app:cached
- node_modules:/workspaces/app/node_modules
- claude_code_config:/home/node/.claude
environment:
DATABASE_URL: postgresql://app:app_password@db:5432/app
REDIS_URL: redis://redis:6379
NEXT_TELEMETRY_DISABLED: "1"
depends_on:
db:
condition: service_healthy
redis:
condition: service_healthy
db:
image: postgres:16-alpine
restart: unless-stopped
environment:
POSTGRES_USER: app
POSTGRES_PASSWORD: app_password
POSTGRES_DB: app
volumes:
- postgres_data:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U app -d app"]
interval: 5s
timeout: 5s
retries: 20
redis:
image: redis:7-alpine
restart: unless-stopped
volumes:
- redis_data:/data
healthcheck:
test: ["CMD", "redis-cli", "ping"]
interval: 5s
timeout: 5s
retries: 20
volumes:
node_modules:
claude_code_config:
postgres_data:
redis_data:
Dockerfile에서는 Claude Code CLI를 고정합니다. 이 글에서는 2026년 6월 2일 npm view @anthropic-ai/claude-code version으로 확인한 2.1.160을 사용합니다.
FROM mcr.microsoft.com/devcontainers/typescript-node:1-22-bookworm
ARG CLAUDE_CODE_VERSION=2.1.160
ENV DISABLE_AUTOUPDATER=1
ENV NEXT_TELEMETRY_DISABLED=1
RUN apt-get update \
&& apt-get install -y --no-install-recommends \
jq \
postgresql-client \
redis-tools \
ripgrep \
&& rm -rf /var/lib/apt/lists/*
RUN npm install -g "@anthropic-ai/claude-code@${CLAUDE_CODE_VERSION}" \
&& npm cache clean --force
USER node
WORKDIR /workspaces/app
RUN mkdir -p /home/node/.claude
버전을 고정하면 업데이트가 어려워지는 것이 아니라, 업데이트가 리뷰 가능한 변경이 됩니다. ARG를 올리고, 컨테이너를 재빌드하고, claude --version, lint, test 결과를 확인하면 됩니다.
postCreateCommand 스크립트
초기 설정은 .devcontainer/post-create.sh에 둡니다. 긴 명령을 JSON 한 줄에 넣으면 리뷰하기 어렵습니다.
#!/usr/bin/env bash
set -euo pipefail
cd /workspaces/app
corepack enable
if [ -f pnpm-lock.yaml ]; then
pnpm install --frozen-lockfile
elif [ -f yarn.lock ]; then
yarn install --immutable
elif [ -f package-lock.json ]; then
npm ci
elif [ -f package.json ]; then
npm install
fi
if [ -f prisma/schema.prisma ]; then
npx prisma generate
fi
node --version
npm --version
claude --version || true
이 스크립트는 lockfile을 우선합니다. Claude Code에게 단순히 “의존성을 설치해”라고 맡기면 npm install로 패키지 매니저 흐름이 바뀔 수 있습니다. postCreateCommand는 반드시 끝나는 작업이어야 하며, npm run dev 같은 상주 프로세스는 별도 터미널이나 task로 실행하는 편이 낫습니다.
권한, 비밀 정보, volume, port-forward
프로덕션 .env, ~/.ssh, ~/.aws, ~/.config/gcloud를 그대로 mount하지 마세요. 개발용 DATABASE_URL은 Compose 내부 서비스만 가리키고, 실제 API key는 Codespaces secret이나 짧은 수명의 토큰으로 다루는 편이 안전합니다.
프로젝트 수준 권한은 .claude/settings.json에 둡니다.
{
"$schema": "https://json.schemastore.org/claude-code-settings.json",
"permissions": {
"allow": [
"Bash(npm run lint)",
"Bash(npm run test *)",
"Bash(npm run typecheck)",
"Bash(claude --version)"
],
"deny": [
"Read(./.env)",
"Read(./.env.*)",
"Read(./secrets/**)",
"Bash(printenv *)",
"Bash(git push *)",
"Bash(docker system prune *)"
]
}
}
volume은 속도를 높이지만 오래된 상태도 보존합니다. node_modules는 컨테이너 volume에 두는 것이 안전하고, DB volume은 migration 실험 후 리셋 절차가 필요합니다. port-forward는 먼저 forwardPorts로 처리하고, DB나 Redis를 Compose ports로 공개하는 것은 정말 필요한 경우로 제한합니다.
실패 사례와 확인 순서
가장 흔한 실패는 root로 Claude Code를 실행하는 것입니다. 파일 소유권이 깨지고, 권한 우회 모드의 위험도 커집니다. 두 번째는 CLI 버전을 고정하지 않는 것입니다. 어제와 오늘의 Claude Code가 다르면 리뷰 결과를 재현하기 어렵습니다. 세 번째는 .env를 컨테이너에 넣어 놓고 권한 설정만 믿는 것입니다. 애초에 전달하지 않는 쪽이 더 강합니다.
재빌드 후에는 node --version, npm --version, claude --version을 확인하고, npm run lint나 npm test 중 하나 이상을 실행합니다. http://localhost:3000이 forward port로 열리는지, .env와secrets/**가 Claude Code 읽기 범위 밖인지, DB와 Redis가 불필요하게 외부 공개되지 않았는지 확인합니다.
서비스 구성을 더 자세히 보려면 Claude Code Docker Compose 개발 환경을, 검증을 배포 흐름에 넣고 싶다면 Claude Code CI/CD 설정 가이드를 함께 읽어보세요. 팀에서 devcontainer, CLAUDE.md, 권한, 리뷰 체크리스트를 표준화하려면 training / consultation에서 시작할 수 있습니다.
실제로 적용해 본 결과
작은 Next.js 검증 저장소에 이 구성을 적용했을 때 가장 효과가 컸던 부분은 postCreateCommand를 스크립트로 분리한 것, node_modules를 컨테이너 volume으로 둔 것, /home/node/.claude를 프로젝트별 volume으로 보존한 것이었습니다. 재빌드 후 Claude Code 버전, 의존성 설치, Prisma 생성, 3000번 포트 확인이 같은 순서로 재현됐습니다. 반대로 DB volume은 migration 실험 후 오래된 상태를 남길 수 있으므로 리셋 기준을 문서화해야 합니다.
무료 PDF: Claude Code 치트시트
이메일을 입력하면 명령, 리뷰 습관, 안전한 워크플로를 정리한 PDF를 받을 수 있습니다.
개인정보를 안전하게 관리하며 스팸을 보내지 않습니다.
작성자 소개
Masa
Claude Code 실무 워크플로와 팀 도입을 검증하는 엔지니어입니다.
관련 글
Claude Code 권한 세이프티 래더: 통제력을 잃지 않고 allow 넓히기
read-only에서 제한 편집, 검증 명령, deploy 확인까지 권한을 단계적으로 넓히는 방법.
Claude Code Small PR Proof Pack: 작은 PR을 리뷰 가능한 상태로 만드는 증거 세트
Claude Code의 작은 PR에 diff, 검증, 공개 URL, CTA 경로, rollback을 붙이는 실무 체크리스트.
Claude Code 커밋 전 리뷰 게이트: diff, 테스트, 공개 URL, CTA 확인
Claude Code 작업을 커밋하기 전에 diff 범위, build, 공개 URL, Gumroad 링크, 상담 CTA, 테스트 누락과 무관한 파일을 확인하는 방법입니다.