Docker 입문 완전 가이드 2026 — 개발자 필수 컨테이너 기초
Docker의 핵심 개념, Dockerfile 작성, Docker Compose 멀티 서비스 구성, Next.js multi-stage 빌드, 보안 모범 사례까지 개발자가 알아야 할 Docker 기초를 완전 정리했습니다.
"내 컴퓨터에서는 되는데..." — Docker가 해결합니다
Docker는 애플리케이션과 실행 환경을 컨테이너로 패키징해 어디서나 동일하게 실행할 수 있게 해주는 플랫폼입니다. 개발 환경의 불일치, 배포 환경 설정 복잡성 문제를 근본적으로 해결합니다.
핵심 개념
이미지 (Image)
실행 환경의 스냅샷. Dockerfile로 정의하며, Docker Hub 같은 레지스트리에 저장·공유합니다.
Node.js 공식 이미지 → 내 코드를 추가 → 내 앱 이미지
컨테이너 (Container)
이미지를 실행한 인스턴스. 격리된 프로세스로 호스트 시스템과 독립적입니다.
이미지 = 레시피
컨테이너 = 레시피로 만든 요리 (실행 중인 인스턴스)
레이어 캐싱
Dockerfile의 각 명령어는 레이어를 만듭니다. 변경된 레이어부터만 재빌드하므로 순서가 중요합니다.
# 변경이 적은 것 먼저 (캐시 활용 극대화)
COPY package*.json ./ # 패키지 목록 변경 시에만 재실행
RUN npm ci # 위가 캐시 히트면 이것도 캐시
COPY . . # 소스 변경마다 재실행
Dockerfile 기초
Node.js 앱 기본 Dockerfile
FROM node:20-alpine
WORKDIR /app
# 패키지 파일 먼저 복사 (캐시 활용)
COPY package*.json ./
RUN npm ci --only=production
# 소스 코드 복사
COPY . .
RUN npm run build
EXPOSE 3000
CMD ["npm", "start"]
.dockerignore — 반드시 설정
node_modules
.next
.git
*.log
.env
.env.local
.env.*.local
*.md
coverage
.dockerignore 없이 COPY . . 하면 node_modules (수백 MB)가 컨테이너에 복사됩니다.
자주 쓰는 명령어
# 이미지 빌드 (-t: 태그/이름 지정)
docker build -t my-app .
docker build -t my-app:v1.2.3 .
# 컨테이너 실행
docker run -p 3000:3000 my-app # 포어그라운드
docker run -d -p 3000:3000 my-app # 백그라운드 (detached)
docker run -d -p 3000:3000 --name webapp my-app # 이름 지정
# 환경 변수 전달
docker run -d -e NODE_ENV=production -e DATABASE_URL=... my-app
# 실행 중인 컨테이너 확인
docker ps
docker ps -a # 중지된 것 포함
# 컨테이너 조작
docker stop webapp
docker start webapp
docker restart webapp
docker rm webapp # 컨테이너 삭제
# 로그 확인
docker logs webapp
docker logs -f webapp # 실시간 스트리밍
# 컨테이너 내부 진입
docker exec -it webapp sh
docker exec -it webapp bash # bash 있는 이미지의 경우
# 이미지 관리
docker images
docker rmi my-app # 이미지 삭제
docker image prune # 사용하지 않는 이미지 정리
Docker Compose — 멀티 컨테이너 관리
앱 서버 + 데이터베이스 + 캐시 등 여러 서비스를 하나의 파일로 정의합니다.
기본 구성 (Next.js + PostgreSQL + Redis)
# docker-compose.yml
services:
app:
build: .
ports:
- "3000:3000"
environment:
- DATABASE_URL=postgresql://user:pass@db:5432/mydb
- REDIS_URL=redis://cache:6379
depends_on:
db:
condition: service_healthy
cache:
condition: service_started
restart: unless-stopped
db:
image: postgres:16-alpine
environment:
POSTGRES_USER: user
POSTGRES_PASSWORD: pass
POSTGRES_DB: mydb
volumes:
- pgdata:/var/lib/postgresql/data
ports:
- "5432:5432"
healthcheck:
test: ["CMD-SHELL", "pg_isready -U user -d mydb"]
interval: 10s
timeout: 5s
retries: 5
cache:
image: redis:7-alpine
volumes:
- redisdata:/data
ports:
- "6379:6379"
volumes:
pgdata:
redisdata:
# 전체 시작 (백그라운드)
docker compose up -d
# 특정 서비스만 시작
docker compose up -d db cache
# 실시간 로그
docker compose logs -f app
# 전체 중지 및 컨테이너 삭제
docker compose down
# 볼륨까지 삭제 (데이터 초기화)
docker compose down -v
# 재빌드 후 시작
docker compose up -d --build
개발 환경 오버라이드
# docker-compose.dev.yml (개발 전용 설정)
services:
app:
volumes:
- .:/app # 소스 코드 바인드 마운트
- /app/node_modules # node_modules는 컨테이너 것 사용
command: npm run dev # dev 서버로 실행
environment:
- NODE_ENV=development
# 개발 환경 실행
docker compose -f docker-compose.yml -f docker-compose.dev.yml up -d
Next.js Multi-stage Build (최적화)
빌드 도구와 소스 코드는 제외하고 실행에 필요한 것만 최종 이미지에 포함합니다.
# 1단계: 의존성 설치
FROM node:20-alpine AS deps
WORKDIR /app
COPY package*.json ./
RUN npm ci
# 2단계: 빌드
FROM node:20-alpine AS builder
WORKDIR /app
COPY --from=deps /app/node_modules ./node_modules
COPY . .
RUN npm run build
# 3단계: 프로덕션 (최소 이미지)
FROM node:20-alpine AS runner
WORKDIR /app
ENV NODE_ENV=production
# non-root 사용자 (보안)
RUN addgroup --system --gid 1001 nodejs
RUN adduser --system --uid 1001 nextjs
COPY --from=builder /app/public ./public
COPY --from=builder --chown=nextjs:nodejs /app/.next/standalone ./
COPY --from=builder --chown=nextjs:nodejs /app/.next/static ./.next/static
USER nextjs
EXPOSE 3000
ENV PORT=3000
HEALTHCHECK --interval=30s --timeout=3s \
CMD wget --no-verbose --tries=1 --spider http://localhost:3000/ || exit 1
CMD ["node", "server.js"]
이미지 크기 비교:
단순 빌드: ~1.2GB
multi-stage: ~180MB (85% 감소)
next.config.ts에 output: "standalone" 추가 필요:
const nextConfig = {
output: "standalone",
};
보안 모범 사례
1. Non-root 유저 사용
RUN addgroup -S appgroup && adduser -S appuser -G appgroup
USER appuser
2. 시크릿 관리
# 나쁜 예 — 이미지 레이어에 시크릿이 남음
RUN export API_KEY=secret && npm run setup
# 좋은 예 — 환경 변수로 런타임에 전달
ENV API_KEY="" # 기본값만, 실제 값은 실행 시 주입
# 실행 시 시크릿 전달
docker run -e API_KEY=${API_KEY} my-app
3. Healthcheck 추가
HEALTHCHECK --interval=30s --timeout=3s --retries=3 \
CMD wget --no-verbose --tries=1 --spider http://localhost:3000/api/health || exit 1
4. 최신 보안 패치 적용
# Alpine에서 최신 보안 패치 적용
RUN apk update && apk upgrade --no-cache
실무 팁
| 팁 | 내용 |
|---|---|
| Alpine 이미지 | node:20 → node:20-alpine (900MB → 130MB) |
| 레이어 캐싱 | package.json 복사 → npm ci → 소스 복사 순서 유지 |
| Non-root 유저 | 보안을 위해 root가 아닌 전용 유저로 실행 |
| .dockerignore | node_modules, .next, .git 반드시 제외 |
| Healthcheck | 컨테이너 상태 모니터링 및 재시작 자동화 |
| Multi-stage | 빌드 이미지와 실행 이미지 분리로 크기 최소화 |
자주 하는 실수
# 실수: 컨테이너 안에서 소스 수정
# → 컨테이너 재시작 시 초기화됨
# 올바른 방법: 바인드 마운트 사용 (개발 시)
docker run -v $(pwd):/app my-app
# 실수: 환경 변수를 Dockerfile에 하드코딩
ENV DATABASE_URL="postgresql://prod-server..."
# 올바른 방법: 실행 시 주입
docker run -e DATABASE_URL=${DATABASE_URL} my-app
관련 글: GitHub Actions CI/CD 설정법 · Vercel 무료 배포 완전 가이드 · Supabase 사이드프로젝트 시작하기
관련 글
Kubernetes 입문 2026 — 개발자가 알아야 할 쿠버네티스 핵심 개념과 실전
개발자를 위한 Kubernetes(쿠버네티스) 입문 가이드. Pod, Deployment, Service, Ingress 핵심 개념부터 kubectl 명령어, 로컬 개발 환경(minikube/kind), 실전 배포까지 정리했습니다.
GitHub Actions CI/CD 완전 가이드 2026 — Next.js 자동 배포 파이프라인 구축
GitHub Actions로 Next.js 프로젝트의 CI/CD 파이프라인을 구축하는 실전 가이드. lint·타입체크·빌드 자동화, Vercel 배포, 캐싱 전략, 시크릿 관리, 비용 최적화까지 정리했습니다.
AWS 비용 최적화 완전 가이드 2026 — EC2, S3, RDS 비용 절감 실전
AWS 비용이 예상보다 많이 나오는 개발자를 위한 최적화 가이드. EC2 Reserved Instance vs Savings Plans, S3 스토리지 클래스, RDS 비용 절감, 무료 티어 한도, 비용 모니터링 설정까지 실전 방법을 정리했습니다.