
현대 웹 애플리케이션은 주로 Frontend(이하 FE)와 Backend(이하 BE)로 분리되어 개발됩니다. 컨테이너화(Containerization)는 애플리케이션을 가볍고 이식성 높은 단위로 패키징하여 어디서든 동일한 환경에서 실행될 수 있도록 해 주는 기술입니다. 본 블로그에서는 FE 컨테이너화와 BE 컨테이너화의 개념, 구현 방법, 고려 사항을 상세히 다루겠습니다.
아래는 React 애플리케이션을 Nginx로 서빙하기 위한 간단한 Dockerfile 예시입니다.
# 1. 빌드 스테이지
FROM node:18-alpine AS build
# 작업 디렉토리 설정
WORKDIR /app
# 의존성 복사 및 설치
COPY package.json package-lock.json ./
RUN npm ci --silent
# 소스 코드 복사
COPY . .
# 빌드 실행 (production용)
RUN npm run build
# 2. 실제 실행 스테이지 (정적 서버 구성)
FROM nginx:1.23-alpine
# 기존 default.conf 제거 (옵션)
RUN rm /etc/nginx/conf.d/default.conf
# 커스텀 Nginx 설정 복사 (예: /etc/nginx/conf.d/app.conf)
COPY nginx.conf /etc/nginx/conf.d/app.conf
# 빌드 결과물을 Nginx가 서빙할 경로로 복사
COPY --from=build /app/build /usr/share/nginx/html
# 컨테이너 실행 시 Nginx 데몬이 포그라운드로 실행되도록 설정
CMD ["nginx", "-g", "daemon off;"]
node:18-alpine 이미지를 사용하여 프로젝트 의존성을 설치하고 npm run build를 실행. /app/build 디렉토리에 생성됨. nginx:1.23-alpine 이미지를 기반으로 custom Nginx 설정(nginx.conf)을 적용. /usr/share/nginx/html)로 복사. gzip 압축 설정 npm run build)에 REACT_APP_* 형태의 환경 변수를 주입하거나, 런타임에 envsubst 등을 이용해 템플릿 처리 docker build → docker push → Kubernetes/서버 배포 플로우 구성 nginx 또는 www-data 등 비루트 사용자로 설정아래는 Express 기반의 간단한 Node.js BE 애플리케이션을 컨테이너화하기 위한 Dockerfile 예시입니다.
# 1. 베이스 이미지 설정
FROM node:18-alpine
# 애플리케이션 전용 디렉토리 생성 및 작업 디렉토리 설정
WORKDIR /usr/src/app
# 의존성 설치를 위한 패키지 복사
COPY package.json package-lock.json ./
# 프로덕션 의존성만 설치 (옵션: devDependencies 제외)
RUN npm ci --only=production
# 소스 코드 복사
COPY . .
# 환경 변수 설정 (예: 포트, 실행 모드 등)
ENV NODE_ENV=production
ENV PORT=3000
# 컨테이너가 바인딩할 포트
EXPOSE 3000
# 애플리케이션 시작 명령
CMD ["node", "server.js"]
node:18-alpine을 사용하여 경량화 및 빠른 빌드 지원. npm ci --only=production으로 프로덕션용 의존성만 설치. NODE_ENV, PORT 등 런타임에 필요한 환경 변수 설정. server.js를 통해 Express 서버 기동.package.json과 package-lock.json을 먼저 복사하고 npm ci를 실행하면, 코드 변경 시 의존성이 변경되지 않았다면 Docker 빌드 캐시 활용 가능..env 파일을 직접 복사하지 말고, 도커 실행 시 -e 옵션 또는 docker-compose.yml에서 env_file을 활용. HEALTHCHECK 지시어를 사용하여 주기적으로 /health 엔드포인트 요청 등으로 상태 확인. maven 또는 gradle 빌드 스테이지를 별도로 두고, 실행 시 jre 이미지를 사용하는 식으로 이미지 경량화 가능.| 구분 | FE 컨테이너화 | BE 컨테이너화 |
|---|---|---|
| 목적 | 정적 자산(HTML/CSS/JS)을 서빙 | API 서버 또는 애플리케이션 서버 실행 |
| 기반 기술 스택 | React, Vue, Angular 등 + Nginx, Caddy 등 | Node.js, Java(Spring Boot), Python(Django/Flask) 등 |
| 이미지 크기 | 대체로 경량 (정적 빌드 결과물 + 웹 서버) | 의존성(런타임, 라이브러리) 포함 → 상대적으로 더 무거움 |
| 빌드 단계 | - Node.js 기반 빌드 스테이지 - Nginx 실행 스테이지 | - 단일 스테이지 또는 멀티 스테이지(빌드 & 런타임 분리) |
| 환경 변수 주입 시점 | - 빌드 시점(npm run build)- 런타임( envsubst) | - 런타임(docker run -e, env_file, 시크릿 관리) |
| 서빙 방식 | 정적 파일 서빙(Nginx) | HTTP API 엔드포인트 노출(Express, Spring Controller 등) |
| 볼륨/클러스터링 | 일반적으로 볼륨이 필요 없음(정적 파일만 포함) | 데이터베이스, 파일 스토리지 등 외부 서비스와 연동 필요 |
| 스케일링 | 정적 컨텐츠 캐시 → CDN 활용 | 오토스케일링 필요(로드밸런서 → 여러 컨테이너 인스턴스) |
| 보안 고려 사항 | - HTTPS 설정 - 콘텐츠 보안 헤더 설정 | - 인증/인가 - 시크릿 관리 - CORS 설정 등 |
FE 파이프라인 예시 (GitHub Actions 기반)
Checkout 코드
- name: Checkout code
uses: actions/checkout@v3
Node.js 설정 & 의존성 설치
- name: Setup Node.js
uses: actions/setup-node@v3
with:
node-version: '18'
- name: Install dependencies
run: npm ci
빌드 & 이미지 생성
- name: Build React App
run: npm run build
- name: Build Docker Image
run: |
docker build -t myrepo/frontend:${{ github.sha }} .
Docker Registry 푸시
- name: Log in to DockerHub
uses: docker/login-action@v2
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Push Docker Image
run: |
docker push myrepo/frontend:${{ github.sha }}
배포 (예: Kubernetes, ECS 등)
- name: Deploy to Kubernetes
run: |
kubectl set image deployment/frontend-deployment \
frontend=myrepo/frontend:${{ github.sha }}
BE 파이프라인 예시 (GitLab CI 기반)
stages:
- build
- test
- dockerize
- deploy
build:
stage: build
image: node:18-alpine
script:
- npm ci
- npm run lint
- npm test
dockerize:
stage: dockerize
image: docker:stable
services:
- docker:dind
script:
- docker build -t registry.example.com/mygroup/backend:$CI_COMMIT_SHORT_SHA .
- docker push registry.example.com/mygroup/backend:$CI_COMMIT_SHORT_SHA
deploy:
stage: deploy
image: bitnami/kubectl:latest
script:
- kubectl set image deployment/backend-deployment backend=registry.example.com/mygroup/backend:$CI_COMMIT_SHORT_SHA