Vite 프론트엔드 앱을 Docker로 배포하기와 자주 겪는 에러
1. Dockerfile
정적 SPA는 빌드 결과물만 웹 서버로 제공하면 된다. 개발 서버(npm run dev)를 컨테이너에서 띄우는 방식은 운영 배포에 쓰지 않는다. 빌드 단계와 실행 단계를 나눈 멀티 스테이지 빌드를 사용한다.
# 1단계: 빌드
FROM node:22-alpine AS build
WORKDIR /app
# 의존성 파일만 먼저 복사해 레이어 캐시를 활용
COPY package.json package-lock.json ./
RUN npm ci
COPY . .
RUN npm run build
# 2단계: 정적 파일 제공
FROM nginx:alpine
COPY --from=build /app/dist /usr/share/nginx/html
EXPOSE 80
CMD ["nginx", "-g", "daemon off;"]
node:latest처럼 태그를 고정하지 않으면 빌드할 때마다 Node 메이저 버전이 바뀔 수 있으니 버전을 명시한다. SPA 라우팅을 쓴다면 nginx 설정에 try_files $uri /index.html;을 추가해야 새로고침 시 404가 나지 않는다.
.dockerignore도 함께 둔다. 호스트의 node_modules가 이미지로 복사되는 것을 막는 것이 핵심이다(아래 에러 참고).
# .dockerignore
node_modules
dist
.git
.env*
2. docker-compose.yml
services:
web:
image: my-app:1.0.0
build: .
restart: always
ports:
# 호스트 포트:컨테이너 포트(EXPOSE한 포트)
- "8080:80"
ports는 호스트:컨테이너 순서다. 컨테이너 안의 nginx가 80에서 동작하므로 오른쪽은 80이어야 한다. 최신 Docker Compose에서는 맨 위의 version: 키가 더 이상 필요 없다.
3. 빌드와 실행
# 이미지 빌드 (이름:태그 지정)
docker build -t my-app:1.0.0 .
# 컨테이너 실행: 호스트 8080 → 컨테이너 80
docker run -d -p 8080:80 --name my-app my-app:1.0.0
Compose를 쓴다면:
docker compose up -d --build
docker compose build는 이미지만 만들고 컨테이너를 띄우지 않는다. 실행까지 하려면 up을 사용한다. 이후 http://localhost:8080으로 접속한다.
4. 이미지를 파일로 옮기기 (save / load)
레지스트리 없이 서버로 이미지를 옮길 때 사용한다.
# 로컬: 이미지를 tar로 저장
docker save -o my-app.tar my-app:1.0.0
# 서버: tar에서 이미지 불러오기
docker load -i my-app.tar
Apple Silicon Mac에서 빌드한 이미지를 x86 서버에서 실행하려면 docker build --platform linux/amd64 ...로 빌드해야 한다.
5. 자주 쓰는 명령어
| 명령어 | 설명 |
|---|---|
docker ps | 실행 중인 컨테이너 목록 |
docker ps -a | 중지된 컨테이너 포함 전체 목록 |
docker images | 로컬 이미지 목록 |
docker logs -f <컨테이너> | 컨테이너 로그 확인 |
docker stop <컨테이너> | 컨테이너 중지 |
docker rm <컨테이너> | 중지된 컨테이너 삭제 |
docker rmi <이미지> | 이미지 삭제 |
docker system prune -a | 사용하지 않는 컨테이너·네트워크·이미지·빌드 캐시 삭제 (볼륨은 --volumes를 붙여야 삭제됨) |
6. 자주 겪는 에러
Cannot find module @rollup/rollup-linux-x64-gnu
You installed esbuild for another platform than the one you're currently using
두 에러는 원인이 같다. Rollup과 esbuild는 플랫폼별 네이티브 바이너리를 설치하는데, macOS·Windows에서 설치한 node_modules가 COPY . .으로 리눅스 컨테이너에 그대로 들어가면 플랫폼이 맞지 않는다.
@rollup/rollup-linux-x64-gnu를 직접 설치하거나 esbuild-wasm으로 바꾸는 우회책도 있지만, 근본 해결은 다음 두 가지다.
.dockerignore에node_modules를 넣어 호스트 의존성이 이미지로 들어가지 않게 한다.- 컨테이너 안에서
npm ci로 의존성을 새로 설치한다.
lockfile을 macOS에서 만들었는데 리눅스용 optional 의존성이 빠져 같은 에러가 나는 경우도 있다(npm 이슈 #4828). 이때는 package-lock.json과 node_modules를 지우고 다시 설치한 lockfile을 커밋한다.