feat: 개발 모드 → 배포 모드 전환

- 프론트엔드+백엔드 단일 컨테이너로 통합 (Dockerfile)
- Fastify 정적 파일 서빙 + SPA fallback
- @fastify/static 추가
- Caddy 프록시 대상 변경 (traeon-frontend → traeon)
- 체크박스 w-5/w-0 CSS 충돌 수정

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
caadiq 2026-03-24 21:05:46 +09:00
parent 92b223eea1
commit f2070aa8ba
8 changed files with 86 additions and 27 deletions

9
.dockerignore Normal file
View file

@ -0,0 +1,9 @@
node_modules
frontend/node_modules
backend/node_modules
frontend/dist
backend/dist
logo
docs
.git
*.md

20
Dockerfile Normal file
View file

@ -0,0 +1,20 @@
FROM node:20-alpine
WORKDIR /app
# 프론트엔드 빌드
COPY frontend/package*.json ./frontend/
RUN cd frontend && npm install
COPY frontend/ ./frontend/
RUN cd frontend && npm run build
# 백엔드
COPY backend/package*.json ./backend/
RUN cd backend && npm install --omit=dev
COPY backend/ ./backend/
# 프론트엔드 빌드 결과를 백엔드 dist로 복사
RUN cp -r frontend/dist backend/dist
WORKDIR /app/backend
EXPOSE 80
CMD ["node", "src/server.js"]

View file

@ -1,4 +1,21 @@
# 개발 모드 # 배포 모드
FROM node:20-alpine FROM node:20-alpine AS base
WORKDIR /app WORKDIR /app
CMD ["sh", "-c", "npm install && npm run dev"]
# 프론트엔드 빌드
COPY frontend/package*.json ./frontend/
RUN cd frontend && npm ci
COPY frontend/ ./frontend/
RUN cd frontend && npm run build
# 백엔드
COPY backend/package*.json ./backend/
RUN cd backend && npm ci --production
COPY backend/ ./backend/
# 프론트엔드 빌드 결과를 백엔드 dist로 복사
RUN cp -r frontend/dist backend/dist
WORKDIR /app/backend
EXPOSE 80
CMD ["node", "src/server.js"]

View file

@ -8,6 +8,7 @@
}, },
"dependencies": { "dependencies": {
"@fastify/cors": "^11.2.0", "@fastify/cors": "^11.2.0",
"@fastify/static": "^8.0.0",
"dayjs": "^1.11.13", "dayjs": "^1.11.13",
"fastify": "^5.2.1", "fastify": "^5.2.1",
"fastify-plugin": "^5.0.1", "fastify-plugin": "^5.0.1",

View file

@ -1,10 +1,17 @@
import fs from 'fs';
import path from 'path';
import { fileURLToPath } from 'url';
import Fastify from 'fastify'; import Fastify from 'fastify';
import fastifyCors from '@fastify/cors'; import fastifyCors from '@fastify/cors';
import fastifyStatic from '@fastify/static';
import config from './config/index.js'; import config from './config/index.js';
import dbPlugin from './plugins/db.js'; import dbPlugin from './plugins/db.js';
import trackerPlugin from './plugins/tracker.js'; import trackerPlugin from './plugins/tracker.js';
import routes from './routes/index.js'; import routes from './routes/index.js';
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
export async function buildApp(opts = {}) { export async function buildApp(opts = {}) {
const fastify = Fastify({ const fastify = Fastify({
logger: { logger: {
@ -33,5 +40,22 @@ export async function buildApp(opts = {}) {
return { status: 'ok', timestamp: new Date().toISOString() }; return { status: 'ok', timestamp: new Date().toISOString() };
}); });
// 정적 파일 서빙 (프론트엔드 빌드 결과물)
const distPath = path.join(__dirname, '../dist');
if (fs.existsSync(distPath)) {
await fastify.register(fastifyStatic, {
root: distPath,
prefix: '/',
});
// SPA fallback
fastify.setNotFoundHandler((request, reply) => {
if (request.url.startsWith('/api/')) {
return reply.code(404).send({ error: 'Not found' });
}
return reply.sendFile('index.html');
});
}
return fastify; return fastify;
} }

View file

@ -1,26 +1,11 @@
services: services:
traeon-frontend: traeon:
build: ./frontend build: .
container_name: traeon-frontend container_name: traeon
labels:
- "com.centurylinklabs.watchtower.enable=false"
volumes:
- ./frontend:/app
depends_on:
- traeon-backend
networks:
- app
restart: unless-stopped
traeon-backend:
build: ./backend
container_name: traeon-backend
labels: labels:
- "com.centurylinklabs.watchtower.enable=false" - "com.centurylinklabs.watchtower.enable=false"
env_file: env_file:
- .env - .env
volumes:
- ./backend:/app
depends_on: depends_on:
- delivery-tracker - delivery-tracker
networks: networks:

View file

@ -1,4 +1,7 @@
# 개발 모드 # 프론트엔드 빌드 전용
FROM node:20-alpine FROM node:20-alpine AS build
WORKDIR /app WORKDIR /app
CMD ["sh", "-c", "npm install --include=dev && npm run dev -- --host 0.0.0.0"] COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build

View file

@ -262,10 +262,10 @@ function MainPage({ showForm, setShowForm }) {
e.stopPropagation(); e.stopPropagation();
toggleCheck(parcel.id); toggleCheck(parcel.id);
}} }}
className={`w-5 h-5 rounded-md border-2 flex items-center justify-center shrink-0 transition-all duration-300 ${ className={`h-5 rounded-md border-2 flex items-center justify-center shrink-0 transition-all duration-300 ${
deleteMode deleteMode
? "opacity-100 scale-100 mr-0" ? "w-5 opacity-100 scale-100 mr-0"
: "opacity-0 scale-0 w-0 -mr-2" : "w-0 opacity-0 scale-0 -mr-2"
} ${ } ${
deleteMode && checkedIds.has(parcel.id) deleteMode && checkedIds.has(parcel.id)
? "bg-primary border-primary cursor-pointer" ? "bg-primary border-primary cursor-pointer"