오늘은 React(Frontend), FastAPI(Backend), 그리고 **DB(PostgreSQL, Elasticsearch)**까지 복잡한 풀스택 환경을 Docker와 Nginx를 이용해 깔끔하게 정리하는 방법을 소개합니다.
특히, 정적 파일 서빙 시 발생하는 500 Error 해결법과 실제 주식 분석 대시보드(StockPulse) 구축 사례를 통해 실무 적용 팁까지 알려드릴게요!
1. 아키텍처 구조 잡기
우리가 만들 구조는 다음과 같습니다. 모든 서비스는 my_network라는 도커 네트워크 안에서 서로 통신합니다.
- Frontend: React (Node.js로 빌드 -> Nginx로 서빙)
- Backend: FastAPI
- Database: PostgreSQL, Elasticsearch
2. Dockerfile: React 빌드와 Nginx 서빙을 동시에! (Multi-stage Build)
가장 중요한 프론트엔드 설정입니다. 단순히 Node.js 컨테이너를 띄우는 게 아니라, 빌드(Build) 단계와 실행(Run) 단계를 나누는 것이 핵심입니다. 이렇게 하면 이미지 용량을 획기적으로 줄일 수 있습니다.
📄 Frontend Dockerfile 예시
Dockerfile
# 1단계: Builder (Node.js 환경)
FROM node:18-alpine AS builder
WORKDIR /app
# pnpm 설치 및 의존성 설치
RUN npm install -g pnpm
COPY package.json pnpm-lock.yaml ./
RUN pnpm install --frozen-lockfile
# 소스 코드 복사 및 빌드
COPY . .
RUN pnpm build
# 2단계: Runner (Nginx 환경)
FROM nginx:alpine
# Nginx 설정 파일 덮어쓰기 (커스텀 설정 필요 시)
COPY nginx.conf /etc/nginx/conf.d/default.conf
# 1단계에서 빌드된 결과물(dist)을 Nginx의 서빙 디렉토리로 복사
COPY --from=builder /app/dist /usr/share/nginx/html
# 권한 설정 (매우 중요! ★★★)
# Nginx가 파일에 접근할 수 있도록 권한을 부여해야 500 에러를 방지함
RUN chmod -R 755 /usr/share/nginx/html
EXPOSE 80
CMD ["nginx", "-g", "daemon off;"]
💡 핵심 포인트: COPY --from=builder를 사용해 빌드된 정적 파일만 쏙 빼와서 Nginx로 옮겼습니다. 이것이 가볍고 빠른 컨테이너의 비결입니다.
3. Docker Compose: 모든 서비스를 한 곳에서 (Orchestration)
이제 docker-compose.yml을 통해 DB, 백엔드, 프론트엔드를 하나로 묶습니다.
📄 docker-compose.yml 예시
YAML
version: '3.8'
services:
# 1. 데이터베이스 (PostgreSQL)
db:
image: postgres:15
environment:
POSTGRES_USER: user
POSTGRES_PASSWORD: password
POSTGRES_DB: stock_db
networks:
- my_network
# 2. 검색 엔진 (Elasticsearch)
elasticsearch:
image: docker.elastic.co/elasticsearch/elasticsearch:8.11.1
environment:
- discovery.type=single-node
networks:
- my_network
# 3. 백엔드 (FastAPI)
backend:
build: ./backend
ports:
- "8000:8000"
depends_on:
- db
- elasticsearch
networks:
- my_network
# 4. 프론트엔드 (React + Nginx)
frontend:
build: ./frontend
ports:
- "80:80" # 호스트 80포트를 컨테이너 80포트(Nginx)로 연결
depends_on:
- backend
networks:
- my_network
networks:
my_network:
driver: bridge
4. 🚨 트러블슈팅: 이것만 알면 에러 탈출!
대화 내용에서 발생했던 주요 에러와 해결책을 정리했습니다.
❌ 상황 1: Nginx 500 Internal Server Error
- 증상: 배포 후 페이지 접속 시 ERR_FAILED 500 발생.
- 원인: Nginx가 /usr/share/nginx/html 내부의 파일을 읽으려는데 권한이 없을 때 발생합니다.
- ✅ 해결: Dockerfile 내부에 권한 부여 명령어 추가.또한, Nginx 설정 파일(nginx.conf)에서 로그 경로가 올바른지, root 경로가 정확히 dist 파일들이 있는 곳을 가리키는지 확인해야 합니다.
-
Dockerfile
RUN chmod -R 755 /usr/share/nginx/html
❌ 상황 2: Service 'frontend' failed to build
- 증상: docker-compose up 실행 시 빌드 실패.
- 원인: 주로 pnpm install 과정에서 네트워크 이슈나 package.json 경로 문제, 혹은 백엔드 서비스가 아직 준비되지 않았는데 연결을 시도할 때 발생합니다.
- ✅ 해결:
- Docker Compose의 depends_on을 확인하여 실행 순서 보장.
- 로컬의 node_modules가 컨테이너로 복사되지 않도록 .dockerignore 파일 설정 필수.