Docker 입문 가이드: 개발자가 꼭 알아야 할 컨테이너 기초
"내 컴퓨터에서는 되는데요?" 개발자라면 한 번쯤 들어봤거나 해봤을 말입니다. Docker는 이 문제를 해결합니다. 개발 환경을 코드로 정의해서 어디서든 동일하게 실행합니다.
왜 Docker인가?#
환경 불일치 문제#
개발자 A (Mac, Node 18) → 서버 (Ubuntu, Node 16) → 에러!
개발자 B (Windows, Python 3.11) → 서버 (Python 3.9) → 에러!
Docker의 해결책#
개발자 A → 같은 Docker 이미지 → 서버: 항상 동일
개발자 B → 같은 Docker 이미지 → 서버: 항상 동일
핵심 개념#
이미지 (Image)#
컨테이너를 만들기 위한 읽기 전용 템플릿입니다. 프로그램의 설치 설명서라고 생각하면 됩니다.
# Docker Hub에서 이미지 다운로드
docker pull node:18-alpine
# 로컬 이미지 목록
docker images
컨테이너 (Container)#
이미지를 실행한 격리된 프로세스입니다. 이미지가 클래스라면 컨테이너는 인스턴스입니다.
# 컨테이너 실행
docker run node:18-alpine node --version
# 백그라운드 실행 (-d), 포트 매핑 (-p), 이름 설정 (--name)
docker run -d -p 3000:3000 --name my-app node:18-alpine
# 실행 중인 컨테이너 목록
docker ps
# 컨테이너 중지/삭제
docker stop my-app
docker rm my-app
Dockerfile 작성하기#
Dockerfile은 이미지를 만드는 레시피입니다.
Node.js 앱 Dockerfile#
# 1. 베이스 이미지
FROM node:18-alpine
# 2. 작업 디렉토리 설정
WORKDIR /app
# 3. package.json 먼저 복사 (캐시 최적화)
COPY package*.json ./
# 4. 의존성 설치
RUN npm ci --only=production
# 5. 소스 코드 복사
COPY . .
# 6. 빌드 (Next.js 등)
RUN npm run build
# 7. 포트 노출
EXPOSE 3000
# 8. 실행 명령
CMD ["npm", "start"]
# 이미지 빌드
docker build -t my-nextjs-app .
# 컨테이너 실행
docker run -p 3000:3000 my-nextjs-app
.dockerignore#
불필요한 파일이 이미지에 포함되지 않도록 합니다.
node_modules
.next
.git
*.md
.env
.env.local
레이어 캐싱 최적화#
Dockerfile의 각 명령어는 레이어를 생성합니다. 레이어는 캐시되므로, 자주 변경되는 것을 아래에 배치해야 빌드가 빠릅니다.
# ❌ 비효율: 코드 변경마다 npm install 재실행
COPY . .
RUN npm ci
# ✅ 효율적: package.json이 안 바뀌면 npm install 캐시 사용
COPY package*.json ./
RUN npm ci
COPY . .
Docker Compose#
여러 컨테이너를 함께 관리할 때 사용합니다. 앱 + DB + Redis를 한 번에 실행할 수 있습니다.
# docker-compose.yml
version: '3.8'
services:
app:
build: .
ports:
- "3000:3000"
environment:
- DATABASE_URL=postgresql://user:pass@db:5432/mydb
- REDIS_URL=redis://redis:6379
depends_on:
- db
- redis
db:
image: postgres:15-alpine
environment:
POSTGRES_USER: user
POSTGRES_PASSWORD: pass
POSTGRES_DB: mydb
volumes:
- postgres_data:/var/lib/postgresql/data
redis:
image: redis:7-alpine
ports:
- "6379:6379"
volumes:
postgres_data:
# 모든 서비스 실행
docker compose up -d
# 로그 확인
docker compose logs -f app
# 모든 서비스 중지
docker compose down
# 볼륨까지 삭제
docker compose down -v
개발 환경용 Docker Compose#
코드 변경이 즉시 반영되도록 볼륨 마운트를 사용합니다.
# docker-compose.dev.yml
version: '3.8'
services:
app:
build:
context: .
dockerfile: Dockerfile.dev
ports:
- "3000:3000"
volumes:
- .:/app # 로컬 코드를 컨테이너에 마운트
- /app/node_modules # node_modules는 컨테이너 것 사용
environment:
- NODE_ENV=development
# Dockerfile.dev
FROM node:18-alpine
WORKDIR /app
COPY package*.json ./
RUN npm install
COPY . .
CMD ["npm", "run", "dev"]
docker compose -f docker-compose.dev.yml up
자주 쓰는 Docker 명령어#
# 컨테이너 내부 접속
docker exec -it my-app sh
# 컨테이너 로그 확인
docker logs -f my-app
# 컨테이너 리소스 사용량
docker stats
# 사용하지 않는 리소스 정리
docker system prune -a
# 이미지 크기 확인
docker images --format "{{.Repository}}: {{.Size}}"
멀티 스테이지 빌드#
빌드 환경과 실행 환경을 분리해 이미지 크기를 줄입니다.
# Stage 1: 빌드
FROM node:18-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build
# Stage 2: 실행 (빌드 결과물만 복사)
FROM node:18-alpine AS runner
WORKDIR /app
ENV NODE_ENV=production
COPY --from=builder /app/.next/standalone ./
COPY --from=builder /app/.next/static ./.next/static
COPY --from=builder /app/public ./public
EXPOSE 3000
CMD ["node", "server.js"]
# 결과: 빌드 이미지 ~1GB → 실행 이미지 ~150MB
마무리#
Docker를 익히면 개발 환경 설정 시간이 대폭 줄고, 배포 일관성이 높아집니다. 처음에는 복잡해 보이지만 docker run, Dockerfile, docker-compose.yml 이 세 가지만 익숙해지면 일상적인 개발 워크플로우에 자연스럽게 녹아듭니다. 특히 팀 프로젝트에서 "내 컴퓨터에서는 되는데요" 문제를 완전히 없애줍니다.
관련 포스트
웹 성능 최적화 실전 가이드: Core Web Vitals와 최적화 기법
LCP, FID, CLS 등 Core Web Vitals의 의미와 실제 프론트엔드 성능을 개선하는 실전 기법을 정리했습니다.
REST API vs GraphQL: 실무에서 뭘 선택해야 할까?
REST와 GraphQL의 핵심 차이를 이해하고, 프로젝트 상황에 따른 올바른 선택 기준을 정리했습니다.
CSS Flexbox vs Grid: 언제 뭘 써야 할까?
Flexbox와 Grid의 차이점을 명확히 이해하고, 각 상황에 어떤 레이아웃 방식을 선택해야 하는지 실전 예제와 함께 정리했습니다.