프로젝트 문서 체계 정비

This commit is contained in:
2026-06-23 13:42:38 +09:00
parent e11b1dc6df
commit 00e05dbe3f
18 changed files with 1344 additions and 682 deletions

View File

@@ -1,195 +1,180 @@
# NAS 배포 가이드 (UNIARE v4)
# UNIARE 실행·배포 가이드
## 1. 사전 준비
필요한 부분만 골라서 읽으면 된다.
- Ugreen NAS에 Docker(또는 Container Manager) 설치
- 프로젝트를 NAS에 클론 또는 동기화
- `Git LFS`로 이미지 자산 pull 완료 확인
1. 내 컴퓨터에서 실행: `1. 로컬 실행`
2. NAS에 처음 설치: `2. NAS 최초 배포`
3. 이미지나 카드 데이터 반영: `3. 정적 파일 업데이트`
4. API나 Docker 설정 반영: `4. 전체 업데이트`
## 2. 환경변수 설정
로컬 주소는 `http://localhost:44082`, 운영 주소는 `https://uniare.sori.studio`이다.
Git 저장소는 `https://git.sori.studio/zenn/union-arena-deck-builder.git`이다.
## 시작 전 확인
- 명령은 프로젝트 폴더에서 실행한다.
- 로컬과 NAS는 각각 다른 `.env`와 DB를 사용한다.
- `.env`, DB, 비밀번호는 Git에 올리지 않는다.
- 카드 이미지는 Git LFS로 관리한다.
- 로컬 위치는 `<LOCAL_PROJECT_DIR>/uniare.sori.studio`, NAS 위치는 `<NAS_PROJECT_DIR>/uniare.sori.studio`로 표기한다.
## 1. 로컬 실행
최초 한 번 프로젝트 폴더에서 `cp .env.example .env`를 실행한다.
`.env`에서 다음 값을 변경한다.
```env
JWT_SECRET=32자_이상의_랜덤_문자열
ADMIN_EMAIL=관리자_이메일
ADMIN_PASSWORD=8자_이상의_비밀번호
WEB_PORT=44082
```
랜덤 문자열은 `openssl rand -hex 32`로 만들 수 있다.
실행은 `npm start`, 종료는 `npm stop`, 로그 확인은 `npm run logs`를 사용한다.
실행 후 브라우저에서 `http://localhost:44082`에 접속한다.
## 2. NAS 최초 배포
NAS에 Git, Git LFS, Docker, Docker Compose가 설치되어 있어야 한다.
저장소를 받는다.
```bash
cd <NAS_PROJECT_DIR>
git clone https://git.sori.studio/zenn/union-arena-deck-builder.git uniare.sori.studio
cd uniare.sori.studio
git lfs pull
cp .env.example .env
```
| 변수 | 설명 |
|---|---|
| `JWT_SECRET` | 32자 이상 랜덤 문자열 (필수) |
| `ADMIN_EMAIL` | 최초 관리자 이메일 |
| `ADMIN_PASSWORD` | 최초 관리자 비밀번호 (8자+) |
| `WEB_PORT` | 웹 노출 포트 (기본 `44082`, 기존 정적 사이트 포트 유지) |
| `CORS_ORIGINS` | 옆 프로젝트 도메인 포함, 쉼표 구분 |
예시:
`.env`에서 다음 값을 운영 환경에 맞게 변경한다.
```env
JWT_SECRET=your-random-secret-at-least-32-chars
ADMIN_EMAIL=admin@uniare.sori.studio
ADMIN_PASSWORD=your-secure-password
JWT_SECRET=32자_이상의_랜덤_문자열
ADMIN_EMAIL=관리자_이메일
ADMIN_PASSWORD=8자_이상의_비밀번호
ADMIN_USERNAME=admin
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
CORS_ORIGINS=https://uniare.sori.studio
```
## 3. 로컬 테스트
Docker 실행: `docker compose -f docker-compose.prod.yml up -d --build`
> **한 줄 요약:** 프로젝트 루트에서 `npm start` → 브라우저 **http://localhost:44082**
상태 확인: `docker compose -f docker-compose.prod.yml ps`
회원가입·로그인·덱 저장은 **API 서버**가 떠 있어야 합니다.
HTML만 Live Server 등으로 열면 저장·로그인이 **405** 로 실패합니다.
API 확인: `curl -fsS http://localhost:44082/api/v1/health`
### 빠른 시작 (권장)
```bash
npm start # 백그라운드 실행 (터미널 점유 안 함)
# 브라우저: http://localhost:44082
npm run logs # 로그 보기 (종료: Ctrl+C)
npm stop # 서버 종료
```
| 명령 | 설명 |
|---|---|
| `npm start` | Docker로 웹+API 백그라운드 기동 (**일상 개발용**) |
| `npm stop` | 서버 종료 |
| `npm run logs` | API·Nginx 로그 스트리밍 |
| `npm run dev` | 포그라운드 실행 (로그 붙어 있음, `Ctrl+C`로 **서버도 종료**) |
| `npm run api:dev` | API만 Node로 실행 (Live Server 병행 시) |
### 저장이 안 될 때
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 또는 **반드시 `:44082` 사용**
### 방법 A — Docker (`npm start`와 동일)
```bash
npm start
```
브라우저: **http://localhost:44082** (Live Server 포트가 아님)
### 방법 B — API만 로컬 실행 (Live Server / Cursor 미리보기 사용 시)
터미널 1 — API:
```bash
cd api && npm install # 최초 1회
cd .. && npm run api:dev
```
터미널 2 — HTML은 Live Server 등으로 열기 (예: `:5500`)
`.env`의 `CORS_ORIGINS`에 Live Server 포트를 추가하세요:
```env
CORS_ORIGINS=...,http://localhost:5500,http://127.0.0.1:5500
```
확인:
```bash
curl http://localhost:3000/api/v1/health
# → {"ok":true,"version":"4.0.0"}
```
### 405 오류가 나올 때
| 원인 | 해결 |
|---|---|
| API 미실행 | `npm start` (또는 `npm run dev`) |
| Live Server로 HTML만 연 경우 | POST `/api/v1/...`가 정적 서버로 가서 405 발생 → 방법 A 또는 B 사용 |
| Docker 미기동 | `docker ps`로 `uniare-api`, `uniare-web` 확인 |
## 4. Docker 기동 (NAS / 프로덕션)
```bash
# 프로덕션 스택 (Nginx + API)
docker compose -f docker-compose.prod.yml up -d --build
# API만 단독 (기존 웹서버가 정적 파일 서빙 중일 때)
docker compose up -d --build
```
확인:
```bash
curl http://localhost:44082/api/v1/health
# → {"ok":true,"version":"4.0.0"}
```
## 5. 기존 리버스 프록시 연동
이미 NAS에서 Nginx Proxy Manager, Traefik, Caddy 등을 쓰는 경우:
### 옵션 A — `uniare-web` 포트만 프록시 (권장)
```
https://uniare.sori.studio → http://NAS_IP:44082
```
`uniare-web` 컨테이너가 `/api/`와 정적 파일을 모두 처리합니다.
`docker-compose.prod.yml`은 프로젝트 루트 전체를 웹 루트로 열지 않고, 필요한 HTML·정적 폴더만 read-only로 마운트합니다.
따라서 `.env`, `.git`, `api/`, `docs/` 같은 내부 파일은 Nginx에서 직접 노출되지 않습니다.
### 옵션 B — 기존 정적 호스팅 + API만 Docker
1. `docker compose up -d` (API만, 포트 3000)
2. 기존 웹서버에 `nginx/uadeck.conf` 규칙 추가:
```nginx
location ~ ^/d/([A-Za-z0-9]+)$ {
return 302 /cardList.html?d=$1;
}
location /api/ {
proxy_pass http://127.0.0.1:3000;
# proxy_set_header ... (uadeck.conf 참고)
}
```
## 6. 최초 관리자 로그인
1. `https://uniare.sori.studio/login.html` 접속
2. `.env`의 `ADMIN_EMAIL` 계정으로 로그인
- API 최초 기동 시 해당 이메일이 없으면 `ADMIN_PASSWORD`로 계정이 자동 생성된다.
- **이미 `ADMIN_EMAIL`로 회원가입한 경우** API 재시작 시 해당 계정이 `admin`으로 승격된다. (가입 시 설정한 비밀번호 그대로 사용)
3. 프로필 메뉴 → **관리자** → `/admin.html`
> 관리자 메뉴가 안 보이면 **로그아웃 후 다시 로그인**하세요. 브라우저에 이전 `role`이 캐시되어 있을 수 있습니다.
## 7. 운영 명령
```bash
# 로그
docker compose -f docker-compose.prod.yml logs -f uniare-api
# 재시작
docker compose -f docker-compose.prod.yml restart
# DB 백업 (볼륨 내 SQLite)
docker cp uniare-api:/data/uadeck.db ./backup/uadeck-$(date +%Y%m%d).db
```
## 8. 카드/이미지 업데이트
`datas/`, `images/`, HTML 파일은 **허용된 경로만 read-only 볼륨 마운트**이므로:
1. Git pull (또는 파일 동기화)
2. **재빌드 불필요** — 브라우저 새로고침만으로 반영
API 컨테이너는 `datas/`를 read-only 마운트하므로 새 시리즈 파일 추가 후 API 재시작만 하면 됩니다:
정상이면 `{"ok":true,"version":"4.0.1"}`이 표시된다.
NAS 리버스 프록시는 `https://uniare.sori.studio`을 `http://<NAS_IP>:44082`로 연결한다.
## 3. 정적 파일 업데이트
이미지, 카드 데이터, HTML, CSS, 프론트 JavaScript는 Docker 이미지를 다시 빌드하지 않아도 된다.
### 이미지만 변경
NAS 프로젝트 폴더에서 `git pull --ff-only`와 `git lfs pull`을 실행한 뒤 브라우저를 강력 새로고침한다. 컨테이너 재시작은 필요 없다.
### `datas/*.js` 변경
NAS 프로젝트 폴더에서 다음 명령을 실행한다.
```bash
git pull --ff-only
git lfs pull
docker compose -f docker-compose.prod.yml restart uniare-api
```
## 9. 트러블슈팅
API가 카드 데이터를 메모리에 저장하므로 `datas/` 변경 시 API만 재시작한다. 이미지와 `datas/*.js`를 함께 변경한 경우도 같은 절차를 사용한다.
| 증상 | 확인 |
|---|---|
| 로그인 안 됨 | `JWT_SECRET` 설정, API health 확인 |
| 공유 링크 404 | Nginx `/d/` 리다이렉트 규칙 |
| CORS 오류 | `CORS_ORIGINS`에 옆 프로젝트 도메인 추가 |
| 덱 카드 메타 누락 | `datas/<series>.js` 존재 여부, API `DATAS_PATH` |
확인할 내용:
- `imgSrc`와 실제 이미지 파일명이 같은가?
- 파일명 대소문자가 같은가?
- 신규 시리즈가 번역 파일 3개에 등록되어 있는가?
- 카드 목록과 저장 덱에서 모두 정상 표시되는가?
### HTML·일반 CSS·프론트 JavaScript 변경
`git pull --ff-only`와 `git lfs pull`을 실행하고 브라우저를 강력 새로고침한다. 컨테이너 재시작은 필요 없다.
## 4. 전체 업데이트
`api/`, `nginx/`, Dockerfile, Compose 설정을 변경했다면 NAS 프로젝트 폴더에서 다음 명령을 실행한다.
```bash
git pull --ff-only
git lfs pull
docker compose -f docker-compose.prod.yml up -d --build
```
상태 확인은 `docker compose -f docker-compose.prod.yml ps`, API 확인은 `curl -fsS http://localhost:44082/api/v1/health`를 사용한다.
`.env`를 변경했다면 `docker compose -f docker-compose.prod.yml up -d --force-recreate`로 컨테이너를 재생성한다.
## 5. CSS 빌드
Tailwind 클래스를 새로 추가했을 때만 로컬에서 `npm install`과 `npm run build:css`를 실행한다.
생성된 `style/output.css`도 Git에 포함한다.
이미지, 카드 데이터, 기존 CSS만 수정했다면 실행하지 않아도 된다.
## 6. 자주 쓰는 운영 명령
- 전체 재시작: `docker compose -f docker-compose.prod.yml restart`
- API만 재시작: `docker compose -f docker-compose.prod.yml restart uniare-api`
- 로그 확인: `docker compose -f docker-compose.prod.yml logs -f`
- 종료: `docker compose -f docker-compose.prod.yml down`
- 다시 실행: `docker compose -f docker-compose.prod.yml up -d`
## 7. 백업
DB는 Docker 볼륨의 `/data/uadeck.db`에 있다.
덱 미리보기와 아바타는 `api/data/previews/`, `api/data/avatars/`에 있다.
DB 백업:
```bash
mkdir -p backup
BACKUP_FILE="uadeck-$(date +%Y%m%d-%H%M%S).db"
docker exec uniare-api node --input-type=module -e "import Database from 'better-sqlite3'; const db = new Database('/data/uadeck.db'); await db.backup('/data/$BACKUP_FILE'); db.close();"
docker cp "uniare-api:/data/$BACKUP_FILE" "./backup/$BACKUP_FILE"
docker exec uniare-api rm "/data/$BACKUP_FILE"
```
미리보기와 아바타 백업: `tar -czf "./backup/uploads-$(date +%Y%m%d-%H%M%S).tar.gz" api/data/previews api/data/avatars`
`.env`는 Git이 아닌 접근 제한된 별도 위치에 보관한다.
## 8. 롤백
문제가 발생하면 DB를 먼저 백업하고 `git log --oneline -10`으로 이전 정상 커밋을 확인한다.
```bash
git checkout <정상_커밋_해시>
git lfs pull
docker compose -f docker-compose.prod.yml up -d --build
```
API 확인: `curl -fsS http://localhost:44082/api/v1/health`
최신 `main`으로 돌아갈 때는 `git checkout main`과 `git pull --ff-only`를 실행한다.
## 9. 문제가 생겼을 때
웹이 열리지 않으면 `docker compose -f docker-compose.prod.yml ps`와 `docker compose -f docker-compose.prod.yml logs --tail=100`을 확인한다.
로그인이나 저장이 안 되면 `curl -fsS http://localhost:44082/api/v1/health`로 API 상태를 확인한다.
카드 이미지가 보이지 않으면 `git lfs pull`을 실행하고 `datas/*.js`의 `imgSrc`와 실제 이미지 파일명을 비교한다.
## 완료 확인
- `uniare-web`, `uniare-api`가 실행 중인가?
- API가 `ok: true`를 반환하는가?
- 홈과 카드 이미지가 열리는가?
- 로그인과 덱 저장이 동작하는가?
- `.env`와 DB가 Git에 포함되지 않았는가?