나스 서비스 준비

This commit is contained in:
2026-06-22 19:52:45 +09:00
parent 2ecf6201c0
commit 331d6c5fe9
7 changed files with 46 additions and 21 deletions

View File

@@ -6,7 +6,7 @@ JWT_SECRET=여기에-32자-이상-랜덤-시크릿-입력
JWT_EXPIRES=7d
# --- 웹 (docker-compose.prod.yml) ---
WEB_PORT=8080
WEB_PORT=44082
# --- 관리자 (최초 기동 시 자동 생성) ---
ADMIN_EMAIL=admin@example.com
@@ -14,7 +14,7 @@ ADMIN_PASSWORD=change-me-8chars
ADMIN_USERNAME=admin
# --- CORS (옆 프로젝트·로컬 개발 포트 포함, 쉼표 구분) ---
CORS_ORIGINS=https://uniare.sori.studio,http://localhost:8080,http://127.0.0.1:8080,http://localhost:5500,http://127.0.0.1:5500,http://localhost:5173
CORS_ORIGINS=https://uniare.sori.studio,http://localhost:44082,http://127.0.0.1:44082,http://localhost:8080,http://127.0.0.1:8080,http://localhost:5500,http://127.0.0.1:5500,http://localhost:5173
# --- 로컬 api:dev 전용 (npm run api:dev) ---
DATAS_PATH=./datas

View File

@@ -1,7 +1,7 @@
# UADECK v4 프로덕션 스택 (NAS Docker)
# 사용: docker compose -f docker-compose.prod.yml up -d --build
#
# 정적 파일(HTML, images, datas)은 호스트 볼륨 마운트 — 카드/이미지 추가 시 재빌드 불필요
# 정적 파일(HTML, images, datas)은 필요한 경로만 read-only 마운트 — 카드/이미지 추가 시 재빌드 불필요
services:
uadeck-api:
@@ -35,14 +35,30 @@ services:
start_period: 15s
uadeck-web:
image: nginx:alpine
build:
context: .
dockerfile: nginx/Dockerfile
image: uadeck-web:latest
container_name: uadeck-web
restart: unless-stopped
ports:
- "${WEB_PORT:-8080}:80"
- "${WEB_PORT:-44082}:80"
volumes:
- ./:/usr/share/nginx/html:ro
- ./nginx/default.conf:/etc/nginx/conf.d/default.conf:ro
- ./index.html:/usr/share/nginx/html/index.html:ro
- ./cardList.html:/usr/share/nginx/html/cardList.html:ro
- ./result.html:/usr/share/nginx/html/result.html:ro
- ./login.html:/usr/share/nginx/html/login.html:ro
- ./signup.html:/usr/share/nginx/html/signup.html:ro
- ./deckHistory.html:/usr/share/nginx/html/deckHistory.html:ro
- ./preset.html:/usr/share/nginx/html/preset.html:ro
- ./profile.html:/usr/share/nginx/html/profile.html:ro
- ./admin.html:/usr/share/nginx/html/admin.html:ro
- ./favicon.ico:/usr/share/nginx/html/favicon.ico:ro
- ./script:/usr/share/nginx/html/script:ro
- ./style:/usr/share/nginx/html/style:ro
- ./i18n:/usr/share/nginx/html/i18n:ro
- ./datas:/usr/share/nginx/html/datas:ro
- ./images:/usr/share/nginx/html/images:ro
depends_on:
uadeck-api:
condition: service_healthy

View File

