도커(Docker)는 리눅스 컨테이너 기술을 기반으로 애플리케이션을 격리된 프로세스 단위(컨테이너)로 관리할 수 있도록 도와주는 플랫폼입니다.
가상 머신(VM)처럼 완전한 운영체제를 복제하지 않고 Host OS의 커널을 공유하기 때문에 훨씬 가볍고 빠릅니다.
도커는 계층화된 파일 시스템(Layered File System)을 사용해 애플리케이션 실행 환경을 이미지(Image) 형태로 정의합니다.
이 이미지를 기반으로 컨테이너를 실행하고, 특정 컨테이너의 상태를 다시 새로운 이미지로 저장할 수도 있습니다.
이렇게 만들어진 이미지는 파일로 보관하거나 Docker Hub 같은 원격 저장소에 업로드하여 쉽게 공유할 수 있습니다.
도커만 설치되어있다면 언제 어디서나 동일한 환경에서 컨테이너를 실행할 수 있습니다.
1. 안정적인 환경 보장
도커 컨테이너는 애플리케이션이 어디서 실행되든 항상 동일한 환경을 제공합니다.
덕분에 개발, 테스트, 프로덕션 환경이 달라서 생기는 환경 차이 문제를 크게 줄일 수 있습니다.
2. 가벼운 리소스 활용
도커는 가상 머신보다 훨씬 가볍습니다. 컨테이너는 Host OS의 커널을 공유하므로 GPU나 메모리 자원을 효율적으로 활용할 수 있고 수십 개의 컨테이너를 동시에 실행해도 부담이 적습니다.
3. 자동화된 배포와 재현성
도커 이미지는 버전 관리가 가능합니다. 한 번 만들어둔 이미지를 어디서든 동일하게 재현할 수 있습니다.
CI/CD 파이프라인과 연동하면 자동 배포도 쉽게 할 수 있습니다.
도커에서 이미지란 애플리케이션을 실행하기 위한 환경(템플릿)이라고 할 수 있습니다. 이 환경은 파일들의 집합입니다.
사용 가능한 이미지 확인:
docker image ls
도커 허브는 도커에서 제공하는 이미지 호스팅 서비스입니다. 자주 사용되는 애플리케이션에 대한 공식 이미지를 제공하고 있습니다.
Node.js 버전 확인 방법
1. Docker Hub에서 확인
https://hub.docker.com/_/node 에서 사용 가능한 태그 확인
2. 명령어로 확인
# 현재 사용 중인 Node.js 버전
node --version
# Docker로 특정 버전 확인
docker run --rm node:20.18.3-slim node --version
태그 선택 가이드
# ❌ 비추천: 버전 고정이 없음
FROM node
# ⚠️ 주의: 메이저 버전만 고정
FROM node:20
# ✅ 정확한 버전 + slim
FROM node:20.18.3-slim
# ✅ 프로덕션 용: Alpine (더 작은 크기)
FROM node:20.18.3-alpine
컨테이너는 이미지를 기반으로 실제 실행된 프로세스(실행 중인 인스턴스)입니다. 이미지가 설계도라면 컨테이너는 그 설계도로 만들어진 집입니다.
컨테이너를 실행하려면 반드시 이미지가 있어야 합니다.
현재 실행중인 컨테이너 확인:
docker container ls
각 컨테이너는 독립된 공간에서 실행되기 때문에, 다른 컨테이너나 호스트 환경과 충돌하지 않습니다.
Dockerfile은 이미지를 생성하기 위한 설정 파일입니다. 어떤 베이스 이미지를 사용할 지, 어떤 명령어를 실행할 지 정의합니다.
예시: Next.js + Nginx (Multi-stage Build)
# Build Stage
FROM node:20.18.3-slim AS build-stage
WORKDIR /app
# pnpm 설치
RUN npm install -g pnpm
RUN pnpm install --frozen-lockfile
# 소스코드 복사 및 빌드
COPY . .
RUN pnpm build
# Production Stage
FROM nginx:alpine AS production-stage
# 빌드된 파일 복사
COPY --from=build-stage /app/dist /usr/share/nginx/html
# Nginx 설정 복사
COPY nginx.conf /etc/nginx/nginx.conf
EXPOSE 80
CMD ["nginx", "-g", "daemon off;"]
npm run build 결과물이 /dist 또는 /build 폴더로 생성 -> /usr/share/nginx/html로 복사next start 실행 필요 -> Nginx가 아닌 Node 서버에서 직접 실행따라서 Next.js인 경우는 다음처럼 구성할 수 있습니다.
예시:
# Build Stage
FROM node:18.18.0-slim as build-stage
WORKDIR /app
COPY . .
RUN npm install --legacy-peer-deps
RUN npm install -g pnpm
RUN npx next build --no-lint
# Production Stage
FROM node:18.18.0-slim as production-stage
WORKDIR /app
COPY --from=build-stage /app /app
EXPOSE 3000
CMD ["pnpm", "start"]
여러 개의 컨테이너를 함께 관리하려면 Docker Compose를 사용합니다.
예시: docker-compose.yml
version: "3.8"
services:
frontend:
build: .
ports:
- "3000:3000"
volumes:
- .:/app
environment:
- NODE_ENV=development
nginx:
image: nginx:alpine
ports:
- "80:80"
volumes:
- ./nginx.conf:/etc/nginx/nginx.conf
- ./dist:/usr/share/nginx/html
유용한 Docker Compose 명령어:
# 모든 서비스 시작
docker-compose up
# 백그라운드 실행
docker-compose up -d
# 실시간 로그 확인
docker-compose logs -f frontend
# 모든 서비스 정지 및 컨테이너 삭제
docker-compose down
# 볼륨까지 삭제
docker-compose down -v
events {
worker_connections 1024;
}
http {
# MIME 타입 설정 추가 (중요!)
include /etc/nginx/mime.types;
default_type application/octet-stream;
server {
listen 80;
server_name localhost;
root /usr/share/nginx/html;
index index.html;
# Vue Router의 History Mode를 위한 설정
location / {
try_files $uri $uri/ /index.html;
}
# 정적 파일에 대한 MIME 타입 명시적 설정
location ~* \.js$ {
add_header Content-Type application/javascript;
expires 1y;
add_header Cache-Control "public, immutable";
access_log off;
}
location ~* \.css$ {
add_header Content-Type text/css;
expires 1y;
add_header Cache-Control "public, immutable";
access_log off;
}
# 기타 정적 파일 캐싱
location ~* \.(png|jpg|jpeg|gif|ico|woff|woff2|ttf|svg|eot)$ {
expires 1y;
add_header Cache-Control "public, immutable";
access_log off;
}
# API 프록시 (CORS 해결)
location /api/ {
proxy_pass http://backend:8000/;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
# gzip 압축
gzip on;
gzip_vary on;
gzip_types
text/plain
text/css
application/json
application/javascript
text/xml
application/xml
application/xml+rss
text/javascript;
# 로그 설정
access_log /var/log/nginx/access.log;
error_log /var/log/nginx/error.log;
}
}
server {
listen 80;
server_name localhost;
# Next.js API 요청과 페이지 요청을 처리
location / {
proxy_pass http://nextjs:3000; # Next.js 서버로 요청 전달
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection 'upgrade';
proxy_set_header Host $host;
proxy_cache_bypass $http_upgrade;
}
# 정적 파일 요청 처리 (선택사항)
location /_next/static/ {
proxy_pass http://nextjs:3000;
expires 1y;
add_header Cache-Control "public, immutable";
}
# 에러 페이지 처리
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root /usr/share/nginx/html;
}
}
.dockerignore 파일은 이미지 빌드 시 제외할 파일/폴더를 지정합니다.
.git, node_modules, dist 등을 제외하면 빌드 속도가 크게 향상됩니다.
예시:
# 의존성 (컨테이너에서 새로 설치)
node_modules
npm-debug.log*
yarn-debug.log*
yarn-error.log*
# 빌드 결과물 (컨테이너에서 새로 빌드)
dist
build
.next
out
# 개발 도구
.git
.gitignore
.vscode
.idea
# 로그 및 캐시
logs
*.log
.cache
.temp
.DS_Store
# 환경 설정 (보안상 제외)
.env.local
.env.*.local
# 테스트
coverage
.nyc_output
개발할 때 종종 마주치는 문제가 CORS(Cross-Origin Resource Sharing)에러 입니다. 외부 API나 테스트 서버를 사용할 때 자주 발생합니다.
Docker + Nginx로 해결:

