devlog.

Docker 입문 가이드: 개발자가 꼭 알아야 할 컨테이너 기초

·8분 읽기

"내 컴퓨터에서는 되는데요?" 개발자라면 한 번쯤 들어봤거나 해봤을 말입니다. 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 이 세 가지만 익숙해지면 일상적인 개발 워크플로우에 자연스럽게 녹아듭니다. 특히 팀 프로젝트에서 "내 컴퓨터에서는 되는데요" 문제를 완전히 없애줍니다.

관련 포스트