@@ -17,7 +17,7 @@ cp .env.example .env
| `JWT_SECRET` | 32자 이상 랜덤 문자열 (필수) |
| `ADMIN_EMAIL` | 최초 관리자 이메일 |
| `ADMIN_PASSWORD` | 최초 관리자 비밀번호 (8자+) |
| `WEB_PORT` | 웹 노출 포트 (기본 `8080`) |
| `WEB_PORT` | 웹 노출 포트 (기본 `44082`, 기존 정적 사이트 포트 유지) |
| `CORS_ORIGINS` | 옆 프로젝트 도메인 포함, 쉼표 구분 |
예시:
@@ -26,13 +26,13 @@ cp .env.example .env
JWT_SECRET=your-random-secret-at-least-32-chars
ADMIN_EMAIL=admin@uniare.sori.studio
ADMIN_PASSWORD=your-secure-password
WEB_PORT=8080
CORS_ORIGINS=https://uniare.sori.studio,https://your-other-app.sori.studio,http://localhost:8080,http://localhost:5500,http://127.0.0.1:5500
WEB_PORT=44082
CORS_ORIGINS=https://uniare.sori.studio,https://your-other-app.sori.studio,http://localhost:44082,http://127.0.0.1:44082,http://localhost:5500,http://127.0.0.1:5500
```
## 3. 로컬 테스트
> **한 줄 요약:** 프로젝트 루트에서 `npm start` → 브라우저 **http://localhost:8080**
> **한 줄 요약:** 프로젝트 루트에서 `npm start` → 브라우저 **http://localhost:44082**
회원가입·로그인·덱 저장은 **API 서버**가 떠 있어야 합니다.
HTML만 Live Server 등으로 열면 저장·로그인이 **405** 로 실패합니다.
@@ -41,7 +41,7 @@ HTML만 Live Server 등으로 열면 저장·로그인이 **405** 로 실패합
```bash
npm start # 백그라운드 실행 (터미널 점유 안 함)
# 브라우저: http://localhost:8080
# 브라우저: http://localhost:44082
npm run logs # 로그 보기 (종료: Ctrl+C)
npm stop # 서버 종료
@@ -57,10 +57,10 @@ npm stop # 서버 종료
### 저장이 안 될 때
1. `curl http://localhost:8080/api/v1/health` → `{"ok":true,...}` 가 나와야 함
1. `curl http://localhost:44082/api/v1/health` → `{"ok":true,...}` 가 나와야 함
2. 안 나오면 `npm start` 다시 실행
3. `npm run dev` 후 `Healthy`에서 멈춘 것처럼 보여도 **정상**이지만, `Ctrl+C`하면 서버가 꺼짐 → **`npm start` 사용 권장**
4. Live Server(`:5500`)로 HTML만 연 경우 → 아래 방법 B 또는 **반드시 `:8080` 사용**
4. Live Server(`:5500`)로 HTML만 연 경우 → 아래 방법 B 또는 **반드시 `:44082` 사용**
### 방법 A — Docker (`npm start`와 동일)
@@ -68,7 +68,7 @@ npm stop # 서버 종료
npm start
```
브라우저: **http://localhost:8080** (Live Server 포트가 아님)
브라우저: **http://localhost:44082** (Live Server 포트가 아님)
### 방법 B — API만 로컬 실행 (Live Server / Cursor 미리보기 사용 시)
@@ -115,7 +115,7 @@ docker compose up -d --build
확인:
```bash
curl http://localhost:8080/api/v1/health
curl http://localhost:44082/api/v1/health
# → {"ok":true,"version":"4.0.0"}
```
@@ -126,11 +126,14 @@ curl http://localhost:8080/api/v1/health
### 옵션 A — `uadeck-web` 포트만 프록시 (권장)
```
https://uniare.sori.studio → http://NAS_IP:8080
https://uniare.sori.studio → http://NAS_IP:44082
```
`uadeck-web` 컨테이너가 `/api/`와 정적 파일을 모두 처리합니다.
`docker-compose.prod.yml`은 프로젝트 루트 전체를 웹 루트로 열지 않고, 필요한 HTML·정적 폴더만 read-only로 마운트합니다.
따라서 `.env`, `.git`, `api/`, `docs/` 같은 내부 파일은 Nginx에서 직접 노출되지 않습니다.
### 옵션 B — 기존 정적 호스팅 + API만 Docker
1. `docker compose up -d` (API만, 포트 3000)
@@ -171,7 +174,7 @@ docker cp uadeck-api:/data/uadeck.db ./backup/uadeck-$(date +%Y%m%d).db
## 8. 카드/이미지 업데이트
`datas/`, `images/`, HTML 파일은 **볼륨 마운트**이므로:
`datas/`, `images/`, HTML 파일은 **허용된 경로만 read-only 볼륨 마운트**이므로:
1. Git pull (또는 파일 동기화)
2. **재빌드 불필요** — 브라우저 새로고침만으로 반영

View File

@@ -3,7 +3,7 @@ FROM nginx:alpine
COPY nginx/default.conf /etc/nginx/conf.d/default.conf
# 정적 사이트 (docker-compose build context = 프로젝트 루트)
COPY index.html cardList.html result.html login.html signup.html deckHistory.html preset.html admin.html /usr/share/nginx/html/
COPY index.html cardList.html result.html login.html signup.html deckHistory.html preset.html profile.html admin.html favicon.ico /usr/share/nginx/html/
COPY script /usr/share/nginx/html/script
COPY style /usr/share/nginx/html/style
COPY i18n /usr/share/nginx/html/i18n

View File

@@ -7,6 +7,11 @@ server {
client_max_body_size 25m;
# 공유 링크 → 덱 로드 페이지
location ~ ^/d/([A-Za-z0-9]+)$ {
return 302 /cardList.html?d=$1;
}
# API → 백엔드 컨테이너
location /api/ {
proxy_pass http://uadeck-api:3000;

View File

@@ -4,7 +4,7 @@
"tailwindcss": "^4.1.12"
},
"scripts": {
"start": "docker compose -f docker-compose.prod.yml up -d --build && printf '\\n✅ UADECK 실행 중 → http://localhost:8080\\n 종료: npm stop\\n 로그: npm run logs\\n\\n'",
"start": "docker compose -f docker-compose.prod.yml up -d --build && node -e \"console.log('\\nUADECK running: http://localhost:' + (process.env.WEB_PORT || '44082') + '\\nStop: npm stop\\nLogs: npm run logs\\n')\"",
"stop": "docker compose -f docker-compose.prod.yml down",
"logs": "docker compose -f docker-compose.prod.yml logs -f",
"dev": "docker compose -f docker-compose.prod.yml up --build",

View File

@@ -15,7 +15,8 @@ const UaDeckApi = (() => {
const { hostname, port, protocol } = window.location;
const isLocal = hostname === 'localhost' || hostname === '127.0.0.1';
// Live Server 등 정적 전용 포트 → API 컨테이너/프로세스(3000)로 직접 연결
if (isLocal && port && port !== '8080' && port !== '80' && port !== '443' && port !== '') {
const proxiedWebPorts = new Set(['44082', '8080', '80', '443', '']);
if (isLocal && port && !proxiedWebPorts.has(port)) {
return `${protocol}//${hostname}:3000/api/v1`;
}