docker-compose.yml
version: "3.8"
services:
nginx:
image: nginx:stable-alpine
volumes:
- ./local-nginx.conf:/etc/nginx/nginx.conf:ro
ports:
- "80:80"
# Linux 사용자의 경우 호스트 네트워크 모드 고려
# network_mode: "host" # Linux에서만 사용
nginx.conf
events { }
http {
# upstream: Next.js 개발 서버 정의
upstream frontend {
# 호스트 머신의 개발 서버 연결
# macOS/Windows: host.docker.internal 사용
# Linux: --network=host 모드 또는 172.17.0.1 사용
server host.docker.internal:3000;
}
server {
listen 80;
server_name 127.0.0.1 localhost;
# API 요청 프록시 (CORS 우회)
location /api/ {
proxy_pass http://test.api.com:7777/api/;
proxy_set_header Host test.api.com;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
# 쿠키 도메인 재작성 (필요시)
proxy_cookie_domain test.api.com localhost;
# CORS 헤더 추가 (백업용)
add_header Access-Control-Allow-Origin *;
add_header Access-Control-Allow-Methods "GET, POST, PUT, DELETE, OPTIONS";
add_header Access-Control-Allow-Headers "Accept,Authorization,Cache-Control,Content-Type,DNT,If-Modified-Since,Keep-Alive,Origin,User-Agent,X-Requested-With";
}
# 🖥️ 프론트엔드 요청 프록시
location / {
proxy_pass http://frontend;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection 'upgrade';
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}
}
사용 방법:
# 1. Next.js 개발 서버 시작 (호스트에서)
npm run dev # localhost:3000에서 실행
# 2. Nginx 프록시 시작 (Docker로)
docker-compose up -d
# 3. 브라우저에서 접속
# http://localhost (포트 80)
프론트엔드 코드에서:
// CORS 에러 없이 API 호출 가능
const response = await fetch('/api/users') // localhost:80/api/users
// 실제로는 test.api.com:7777/api/users로 프록시됨
Docker같은 CI/CD가 매번 구성할때 찾아보고 다시 구성할때 찾아보게 되는데, 그 때마다 이 글을 다시 읽으면 될 것 같이 잘 정리된 글이네요..!!! 좋은 글 감사합니다!!!