❓ Information
- vite 프로젝트를 docker 로 배포하는 방법
Dockerfile (멀티 스테이지 빌드)
# 1단계: build
FROM node:22 AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build
# 2단계: serve
FROM nginx:alpine
COPY --from=builder /app/dist /usr/share/nginx/html
COPY nginx.conf /etc/nginx/conf.d/default.conf1단계
package*.json만 먼저 복사 ->npm ci실행 ->node_modules생성.- 이후에
COPY . .로 전체 소스 복사 이렇게 하면 Docker layer cache 를 활용할 수 있다.
package.json안바뀌면npm ci단계는 캐시됨 -> build 속도 빨라짐.- 만약 처음부터
COPY . .해버리면, 코드 조금만 바뀌어도 매번 dependency 설치를 새로 해야 되서 비효율적.
2단계
nginx:alpine vs nginx:latest
nginx:apine-> 경량화된 버전 (약 20MB)nginx:latest-> 표준 버전 (약 130MB 이상) 👉운영 환경에서는 불필요한 패키지가 빠진 alpine 버전을 많이 씀. (보안/배포 속도/스토리지 절약)
/usr/share/nginx/html 이유
- nginx 기본 설정에서 정적 파일 root 경로가
/usr/share/nginx/html로 잡혀 있음 - 우리가
dist빌드 결과를 여기에 복사하면 nginx 가 자동으로 index.html + 정적 파일을 서비스함 👉즉, nginx 디폴트 동작을 그대로 활용하는것
/etc/nginx/conf.d/default.conf 이유
- nginx 기본 이미지는
/etc/nginx/conf.d/default.conf라는 파일을 기본 설정으로 둠. - 우리가 정의한
nginx.conf를 이 위치에 덮어씌우면 커스텀 설정 이 적용됨. /etc/nginx/nginx.conf도 있지만, 보통 메인 프레임워크 파일이고 실제 vhost(사이트 설정)는conf.d폴더에서 불러옴 👉따라서 프로젝트에서 만든nginx.conf->default.conf로 복사하는게 일반적인 패턴.
docker-compose.yml
version: "3.8"
networks:
projectname-licence-network:
driver: bridge
services:
frontend:
build:
context: .
dockerfile: Dockerfile
container_name: frontend
ports:
- "3000:80"
depends_on:
- backend
- dbnetworks: external: true
external: true-> 이미 존재하는 네트워크를 사용하겠다는 의미driver: bridge-> 새로운 네트워크를 docker-compose 실행할 때 만들겠다는 의미. 이미projectname-license-network라는 네트워크를 만들었고, FE/BE/DB를 같은 네트워크에 붙이려는거라면external: true를 써야함
build: contsxt: .
context: .-> Docker build context 를 현재 디렉토리 (.) 로 설정.- 이 경로 안에 있는 모든 파일이 build 과정에서 COPY 가능
dockerfile: Dockerfile로 특정 Dockerfile 지정 즉,
build:
context: .
dockerfile: Dockerfile= 현재 폴더에 있는 Dockerfile 로 빌드
depnedes_on 은 service 이름 기준
nginx.conf
server {
listen 80;
root /usr/share/nginx/html;
index index.html;
location / {
try_files $uri /index.html;
}
location /api/ {
proxy_pass http://backend:8000/;
}
}try_files 지시어가 하는 역할
location / {...}
-> 모든 요청 경로 (/로 시작하는 것 전부) 에 대해 적용 하겠다는 의미.try_files $uri /index.html;-> 요청이 들어왔을 때 다음 순서대로 확인:$uri-> 클라이언트가 요청한 실제 파일 경로가 존재하는지 확인 (예:/style.css,/main.js)- 없으면
/index.html로 fallback
왜 이렇게 쓰는가? (SPA 라우팅 문제 해결)
Vite + React 같은 SPA 는 라우팅을 클라이언트 측에서 처리함. 예시:
- 사용자가
/dashboard주소로 들어옴. - 서버 입장에서는
/dashboard/index.html같은 파일이 없음 -> 원래라면 404 NotFound. - 하지만 SPA 는
index.html하나로 시작해서 React Route 같은 클라이언트 라우터가/dashboard를 해석해야함 👉 그래서try_fielx $uri /index.html;을 두면, - 정적 파일(css/js) 요청은 그대로 처리
- 없는 경로 (
/dashboard,/settings등)는/index.html을 반환 -> React Router 가 이어받아 처리
만약 이걸 안쓰면?
- SPA 라우팅에서 직접 UTL 입력 시 전부 404 발생
- 오직
/(루트)에서만 정상적으로 앱이 열림
정리
try_filex $uri /index.html; 은 SPA 배포시 필수 설정 으로 ,
정적 파일은 정상적으로 제공하고, 나머지 모든 경로는 index.html로 fallback 시켜서 클라이언트 라우팅이 동적하게 만드는 역할임.