프로젝트 문서 체계 정비
This commit is contained in:
@@ -143,7 +143,7 @@ const deck = await client.fetchDeck(code);
|
||||
const cards = deck.cards;
|
||||
```
|
||||
|
||||
## 프리셋(메타 덱) 목록
|
||||
## 공개 덱 목록
|
||||
|
||||
```http
|
||||
GET /api/v1/decks/presets
|
||||
@@ -163,7 +163,9 @@ GET /api/v1/decks/presets
|
||||
}
|
||||
```
|
||||
|
||||
프리셋도 동일하게 `GET /decks/:shareCode`로 상세 조회합니다.
|
||||
엔드포인트 이름은 레거시 호환을 위해 `presets`를 유지하지만, 현재 응답 대상은 관리자가 등록한 프리셋이 아니라 사용자가 공개한 미만료 덱입니다.
|
||||
|
||||
상세 조회는 동일하게 `GET /decks/:shareCode`를 사용합니다.
|
||||
|
||||
## 에러 코드
|
||||
|
||||
|
||||
37
docs/changelog.md
Normal file
37
docs/changelog.md
Normal file
@@ -0,0 +1,37 @@
|
||||
# 변경사항
|
||||
|
||||
## v4.0.1
|
||||
|
||||
### 프로젝트 운영 문서 정비
|
||||
|
||||
기능 동작은 변경하지 않고 개발·배포 문서를 현재 서비스 구조에 맞게 정리했습니다.
|
||||
|
||||
로컬 개발과 NAS 운영, 카드 이미지·데이터 업데이트, 백업·복구 절차를 구분해 이후 업데이트를 더 안전하게 진행할 수 있습니다.
|
||||
|
||||
## v4.0.0
|
||||
|
||||
### 회원 계정과 내 덱 관리
|
||||
|
||||
회원가입과 로그인을 지원합니다. 프로필에서 사용자명, 이메일, 프로필 이미지, 비밀번호와 덱 공개 기본값을 관리할 수 있습니다.
|
||||
|
||||
완성한 덱은 결과 화면의 `내 덱으로 저장` 버튼으로 저장하고, `내 덱` 화면에서 이름 변경, 공개 전환, 삭제, 다시 불러오기를 할 수 있습니다.
|
||||
|
||||
### 짧은 공유 링크
|
||||
|
||||
긴 카드 데이터 URL 대신 `/d/공유코드` 형식의 링크를 사용합니다. 공개 덱은 링크를 받은 누구나 불러올 수 있으며 비공개 덱은 소유자와 관리자만 볼 수 있습니다.
|
||||
|
||||
### 공개 덱 탐색과 즐겨찾기
|
||||
|
||||
`공개 덱` 화면에서 다른 사용자가 공개한 덱을 검색하고 시리즈별로 필터링할 수 있습니다. 썸네일을 길게 눌러 크게 보고, 자주 확인하는 덱은 즐겨찾기에 보관할 수 있습니다.
|
||||
|
||||
### 덱 이미지 저장 개선
|
||||
|
||||
저장되는 덱 미리보기 크기를 1200px 기준으로 통일했습니다. 브라우저별로 썸네일 크기가 달라지거나 큰 이미지 저장이 실패하던 문제를 줄였습니다.
|
||||
|
||||
### 다국어 화면 확대
|
||||
|
||||
로그인, 회원가입, 프로필, 내 덱, 공개 덱과 주요 저장 안내를 한국어, 일본어, 영어로 제공합니다.
|
||||
|
||||
### UNIARE 브랜드 적용
|
||||
|
||||
서비스 표시 이름과 헤더 로고를 UNIARE로 통일하고, 페이지 이동 시 헤더와 본문 너비가 안정적으로 유지되도록 개선했습니다.
|
||||
@@ -1,61 +1,71 @@
|
||||
# Codex Handoff Guide
|
||||
# 작업 인계 가이드
|
||||
|
||||
## 목적
|
||||
- 이 문서는 다른 작업 환경이나 다른 Codex 세션에서 프로젝트를 이어받더라도 같은 품질 기준으로 작업하기 위한 운영 기준서다.
|
||||
- 특히 이 저장소는 정적 HTML/JS 프로젝트이면서 카드 데이터와 이미지 자산 비중이 크기 때문에, "코드 수정"보다 "데이터-번역-이미지 일치"가 더 중요하다.
|
||||
## Handoff(핸드오프)란?
|
||||
다른 개발자나 운영 담당자가 지금 작업을 이어받을 때 빠르게 상황을 이해하도록 정리한 문서입니다. 비개발자에게는 '작업 인수인계 노트'라고 생각하면 됩니다.
|
||||
|
||||
## 현재 기준 릴리즈
|
||||
- 현재 작업 기준 버전: `v4.0.0`
|
||||
- 이번 릴리즈 핵심 반영:
|
||||
- 회원·인증·덱 API (`api/`, Docker)
|
||||
- 공유 코드 기반 덱 공유 (`/d/:code`, `GET /api/v1/decks/:shareCode`)
|
||||
- 관리자 화면 (`admin.html`)
|
||||
- `?cards=` URL 방식 폐기
|
||||
## 우선 확인
|
||||
|
||||
## 다음 작업자에게 반드시 전달할 것
|
||||
- 이 프로젝트는 빌드 시스템 중심 앱이 아니라 `index.html`, `cardList.html`, `result.html` 3개 페이지가 직접 동작하는 정적 사이트다.
|
||||
- 신규 시리즈를 추가할 때는 항상 아래 3가지를 한 세트로 본다.
|
||||
- `datas/<series>.js`
|
||||
- `images/<series>/`
|
||||
- `i18n/translations.ko.json`, `i18n/translations.ja.json`, `i18n/translations.en.json`
|
||||
- 이미지 자산은 `images/**` 전체가 `Git LFS` 대상이다. 대용량 이미지가 일반 Git 객체로 들어가지 않게 주의한다.
|
||||
- `cardList.html`은 `?series=<seriesKey>` 값을 기준으로 `./datas/${seriesKey}.js`를 동적 import 한다. 즉, 시리즈 키 불일치가 있으면 화면에서 바로 깨진다.
|
||||
- 카드 이미지 렌더링은 최종적으로 `images/${seriesKey}/${fileName}` 규칙을 사용하므로, `imgSrc`에는 파일명 기준 일관성이 중요하다.
|
||||
- 릴리즈 버전이 바뀌면 최소한 아래 두 곳은 함께 맞춘다.
|
||||
- `index.html`의 `<title>` 및 상단 배지
|
||||
- `docs/spec.md`, `docs/update.md`, 필요 시 `docs/history.md`
|
||||
1. 프로젝트 루트 `AGENTS.md`
|
||||
2. `docs/spec.md`
|
||||
3. `docs/todo.md`
|
||||
4. `docs/history.md`
|
||||
5. `docs/convention.md`
|
||||
6. `docs/map.md`
|
||||
7. `docs/deploy.md`
|
||||
|
||||
## 품질 기준
|
||||
- 신규 시리즈 반영 시 데이터 파일만 추가하고 번역 키를 빼먹지 않는다.
|
||||
- 카드 번호(`Number`) 그룹과 실제 파일명 규칙이 시리즈 키와 일치해야 한다.
|
||||
- 병렬 카드(`parallel: true`)는 일반 카드와 같은 카드 번호를 공유하되 이미지 파일만 다르게 유지한다.
|
||||
- `trigger`는 `null`, `COLOR`, `FINAL`, `SPECIAL`만 사용한다.
|
||||
- AP 카드의 `color`는 `AP`를 사용한다.
|
||||
- 릴리즈 전에 `git diff --stat`으로 변경 범위를 보고, 데이터/번역/이미지 중 빠진 축이 없는지 확인한다.
|
||||
현재 버전은 루트 `VERSION`을 기준으로 확인한다.
|
||||
|
||||
## 작업 절차
|
||||
1. 변경 요청이 신규 시리즈 추가인지, 기존 시리즈 수정인지 먼저 구분한다.
|
||||
2. 관련 `datas/*.js`와 `images/<series>/` 상태를 확인한다.
|
||||
3. `i18n/translations.*.json`에 시리즈명 또는 UI 문구 변경이 필요한지 확인한다.
|
||||
4. 화면에서 버전 표기를 바꿔야 하는 성격의 변경인지 판단한다.
|
||||
5. `docs/update.md`에 이번 반영 내용을 남긴다.
|
||||
6. 릴리즈성 변경이면 `docs/history.md` 또는 이 문서에도 기준 변화를 남긴다.
|
||||
7. 커밋 전 `git status`, `git diff --stat`, 필요한 경우 간단한 구문 체크를 수행한다.
|
||||
## 프로젝트 핵심
|
||||
|
||||
## 릴리즈 전 체크리스트
|
||||
- `datas/<series>.js`가 실제로 import 가능한 형식인지 확인
|
||||
- `images/<series>/` 파일이 데이터의 `imgSrc`와 대응되는지 확인
|
||||
- `translations.ko/ja/en`에 시리즈 키가 모두 존재하는지 확인
|
||||
- 버전 표기가 문서와 UI에서 일치하는지 확인
|
||||
- `images/**`가 계속 LFS로 잡히는지 확인
|
||||
- 커밋 메시지에 릴리즈 의도가 드러나는지 확인
|
||||
- 정적 HTML·JavaScript 프론트와 Express·SQLite API를 함께 사용한다.
|
||||
- 운영은 `docker-compose.prod.yml`의 `uniare-web`, `uniare-api` 구성이 기본이다.
|
||||
- 덱 공유는 `/d/<shareCode>`와 `GET /api/v1/decks/:shareCode`를 사용한다.
|
||||
- `?cards=<JSON>` 공유 방식은 사용하지 않는다.
|
||||
- 표시 브랜드는 UNIARE지만 `UaDeckApi`, `uadeck_token`, `uadeck.db` 등 내부 식별자는 호환을 위해 유지한다.
|
||||
|
||||
## 커밋 메시지 가이드
|
||||
- 릴리즈 커밋 예시: `v3.1.0: add ua51 series and handoff docs`
|
||||
- 수정 커밋 예시: `fix: add missing ua49 parallel images`
|
||||
- 되돌림 커밋 예시: `revert: restore original card list interactions`
|
||||
## 카드 데이터 작업
|
||||
|
||||
## 주의할 점
|
||||
- 이 저장소는 이미지 비중이 매우 크다. 원격 업로드 전에 대용량 변경이 의도된 것인지 다시 확인한다.
|
||||
- 현재 문서 체계는 `docs/update.md`가 작업 로그, `docs/history.md`가 의사결정, `docs/spec.md`가 구조 명세 역할을 맡는다.
|
||||
- 후속 작업자가 빠르게 맥락을 잡게 하려면, "무엇을 바꿨는지"보다 "왜 같이 바꿔야 하는지"를 문서에 남기는 편이 더 중요하다.
|
||||
신규 시리즈 또는 카드는 아래 항목을 한 세트로 확인한다.
|
||||
|
||||
```text
|
||||
datas/<series>.js
|
||||
images/<series>/
|
||||
i18n/translations.ko.json
|
||||
i18n/translations.ja.json
|
||||
i18n/translations.en.json
|
||||
```
|
||||
|
||||
- `imgSrc`와 실제 이미지 파일명이 일치해야 한다.
|
||||
- 동일 카드의 패러렐 판본은 같은 `Number`를 사용한다.
|
||||
- `images/**`는 Git LFS 대상이다.
|
||||
- `datas/` 변경 후 API 카드 캐시를 갱신하려면 `uniare-api`를 재시작한다.
|
||||
|
||||
## 변경 후 문서
|
||||
|
||||
- 현재 구현 변경: `spec.md`
|
||||
- 이번 작업 기록: `update.md`
|
||||
- 결정 이유: `history.md`
|
||||
- 남은 작업: `todo.md`
|
||||
- 사용자 공개 변경: `changelog.md`
|
||||
- 파일 역할 변경: `map.md`
|
||||
- 배포 절차 변경: `deploy.md`
|
||||
|
||||
## 검증
|
||||
|
||||
```bash
|
||||
git diff --check
|
||||
git status --short
|
||||
```
|
||||
|
||||
변경 성격에 따라 다음도 확인한다.
|
||||
|
||||
- HTML·정적 자산: 브라우저 새로고침
|
||||
- `datas/`: API 재시작 후 카드 목록과 저장 덱 조회
|
||||
- API 코드: Docker 재빌드와 `/api/v1/health`
|
||||
- Tailwind 클래스: `npm run build:css`
|
||||
|
||||
## 커밋
|
||||
|
||||
- 커밋 메시지는 한국어로 작성한다.
|
||||
- 커밋하는 완료 작업은 `VERSION`을 증가시킨다.
|
||||
- 민감 정보와 `api/data/` 운영 파일을 커밋하지 않는다.
|
||||
|
||||
@@ -1,46 +1,66 @@
|
||||
# 코딩 컨벤션
|
||||
# 프로젝트 컨벤션
|
||||
|
||||
## 기본 원칙
|
||||
- 모든 문서와 커뮤니케이션은 한국어 기준으로 유지한다.
|
||||
- 코드 주석은 필요 시 `JSDoc` 형식을 우선한다.
|
||||
- 기존 정적 사이트 구조를 존중하고, 대규모 구조 변경 시에는 먼저 `docs/history.md`에 의사결정을 기록한다.
|
||||
## 공통
|
||||
|
||||
## 파일 및 폴더 규칙
|
||||
- 시리즈 데이터 파일은 `datas/uaXX.js`, `datas/uapr.js`처럼 시리즈 키와 일치하게 관리한다.
|
||||
- 이미지 폴더는 `images/<seriesKey>/` 규칙을 유지한다.
|
||||
- 새 문서는 `docs/` 하위에서 목적별 단일 파일에 누적 관리한다.
|
||||
- 모든 문서와 커뮤니케이션은 한국어로 작성한다.
|
||||
- 실제 코드와 문서가 다르면 코드를 기준으로 문서를 갱신한다.
|
||||
- 요청 범위를 넘는 대규모 리팩터링을 하지 않는다.
|
||||
- 운영 URL, 비밀번호, 토큰, DB 정보는 코드에 하드코딩하지 않는다.
|
||||
- JavaScript 함수와 클래스에는 필요한 JSDoc을 작성한다.
|
||||
|
||||
## JavaScript 규칙
|
||||
- 기존 코드 스타일에 맞춰 기본 들여쓰기는 4칸을 사용한다.
|
||||
- 상수는 `const`, 재할당 변수는 `let`을 사용한다.
|
||||
- DOM 참조가 반복될 경우 `getEl()`처럼 묶어서 관리하는 패턴을 우선 고려한다.
|
||||
- URL 파라미터, `localStorage`, 동적 import는 예외 처리와 함께 사용한다.
|
||||
- 카드 데이터 객체의 필드명은 기존 호환성을 위해 현재 형태(`imgSrc`, `Number`, `count`, `key`)를 유지한다.
|
||||
## 버전
|
||||
|
||||
## HTML 규칙
|
||||
- 정적 페이지는 의미 단위별로 `header`, `main`, `aside` 등을 우선 사용한다.
|
||||
- 페이지별 스크립트가 길어질 경우 신규 기능은 별도 JS 파일 분리 여부를 먼저 검토한다.
|
||||
- 인라인 이벤트(`onclick`)는 기존 파일에 이미 존재하므로 즉시 제거 대상은 아니지만, 신규 대형 기능은 `addEventListener` 방식 우선 검토한다.
|
||||
|
||||
## CSS 규칙
|
||||
- 공통 UI는 가능한 한 Tailwind 유틸리티 클래스를 우선 사용한다.
|
||||
- **헤더 로고**는 `style/brand.css`의 `.app-brand-logo` + `--brand-logo-height`(32px)만 사용한다. 페이지마다 `h-8` 등 Tailwind로 크기를 주지 않는다.
|
||||
- `style/output.css`는 **빌드 산출물**이다. HTML 등에 새 Tailwind 클래스를 추가한 뒤 `npm run build:css`로 재생성한다.
|
||||
- `style/output.css`에 포함되지 않은 유틸리티 클래스는 적용되지 않으므로, 빌드 전까지는 페이지 특화 스타일을 인라인 `<style>` 또는 전용 CSS 파일(`style/auth-pages.css` 등)에 명시한다.
|
||||
- 페이지 특화 보정 스타일만 인라인 `<style>` 또는 전용 CSS 파일에 둔다.
|
||||
- 캡처 안정성처럼 이유가 분명한 예외 스타일은 주석으로 목적을 남긴다.
|
||||
|
||||
## 데이터 관리 규칙
|
||||
- 카드 데이터 추가 시 최소 필드는 `imgSrc`, `Number`, `count`, `key`, `color`, `parallel`, `rarity`, `trigger`를 맞춘다.
|
||||
- 동일 카드의 패러렐 판본은 동일 `Number`를 유지하고 `parallel` 값으로 구분한다.
|
||||
- 파일명 변경 시 `datas/*.js`와 `images/<seriesKey>/`가 함께 일치하는지 확인한다.
|
||||
|
||||
## Git 규칙
|
||||
- 현재 버전은 프로젝트 루트 `VERSION`을 기준으로 한다.
|
||||
- 커밋을 포함하는 완료 작업은 버전을 증가시키고 `VERSION`, `docs/update.md`, 필요 시 `docs/changelog.md`를 함께 갱신한다.
|
||||
- 커밋 메시지는 한국어로 작성한다.
|
||||
- 버전 상승 작업은 `docs/update.md` 반영 후 태그명과 맞춘다.
|
||||
- 민감 정보, 로컬 경로, 개인 식별 정보가 커밋되지 않도록 확인한다.
|
||||
|
||||
## 이미지 자산 규칙
|
||||
- `images/**`는 `Git LFS`로 관리한다.
|
||||
- 이미지 파일을 새로 추가하거나 교체할 때는 일반 Git 객체로 들어가지 않도록 `.gitattributes` 설정을 유지한다.
|
||||
- 대량 이미지 추가 전에는 원격 저장소의 LFS 정책과 비용 한도를 확인한다.
|
||||
## 카드 데이터
|
||||
|
||||
- 시리즈 파일은 `datas/uaXX.js` 또는 `datas/uapr.js` 형식을 사용한다.
|
||||
- 카드 객체의 호환 필드 `imgSrc`, `Number`, `count`, `key` 이름은 변경하지 않는다.
|
||||
- 동일 카드의 패러렐 판본은 같은 `Number`를 사용하고 `parallel`로 구분한다.
|
||||
- 신규 시리즈는 데이터, 이미지, 한국어·일본어·영어 번역을 한 작업 단위로 검증한다.
|
||||
- 이미지 파일명 변경 시 관련 `datas/*.js`의 `imgSrc`도 함께 갱신한다.
|
||||
|
||||
## 프론트
|
||||
|
||||
- 현재 정적 HTML/JavaScript 구조를 유지한다.
|
||||
- 신규 대형 기능은 페이지 인라인 스크립트보다 `script/` 모듈 분리를 우선 검토한다.
|
||||
- URL 파라미터, 동적 import, `localStorage` 접근에는 실패 처리를 둔다.
|
||||
- 기존 인라인 이벤트는 즉시 전면 교체하지 않으며 신규 기능은 `addEventListener`를 우선한다.
|
||||
|
||||
## 스타일
|
||||
|
||||
- 현재 CSS를 Tailwind로 일괄 전환하지 않는다.
|
||||
- 새 단순 스타일은 Tailwind 유틸리티를 우선 검토하되 기존 페이지 패턴과 빌드 여부를 함께 확인한다.
|
||||
- 반복되는 공통 스타일은 공통 CSS 또는 컴포넌트 역할의 클래스로 분리한다.
|
||||
- `style/output.css`를 직접 편집하지 않고 `npm run build:css`로 생성한다.
|
||||
- 헤더 로고는 `style/brand.css`의 `.app-brand-logo`만 사용한다.
|
||||
- 다크모드와 Pretendard 계열 기본 글꼴을 고려한다.
|
||||
|
||||
## API·DB
|
||||
|
||||
- API 베이스 경로는 `/api/v1`을 유지한다.
|
||||
- 운영 설정은 `.env`로 관리하며 `.env.example`에는 예시값만 둔다.
|
||||
- 로컬 DB와 운영 DB를 같은 파일 또는 볼륨으로 공유하지 않는다.
|
||||
- DB에는 최소 카드 정보만 저장하고 카드 메타는 `datas/`에서 합성한다.
|
||||
- 권한 검사는 화면뿐 아니라 API에서도 수행한다.
|
||||
|
||||
## 이미지·Git
|
||||
|
||||
- `images/**`는 Git LFS로 관리한다.
|
||||
- `api/data/`의 DB, 아바타, 덱 미리보기는 커밋하지 않는다.
|
||||
- 대량 이미지 추가 전 변경 수와 LFS 추적 여부를 확인한다.
|
||||
- 민감 정보와 개인 로컬 경로가 diff에 포함되지 않았는지 확인한다.
|
||||
|
||||
## 문서
|
||||
|
||||
- `spec.md`: 현재 구현 상태
|
||||
- `update.md`: 개발자 작업 로그
|
||||
- `history.md`: 유지할 의사결정과 이유
|
||||
- `todo.md`: 미완료 작업만
|
||||
- `deploy.md`: 실행·배포·롤백
|
||||
- `widget.md`: 외부 서비스 연동
|
||||
- `map.md`: 파일과 기능 연결
|
||||
- `changelog.md`: 사용자 공개 변경사항
|
||||
- `convention.md`: 반복 적용 규칙
|
||||
|
||||
331
docs/deploy.md
331
docs/deploy.md
@@ -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에 포함되지 않았는가?
|
||||
|
||||
185
docs/history.md
185
docs/history.md
@@ -1,82 +1,121 @@
|
||||
# 의사결정 이력
|
||||
|
||||
## 2026-06-22 / NAS 운영 네이밍 정리 (`uniare-*`)
|
||||
- 배경: NAS에 기존 `uniare-web` 컨테이너와 신규 `uadeck-web` 컨테이너가 공존하면서 포트 충돌 및 식별 혼선이 발생했다.
|
||||
- 결정:
|
||||
- Docker Compose 서비스/컨테이너 명칭을 프로젝트 식별자 기준 `uniare-*`로 통일한다.
|
||||
- Nginx 내부 프록시 대상도 서비스명 변경에 맞춰 `uniare-api`로 함께 정렬한다.
|
||||
- 이유:
|
||||
- 운영 시 `docker ps`, 로그 확인, 재시작 명령에서 컨테이너 목적을 즉시 식별하기 위함.
|
||||
- 기존 운영 스택(`uniare`)과 명칭 일관성을 맞춰 재배포 실수를 줄이기 위함.
|
||||
## 2026-06-23 v4.0.0 — SQLite 백업은 온라인 백업 API를 사용
|
||||
|
||||
## 2026-06-19 / v4.0.0 구현 완료
|
||||
- 배경: 설계 확정 후 회원·API·공유 시스템 1차 구현.
|
||||
- 반영: `api/` 백엔드, Docker Compose, 인증/덱/관리자 API, 프론트 신규 페이지 및 헤더 프로필 UI.
|
||||
- 배포: NAS Docker + Nginx `/api/`, `/d/` 프록시 필요.
|
||||
💡 WAL 모드에서 DB 본파일만 복사하면 아직 병합되지 않은 최신 트랜잭션이 누락될 수 있기 때문.
|
||||
🔧 운영 중에는 `better-sqlite3`의 `backup()`으로 일관된 백업본을 만든 뒤 컨테이너 밖으로 복사.
|
||||
|
||||
## 2026-06-19 / v4.0.0 덱 공유·API 설계 확정
|
||||
- 배경: v3는 덱을 `?cards=<JSON>` URL에 실어 공유했으나 URL이 수천 자로 비대해지고, 옆 프로젝트도 개발 중이라 계약을 새로 잡을 수 있는 시점이다.
|
||||
- 결정:
|
||||
- URL 쿼리 기반 덱 공유는 **폐기**하고, 공유 코드 + REST API 방식으로 전환한다.
|
||||
- **개인 덱**(`share_type=user`): 저장할 때마다 nanoid 8자 고유 코드 발급. 같은 카드 구성이라도 저장자마다 URL이 다르다.
|
||||
- **메타 덱·프리셋**(`share_type=preset`): 카드 구성 해시 기반 고정 코드. 동일 구성이면 항상 같은 URL.
|
||||
- DB에는 `{number, count, key}`만 저장하고, API 응답 시 `datas/`에서 카드 메타를 lookup하여 합성한다.
|
||||
- 작성자, 생성일, 수정일, 조회수, 공개 여부 등 메타데이터를 덱 레코드와 함께 관리한다.
|
||||
- 외부 연동(옆 프로젝트) 공식 계약: `GET /api/v1/decks/:shareCode`
|
||||
- 프론트는 Vue 전환 없이 정적 HTML + API 호출 + Docker 백엔드로 확장한다.
|
||||
- 이유:
|
||||
- URL 길이·중복 데이터 문제 해소.
|
||||
- 회원 덱 소유권·수정·비공개 설정이 자연스럽다.
|
||||
- DB 용량은 덱 본문 기준 건당 수 KB 수준으로 경미하다.
|
||||
## 2026-06-23 v4.0.0 — 헤더 로고 규격을 공통 스타일로 관리
|
||||
|
||||
## 2026-04-19 / v3.1.0 릴리즈 기준 확정
|
||||
- 배경: 저장소의 마지막 명시 릴리즈 기록은 `v3.0.1`인데, 메인 화면은 이미 `v3.1`을 노출하고 있어 실제 릴리즈 이력과 화면 표기가 어긋나 있었다.
|
||||
- 결정:
|
||||
- 이번 반영분을 `v3.1.0`으로 정의하고, 화면 표기와 문서 표기를 같은 버전으로 통일한다.
|
||||
- 이후 버전 증가는 `docs/update.md`에 먼저 기록한 뒤 화면 표기와 함께 반영한다.
|
||||
- 이유:
|
||||
- 외부 협업자나 후속 Codex 작업자가 "현재 배포 기준이 무엇인지" 즉시 판단할 수 있어야 한다.
|
||||
- 자산 중심 정적 프로젝트 특성상 코드보다 데이터/이미지 변경이 잦기 때문에, 명시 릴리즈 기준이 없으면 누락 여부를 추적하기 어렵다.
|
||||
💡 페이지마다 로고 크기를 따로 지정하면 화면 이동 시 높이가 달라지고 수정 지점도 늘어나기 때문.
|
||||
🔧 `style/brand.css`의 `.app-brand-logo`와 `--brand-logo-height`를 모든 헤더의 단일 기준으로 채택.
|
||||
|
||||
## 2026-03-30 / 문서 체계 초기화
|
||||
- 배경: 프로젝트 루트에 AI 작업 규칙은 있었지만 `docs/` 실체가 없어 규칙과 실제 운영 상태가 분리되어 있었다.
|
||||
- 결정: 규칙 문서가 요구하는 핵심 문서 6종을 먼저 생성하고, 이후 변경 시 이 파일들을 기준 문서로 유지한다.
|
||||
- 기대 효과: 작업 내역, 기술 구조, 화면-파일 연결, 향후 할 일을 한곳에서 추적할 수 있다.
|
||||
## 2026-06-23 v4.0.0 — 표시 브랜드는 UNIARE, 내부 식별자는 호환 유지
|
||||
|
||||
## 2026-03-30 / 이미지 관리 방식 1차 판단
|
||||
- 배경: `images/` 디렉터리 용량이 약 `1.7G`, 파일 수가 `9,370개` 수준으로 증가했다.
|
||||
- 관찰:
|
||||
- 개별 파일이 아주 크지 않아도 Git 일반 객체로 누적되면 clone 및 fetch 비용이 계속 증가한다.
|
||||
- 이미지 추가가 잦은 프로젝트 특성상 저장소 히스토리 비대화가 반복될 가능성이 높다.
|
||||
- 결정:
|
||||
- 신규 이미지 유입 관리 목적이라면 `Git LFS` 도입을 권장한다.
|
||||
- 단, 이미 커진 저장소를 즉시 가볍게 만드는 해결책으로 `Git LFS`만 기대해서는 안 된다.
|
||||
- 히스토리 크기까지 줄여야 하면 `git lfs migrate` 또는 자산 저장소 분리/CDN 이전을 별도 의사결정으로 다룬다.
|
||||
- 보류 사유:
|
||||
- 원격 저장소의 LFS 지원 여부, 무료 용량, 대역폭 정책을 아직 확인하지 않았다.
|
||||
- 기존 협업자들의 개발 환경에 `git lfs` 설치를 요구하게 되므로 도입 공지가 필요하다.
|
||||
💡 사용자에게 보이는 제품명은 통일하되 기존 API 클라이언트와 저장 데이터의 호환성은 깨지 않기 위함.
|
||||
🔧 화면·문서·다운로드 파일명은 `UNIARE`로 변경하고 `uadeck_token`, `uadeck.db`, `UaDeckApi`, `/api/v1`은 유지.
|
||||
|
||||
## 2026-03-30 / 이미지 히스토리 전체 LFS 마이그레이션 결정
|
||||
- 배경: 목적이 "앞으로 예방"이 아니라 "기존 저장소 용량 절감"으로 명확해졌다.
|
||||
- 확인:
|
||||
- 로컬 환경에 `git lfs`가 설치되어 있다.
|
||||
- 원격 저장소에 LFS 엔드포인트가 설정되어 있다.
|
||||
- 결정:
|
||||
- `images/**` 전체 히스토리를 `Git LFS`로 마이그레이션한다.
|
||||
- 현재 작업 중인 `ua27` 데이터 및 이미지 추가분도 함께 반영한다.
|
||||
- 영향:
|
||||
- 기존 커밋 해시가 모두 재작성된다.
|
||||
- 원격 반영 시 강제 푸시가 필요하다.
|
||||
- 협업 중인 다른 클론은 재동기화 절차가 필요하다.
|
||||
## 2026-06-23 v4.0.0 — 주요 화면 너비를 1280px로 통일
|
||||
|
||||
## 2026-03-30 / 새 루트 커밋 기준 저장소 재시작 결정
|
||||
- 배경: `images/**`는 LFS 포인터로 전환됐지만, 저장소 자체는 과거 Git 객체 영향으로 여전히 비대했다.
|
||||
- 결정:
|
||||
- 현재 로컬 작업본을 단일 기준 스냅샷으로 보고 새 루트 커밋을 만든다.
|
||||
- 원격 `main`은 새 루트 커밋으로 교체한다.
|
||||
- 기존 히스토리는 로컬 백업 브랜치에서만 보존한다.
|
||||
- 새 버전 체계는 `v3.0.0`부터 다시 시작한다.
|
||||
- 이미지 자산은 새 시작 이후에도 `Git LFS` 정책을 유지한다.
|
||||
- 이유:
|
||||
- 가장 확실하게 Git 일반 히스토리 용량을 줄일 수 있다.
|
||||
- 추가적인 서버 GC 상태나 숨은 과거 객체를 더 추적하지 않고도 정리 목적을 달성할 수 있다.
|
||||
💡 페이지 이동 때 헤더와 본문 폭이 달라지는 시각적 흔들림을 줄이고 광고 레일을 안정적으로 배치하기 위함.
|
||||
🔧 홈·편집·결과·프로필·덱 목록의 공통 최대 너비를 `80rem` 기준으로 정렬.
|
||||
|
||||
## 2026-06-22 v4.0.0 — Docker 서비스 이름을 `uniare-*`로 통일
|
||||
|
||||
💡 NAS에서 구·신규 컨테이너가 혼재할 때 포트 충돌과 운영 명령 실수를 줄이기 위함.
|
||||
🔧 Compose 서비스·컨테이너·볼륨과 Nginx 프록시 대상을 `uniare-web`, `uniare-api`, `uniare-data`로 정렬.
|
||||
|
||||
## 2026-06-22 v4.0.0 — 운영 웹 컨테이너는 허용 자산만 읽기 전용 공개
|
||||
|
||||
💡 프로젝트 루트 전체를 웹 루트로 노출하면 `.env`, `.git`, `docs`, API 소스가 외부에 노출될 수 있기 때문.
|
||||
🔧 HTML과 `script`, `style`, `i18n`, `datas`, `images`만 Nginx에 read-only 볼륨으로 연결.
|
||||
|
||||
## 2026-06-22 v4.0.0 — 공개 덱은 사용자 공개 설정을 기준으로 구성
|
||||
|
||||
💡 관리자 전용 프리셋보다 사용자가 직접 공개한 덱을 탐색하는 방식이 서비스 흐름에 더 자연스럽기 때문.
|
||||
🔧 `/preset.html`과 `GET /api/v1/decks/presets`는 만료되지 않은 공개 사용자 덱을 최신순으로 제공.
|
||||
|
||||
## 2026-06-22 v4.0.0 — 덱 미리보기는 1200px PNG로 정규화
|
||||
|
||||
💡 브라우저와 옵션에 따라 저장 썸네일 크기가 달라지고 큰 base64 요청이 413 오류를 일으켰기 때문.
|
||||
🔧 저장 이미지는 1200px 기준 PNG로 만들고 Nginx 요청 한도 25MB, Express JSON 한도 20MB를 적용.
|
||||
|
||||
## 2026-06-22 v4.0.0 — 즐겨찾기는 브라우저 로컬 상태로 우선 제공
|
||||
|
||||
💡 서버 스키마를 늘리지 않고도 공개 덱과 내 덱 탐색 편의를 빠르게 제공하기 위함.
|
||||
🔧 공유 코드를 키로 `localStorage`에 저장하며 계정 간 동기화는 후속 과제로 남김.
|
||||
|
||||
## 2026-06-22 v4.0.0 — 정적 프론트를 유지하며 회원·덱 API를 추가
|
||||
|
||||
💡 기존 카드 데이터와 화면을 전면 재작성하지 않고 로그인, 소유권, 공유 기능을 확장하기 위함.
|
||||
🔧 정적 HTML/JavaScript는 유지하고 Express·SQLite API와 Docker Compose 운영 구성을 추가.
|
||||
|
||||
## 2026-06-22 v4.0.0 — 덱 공유는 공유 코드와 REST API를 사용
|
||||
|
||||
💡 `?cards=<JSON>` 방식의 긴 URL과 중복 데이터를 없애고 공개 범위·작성자·수정 권한을 관리하기 위함.
|
||||
🔧 DB에는 `{number,count,key}`만 저장하고 `GET /api/v1/decks/:shareCode` 응답에서 카드 메타를 합성.
|
||||
|
||||
## 2026-06-22 v4.0.0 — 회원 덱과 비회원 공유의 보존 기간을 분리
|
||||
|
||||
💡 계정 소유 덱은 영구 관리하고 익명 공유 데이터는 무기한 누적되지 않게 하기 위함.
|
||||
🔧 회원 덱은 만료 없이 저장하고 비회원 덱은 공개 상태로 30일 뒤 만료되도록 구성.
|
||||
|
||||
## 2026-06-22 v4.0.0 — 프로필 이미지는 클라이언트에서 PNG로 정규화
|
||||
|
||||
💡 JPEG 등 원본 바이트를 `.png` 확장자로 저장해 브라우저 표시가 실패하는 문제를 막기 위함.
|
||||
🔧 업로드 전에 Canvas로 PNG 변환·리사이즈하고 `updatedAt` 쿼리로 캐시를 갱신.
|
||||
|
||||
## 2026-06-17 v3.1.2 — 유니티켓은 기존 시리즈 데이터에 병합
|
||||
|
||||
💡 별도 시리즈를 만들기보다 동일 작품의 카드 번호 체계와 필터 흐름을 유지하는 편이 검색과 덱 구성이 일관되기 때문.
|
||||
🔧 `ua03`, `ua06`, `ua10` 데이터와 이미지에 헌터×헌터·테일즈 오브 어라이즈·히로아카 유니티켓을 추가.
|
||||
|
||||
## 2026-06-15 v3.1.2 — 후속 부스터도 기존 시리즈 키를 재사용
|
||||
|
||||
💡 작품 단위 진입 구조를 유지하면서 신규 부스터와 기존 카드를 한 화면에서 다루기 위함.
|
||||
🔧 마크로스 2nd는 `ua36`에 병합하고 무직전생 1st는 신규 `ua54` 데이터·이미지·번역 세트로 추가.
|
||||
|
||||
## 2026-05-26 v3.1.2 — 신규 시리즈 반영은 데이터·이미지·번역을 한 세트로 관리
|
||||
|
||||
💡 세 축 중 하나라도 빠지면 홈 노출, 카드 로딩, 이미지 표시 중 일부가 즉시 깨지기 때문.
|
||||
🔧 `ua50`, `ua53` 추가와 `ua52` 병렬 이미지 보강을 같은 릴리즈 단위로 반영.
|
||||
|
||||
## 2026-05-16 v3.1.1 — 신규 시리즈와 기존 누락 자산을 함께 보강
|
||||
|
||||
💡 새 시리즈 배포 시 인접 시리즈의 확인된 누락 이미지도 함께 정리해 데이터 신뢰도를 유지하기 위함.
|
||||
🔧 `ua52` 데이터·이미지·3개 언어명을 추가하고 `ua51` 병렬 이미지 누락분을 보강.
|
||||
|
||||
## 2026-04-19 v3.1.0 — 릴리즈 기준과 작업 인계 문서를 명시
|
||||
|
||||
💡 화면 버전과 실제 변경 이력이 어긋나 후속 작업자가 현재 배포 기준을 판단하기 어려웠기 때문.
|
||||
🔧 `ua51` 릴리즈를 `v3.1.0`으로 정의하고 데이터·이미지·번역 검증 절차를 문서화.
|
||||
|
||||
## 2026-04-08 v3.0.1 — 카드 목록 상호작용은 기존 동작을 우선 보존
|
||||
|
||||
💡 구조 개선 과정에서 수량 조절과 카드 선택 UX가 달라져 기존 사용 흐름이 훼손되었기 때문.
|
||||
🔧 변경된 목록 상호작용을 되돌리고 이후 UI 수정은 기존 동작 검증을 선행하도록 기준화.
|
||||
|
||||
## 2026-04-08 v3.0.1 — 카드 번호와 덱 집계는 동일 식별자를 기준으로 처리
|
||||
|
||||
💡 잘못된 카드 번호 하나가 패러렐 묶음과 덱 수량 계산까지 연쇄적으로 어긋나게 하기 때문.
|
||||
🔧 `ua27` 카드 번호를 정정하고 동일 `Number` 그룹의 수량 표시를 다시 동기화.
|
||||
|
||||
## 2026-03-30 v3.0.1 — 데이터 경로는 실제 이미지 파일명을 기준으로 정합성 유지
|
||||
|
||||
💡 데이터의 `imgSrc`와 저장 파일명이 다르면 카드가 존재해도 화면에서는 누락으로 보이기 때문.
|
||||
🔧 오기입 경로를 수정하고 누락 이미지 추가와 사용하지 않는 주석 데이터 제거를 함께 수행.
|
||||
|
||||
## 2026-03-30 v3.0.0 — 저장소를 현재 스냅샷 기준 새 루트로 재시작
|
||||
|
||||
💡 과거 대용량 이미지 객체로 비대해진 Git 이력을 가장 확실하게 줄이기 위함.
|
||||
🔧 현재 작업본을 새 루트 커밋으로 삼고 버전은 `v3.0.0`부터 재시작하며 기존 이력은 로컬 백업 브랜치에 보존.
|
||||
|
||||
## 2026-03-30 v3.0.0 — 이미지 자산은 Git LFS로 관리
|
||||
|
||||
💡 수천 개 카드 이미지가 일반 Git 객체로 누적되는 비용을 줄이고 코드 이력과 대용량 자산 관리를 분리하기 위함.
|
||||
🔧 `.gitattributes`에서 `images/**`를 LFS 대상으로 지정하고 신규 이미지도 같은 정책을 적용.
|
||||
|
||||
## 2026-03-30 v3.0.0 — 문서를 목적별 기준 파일로 분리
|
||||
|
||||
💡 구현 상태, 작업 로그, 결정 이유, 할 일을 한 문서에 섞지 않고 후속 작업자가 빠르게 맥락을 찾게 하기 위함.
|
||||
🔧 `spec`, `update`, `history`, `todo`, `deploy`, `map`, `convention`을 역할별 기준 문서로 운영.
|
||||
|
||||
205
docs/map.md
205
docs/map.md
@@ -1,63 +1,164 @@
|
||||
# 파일-화면 매핑 가이드
|
||||
# 파일-기능 지도
|
||||
|
||||
## `/`
|
||||
- 진입 파일: `index.html`
|
||||
- 화면 역할: 시리즈 선택 홈 화면
|
||||
- 연결: `script/auth-header.js`, `script/api.js`
|
||||
## 화면
|
||||
|
||||
## `/cardList.html?series=<key>&d=<shareCode>`
|
||||
- 진입 파일: `cardList.html`
|
||||
- 화면 역할: 카드 검색·필터·덱 편집·저장·공유
|
||||
- `?d=` 있으면 API에서 덱 복원
|
||||
- 연결: `datas/<seriesKey>.js`, `script/api.js`
|
||||
```text
|
||||
/
|
||||
index.html
|
||||
|
||||
## `/result.html`
|
||||
- 진입 파일: `result.html`
|
||||
- 화면 역할: 덱 미리보기·PNG 저장·내 덱 저장
|
||||
- 데이터 소스: `localStorage.selectedCards`
|
||||
- 하단 고정 바: `홈으로` | `이미지로 다운로드` | `내 덱으로 저장` (공개 여부 확인 모달)
|
||||
- `script/deck-prefs.js`: 덱 공개 기본값·프롬프트 설정
|
||||
/cardList.html
|
||||
cardList.html
|
||||
|
||||
## `/d/<shareCode>` (Nginx 리다이렉트)
|
||||
- `nginx/uadeck.conf` 규칙으로 `cardList.html?d=` 로 이동
|
||||
- 옆 프로젝트 연동: `GET /api/v1/decks/<shareCode>`
|
||||
/result.html
|
||||
result.html
|
||||
|
||||
## `/login.html`, `/signup.html`
|
||||
- 인증 화면
|
||||
- 연결: `script/page-i18n.js` (`data-i18n` 속성)
|
||||
/login.html
|
||||
login.html
|
||||
|
||||
## `/profile.html` (내 프로필)
|
||||
- 로그인 사용자: 이메일·사용자명·프로필 사진·비밀번호 변경
|
||||
- 연결: `script/api.js` (`updateProfile`, `changePassword`), `script/image-utils.js`, `script/page-i18n.js`, `script/ads.js`, `style/profile.css`
|
||||
/signup.html
|
||||
signup.html
|
||||
|
||||
## `/deckHistory.html`
|
||||
- 로그인 사용자의 저장 덱 목록·불러오기·삭제
|
||||
- 카드 그리드 + 미리보기 썸네일 (`preview_url`), 클릭·길게 눌러 확대 (`deck-preview-hold.js`)
|
||||
- 시리즈 필터·검색 (`style/deck-history.css`)
|
||||
- 연결: `script/page-i18n.js`, `script/ads.js` (우측 AdSense `4260332296`)
|
||||
/profile.html
|
||||
profile.html
|
||||
|
||||
## `/preset.html` (공개 덱)
|
||||
- 사용자가 공개 전환한 덱 목록 (카드 그리드)
|
||||
- 썸네일 클릭·길게 눌러 확대 (`deck-preview-hold.js`, 안내 문구 없음)
|
||||
- API: `GET /api/v1/decks/presets` → `is_public=1` user 덱
|
||||
- 연결: `script/page-i18n.js`, `script/ads.js`
|
||||
/deckHistory.html
|
||||
deckHistory.html
|
||||
|
||||
## `/admin.html`
|
||||
- 관리자: 회원 관리, 덱 관리 (`style/admin.css`)
|
||||
/preset.html
|
||||
preset.html
|
||||
|
||||
## API (`api/`)
|
||||
- Docker: `docker-compose.yml`
|
||||
- 엔드포인트 베이스: `/api/v1`
|
||||
/admin.html
|
||||
admin.html
|
||||
|
||||
## 공통
|
||||
- `style/app-shell.css`: 헤더·본문 최대 너비 1280px (`max-w-7xl`), 데스크톱 우측 광고 레일
|
||||
- `script/ads.js`: Google AdSense `ins.adsbygoogle` 슬롯 초기화
|
||||
- `script/page-help.js`: 페이지 제목 `?` 툴팁
|
||||
- `script/page-i18n.js`: `data-i18n` / `data-i18n-html` / `data-i18n-placeholder`, `UaPageI18n.t()`
|
||||
- `script/deck-favorites.js`: 덱 즐겨찾기 (localStorage)
|
||||
- `script/deck-preview-hold.js`: 덱 썸네일 길게 눌러 확대 (내 덱·공개 덱)
|
||||
- `style/brand.css`: 헤더 UNIARE 로고 크기 통일 (32px)
|
||||
- `script/api.js`: API 클라이언트 (`updateProfile`, `changePassword` 포함)
|
||||
- `script/auth-header.js`: 헤더 프로필 드롭다운 (i18n), 로그인 시 **썸네일 옆 닉네임** 표시 (`auth-profile-trigger`)
|
||||
- `script/image-utils.js`: 클라이언트 이미지 PNG 리사이즈 (`UaImageUtils.resizeImageToPng`)
|
||||
- `style/auth-header.css`
|
||||
/d/<shareCode>
|
||||
nginx/default.conf
|
||||
nginx/uadeck.conf
|
||||
```
|
||||
|
||||
## 프론트 공통 모듈
|
||||
|
||||
```text
|
||||
API 요청
|
||||
script/api.js
|
||||
|
||||
인증 헤더
|
||||
script/auth-header.js
|
||||
style/auth-header.css
|
||||
|
||||
페이지 공통 레이아웃
|
||||
style/app-shell.css
|
||||
|
||||
브랜드 로고 규격
|
||||
style/brand.css
|
||||
|
||||
페이지 번역
|
||||
script/page-i18n.js
|
||||
i18n/translations.ko.json
|
||||
i18n/translations.ja.json
|
||||
i18n/translations.en.json
|
||||
|
||||
페이지 도움말
|
||||
script/page-help.js
|
||||
|
||||
덱 공개 기본값
|
||||
script/deck-prefs.js
|
||||
|
||||
덱 즐겨찾기
|
||||
script/deck-favorites.js
|
||||
|
||||
덱 썸네일 확대
|
||||
script/deck-preview-hold.js
|
||||
|
||||
이미지 PNG 변환
|
||||
script/image-utils.js
|
||||
|
||||
광고 초기화
|
||||
script/ads.js
|
||||
```
|
||||
|
||||
## 페이지 전용 스타일
|
||||
|
||||
```text
|
||||
로그인·회원가입
|
||||
style/auth-pages.css
|
||||
|
||||
프로필
|
||||
style/profile.css
|
||||
|
||||
내 덱·공개 덱
|
||||
style/deck-history.css
|
||||
|
||||
관리자
|
||||
style/admin.css
|
||||
|
||||
Tailwind 입력·산출물
|
||||
style/input.css
|
||||
style/output.css
|
||||
```
|
||||
|
||||
## API
|
||||
|
||||
```text
|
||||
서버 진입·헬스 체크
|
||||
api/src/index.js
|
||||
|
||||
SQLite 스키마·관리자 시드
|
||||
api/src/db.js
|
||||
|
||||
JWT 인증·권한
|
||||
api/src/middleware/auth.js
|
||||
|
||||
회원가입·로그인·프로필
|
||||
api/src/routes/auth.js
|
||||
|
||||
덱 저장·조회·공개 목록
|
||||
api/src/routes/decks.js
|
||||
|
||||
회원·덱 관리자 API
|
||||
api/src/routes/admin.js
|
||||
|
||||
카드 메타 합성
|
||||
api/src/services/cardResolver.js
|
||||
|
||||
아바타 저장
|
||||
api/src/services/avatarImage.js
|
||||
|
||||
덱 미리보기 저장
|
||||
api/src/services/previewImage.js
|
||||
```
|
||||
|
||||
## 데이터·자산
|
||||
|
||||
```text
|
||||
카드 데이터
|
||||
datas/uaXX.js
|
||||
datas/uapr.js
|
||||
|
||||
카드 이미지
|
||||
images/<seriesKey>/
|
||||
|
||||
브랜드·파비콘
|
||||
images/assets/
|
||||
favicon.ico
|
||||
```
|
||||
|
||||
## 운영·외부 연동
|
||||
|
||||
```text
|
||||
API 단독 Compose
|
||||
docker-compose.yml
|
||||
|
||||
Nginx + API Compose
|
||||
docker-compose.prod.yml
|
||||
|
||||
Nginx 이미지
|
||||
nginx/Dockerfile
|
||||
nginx/default.conf
|
||||
|
||||
외부 덱 API 클라이언트
|
||||
integrations/uadeck-deck-client.js
|
||||
|
||||
외부 연동 문서
|
||||
docs/api-integration.md
|
||||
docs/widget.md
|
||||
```
|
||||
|
||||
349
docs/spec.md
349
docs/spec.md
@@ -1,45 +1,38 @@
|
||||
# 기술 명세
|
||||
|
||||
## 1. 프로젝트 개요
|
||||
- 프로젝트명: `UNIARE - Union Arena Deck Builder` (표시명 UNIARE, 내부 API 식별자 일부는 `uadeck` 레거시 유지)
|
||||
- 형태: 정적 HTML/CSS/JavaScript 프론트 + Docker API 백엔드
|
||||
- 패키지 의존성 (프론트): `tailwindcss`, `@tailwindcss/cli`
|
||||
- 패키지 의존성 (API): `express`, `better-sqlite3`, `bcryptjs`, `jsonwebtoken`, `cors`
|
||||
- 빌드 산출 스타일: `style/output.css`
|
||||
## 프로젝트 개요
|
||||
|
||||
## 2. 화면 구조
|
||||
- 서비스명: `UNIARE - Union Arena Deck Builder`
|
||||
- 현재 버전: `v4.0.1`
|
||||
- 프론트: 정적 HTML, CSS, JavaScript
|
||||
- 백엔드: Express, SQLite(`better-sqlite3`)
|
||||
- 운영: Docker Compose 기반 Nginx + API
|
||||
- 지원 언어: 한국어, 일본어, 영어
|
||||
- 표시 브랜드는 `UNIARE`이며 `UaDeckApi`, `uadeck_token`, `uadeck.db` 등 내부 레거시 식별자는 호환을 위해 유지한다.
|
||||
|
||||
### `/index.html`
|
||||
- 시리즈 선택 랜딩 페이지다.
|
||||
- `i18n/translations.<lang>.json`을 직접 fetch하여 시리즈명과 소개 문구를 렌더링한다.
|
||||
- 사용자가 시리즈를 선택하면 `cardList.html?series=<key>&seriesName=<name>&lang=<lang>`로 이동한다.
|
||||
## 화면 기능
|
||||
|
||||
### `/cardList.html`
|
||||
- 특정 시리즈의 카드 목록을 보여주고 덱을 편집한다.
|
||||
- 쿼리스트링의 `series` 값을 기준으로 `./datas/${seriesKey}.js`를 동적 import 한다.
|
||||
- 필터 기능:
|
||||
- 검색어
|
||||
- 색상(`All`, `Red`, `Blue`, `Green`, `Yellow`, `Purple`)
|
||||
- 시리즈 하위 그룹
|
||||
- 타입(`Basic`, `AP`, `Parallel`)
|
||||
- 트리거(`All`, `Special`, `Color`, `Final`)
|
||||
- 카드 수량 증감, 키 카드 지정, 초기화, 상세 오버레이, 결과 페이지 이동을 담당한다.
|
||||
| 경로 | 기능 |
|
||||
|---|---|
|
||||
| `/` | 시리즈 선택, 언어 전환, 인증 헤더 |
|
||||
| `/cardList.html?series=<key>` | 카드 검색·필터, 수량 조절, 키 카드 지정, 덱 편집 |
|
||||
| `/cardList.html?d=<shareCode>` | API 공유 덱 불러오기 및 편집 |
|
||||
| `/result.html` | 덱 미리보기, PNG 다운로드, 덱 저장 |
|
||||
| `/login.html` | 로그인 |
|
||||
| `/signup.html` | 회원가입 |
|
||||
| `/profile.html` | 이메일·사용자명·아바타·비밀번호·덱 공개 기본값 관리 |
|
||||
| `/deckHistory.html` | 내 덱 검색·필터·이름 변경·공개 설정·삭제·불러오기 |
|
||||
| `/preset.html` | 공개 사용자 덱 검색·필터·즐겨찾기·불러오기 |
|
||||
| `/admin.html` | 회원과 덱 관리 |
|
||||
| `/d/<shareCode>` | Nginx에서 `cardList.html?d=<shareCode>`로 연결 |
|
||||
|
||||
### `/result.html`
|
||||
- 선택한 카드 목록을 미리보기하고 이미지로 저장한다.
|
||||
- 스타일 모드:
|
||||
- `type1`: 키 카드 강조형
|
||||
- `type2`: 클래식 5열형
|
||||
- `html-to-image` CDN을 사용해 덱 이미지를 PNG로 저장한다.
|
||||
## 카드 데이터
|
||||
|
||||
## 3. 데이터 구조
|
||||
- 데이터 위치: `datas/*.js`
|
||||
- 실제 시리즈 데이터: `ua01`~`ua54` 중 현재 존재하는 파일과 `uapr.js`
|
||||
- 샘플 데이터: `ua99 sample.js`, `ua99 sample mini.js`
|
||||
- 모듈 형식: `export const cardList = [...]`
|
||||
|
||||
### 카드 데이터 파일
|
||||
- 위치: `datas/*.js`
|
||||
- 파일 수: 현재 `52개`
|
||||
- 각 파일은 `export const cardList = [...]` 형태를 사용한다.
|
||||
|
||||
### 카드 객체 스키마
|
||||
```js
|
||||
{
|
||||
imgSrc: '/ua24/UA24BT_SHY-1-001.png',
|
||||
@@ -50,209 +43,143 @@
|
||||
parallel: false,
|
||||
rarity: 'C',
|
||||
trigger: null,
|
||||
maxCount: 4,
|
||||
}
|
||||
```
|
||||
|
||||
### 필드 의미
|
||||
- `imgSrc`: 원본 상대 식별자. 실제 렌더링 시 `images/${seriesKey}/${fileName}` 형태로 재구성한다.
|
||||
- `Number`: 카드 번호. 동일 카드 판본 묶음과 필터 기준으로 사용한다.
|
||||
- `count`: 현재 덱에 담긴 수량.
|
||||
- `key`: 키 카드 여부.
|
||||
- `color`: 카드 색상 또는 일부 특수 분류 값.
|
||||
- `parallel`: 패러렐 여부.
|
||||
- `rarity`: 희귀도 표기.
|
||||
- `trigger`: `null`, `COLOR`, `FINAL`, `SPECIAL` 등 트리거 정보.
|
||||
- `maxCount`: 일부 카드만 선택적으로 사용하며 기본값은 `4`로 동작한다.
|
||||
- `Number`가 같은 판본은 동일 카드 수량 그룹으로 처리한다.
|
||||
- `parallel`은 패러렐 판본 구분에 사용한다.
|
||||
- `trigger`는 `null`, `COLOR`, `FINAL`, `SPECIAL` 값을 사용한다.
|
||||
- `maxCount`가 없으면 기본 최대 수량은 4장이다.
|
||||
|
||||
## 4. 이미지 규칙
|
||||
- 기본 위치: `images/<seriesKey>/`
|
||||
- 저장 정책: `images/**`는 `Git LFS` 대상으로 관리한다.
|
||||
- 렌더링 규칙:
|
||||
- 카드 목록 화면: `images/${seriesKey}/${fileName}`
|
||||
- 결과 화면: `/images/${seriesKey}/${fileName}`
|
||||
## 이미지
|
||||
|
||||
- 카드 이미지: `images/<seriesKey>/<fileName>`
|
||||
- 폴백 이미지: `/images/uapr/comingsoon.png`
|
||||
- 현재 용량 현황:
|
||||
- `images/` 약 `1.7G`
|
||||
- 이미지 파일 약 `9,370개`
|
||||
- 브랜드 자산: `images/assets/`
|
||||
- `images/**`는 Git LFS로 관리한다.
|
||||
- 아바타와 덱 미리보기는 API 데이터 디렉터리에 저장하며 Git에 포함하지 않는다.
|
||||
- 덱 저장 미리보기는 1200px 기준 PNG로 정규화한다.
|
||||
|
||||
## 5. 다국어
|
||||
- 지원 언어: `ko`, `ja`, `en`
|
||||
- 번역 파일:
|
||||
- `i18n/translations.ko.json`
|
||||
- `i18n/translations.ja.json`
|
||||
- `i18n/translations.en.json`
|
||||
- `index.html`은 번역 JSON을 직접 읽는다.
|
||||
- 공통 텍스트 치환은 `i18n/i18n.js`의 `data-i18n` 기반 로직을 사용한다.
|
||||
## 브라우저 상태
|
||||
|
||||
## 6. 상태 저장
|
||||
- `localStorage.lang`: 현재 언어
|
||||
- `localStorage.selectedCards`: 편집 중인 덱 (임시, `encodeURIComponent` JSON)
|
||||
- `localStorage.selectedSeries`: 현재 시리즈 키
|
||||
- `localStorage.uadeck_token` / `uadeck_user`: 로그인 세션
|
||||
- 덱 공유·영구 저장: API + DB (`GET /api/v1/decks/:shareCode`)
|
||||
| 키 | 용도 |
|
||||
|---|---|
|
||||
| `lang` | 선택 언어 |
|
||||
| `selectedCards` | 편집 중인 덱 |
|
||||
| `selectedSeries` | 현재 시리즈 |
|
||||
| `uadeck_token` | JWT |
|
||||
| `uadeck_user` | 로그인 사용자 캐시 |
|
||||
| 덱 즐겨찾기 키 | 공유 코드 기반 로컬 즐겨찾기 |
|
||||
|
||||
## 7. 스타일 및 자산
|
||||
- Tailwind 유틸리티 클래스와 페이지별 인라인 스타일을 혼용한다.
|
||||
- 공통 스타일 파일:
|
||||
- `style/output.css`
|
||||
- `style/style.css`
|
||||
- `style/navigation.css`
|
||||
## 인증 및 권한
|
||||
|
||||
## 8. 알려진 제약
|
||||
- `cardList.html`과 `result.html`의 스크립트가 페이지 내부에 크게 포함되어 있어 모듈 분리 전까지는 변경 영향 범위가 넓다.
|
||||
- `script/navigation.js`의 일부 이동 대상 페이지는 현재 저장소에 존재하지 않는다.
|
||||
- 이미지가 Git 일반 객체로 계속 누적되면 저장소 히스토리 크기가 지속 증가한다.
|
||||
- 비밀번호는 bcrypt 비용 12로 해시한다.
|
||||
- JWT 기본 만료는 7일이며 `JWT_EXPIRES`로 변경할 수 있다.
|
||||
- `user`는 자신의 프로필과 덱만 수정·삭제할 수 있다.
|
||||
- `admin`은 회원 상태·역할·비밀번호와 전체 덱을 관리할 수 있다.
|
||||
- 관리자는 자신의 관리자 역할을 해제하거나 자신의 계정을 비활성화할 수 없다.
|
||||
- 비공개 덱은 소유자와 관리자만 조회할 수 있다.
|
||||
|
||||
---
|
||||
## 덱 저장 정책
|
||||
|
||||
## 9. v4.0.0 계획 — 회원·덱 API·공유
|
||||
- 공유 코드는 저장 시 생성되는 8자 코드다.
|
||||
- 회원 덱은 만료 없이 저장하며 공개 여부를 선택할 수 있다.
|
||||
- 비회원 덱은 공개 상태로 저장되고 30일 뒤 만료된다.
|
||||
- DB의 `cards_json`에는 `{number, count, key}`만 저장한다.
|
||||
- API 응답 시 `datas/<series>.js`에서 이미지·색상·희귀도·트리거 정보를 합성한다.
|
||||
- 공개 덱 목록은 `share_type='user'`, `is_public=1`, 미만료 조건을 사용한다.
|
||||
- `preset` 타입과 관리자 프리셋 API는 레거시 호환용으로 코드에 남아 있으며 현재 공개 덱 화면에서는 사용하지 않는다.
|
||||
|
||||
> 상태: **v4.0.0 1차 구현 완료** (2026-06-19)
|
||||
## 데이터베이스
|
||||
|
||||
### 9.1 아키텍처 개요
|
||||
- 프론트: 기존 정적 HTML/JS 유지. 헤더에 프로필·인증 UI 추가.
|
||||
- 백엔드: Docker 컨테이너 API 서버 (NAS 배포).
|
||||
- 카드 DB: `datas/*.js` + `images/` 정적 유지 (변경 없음).
|
||||
- 사용자·덱 데이터: API + SQLite(또는 PostgreSQL) DB.
|
||||
- 외부 연동(옆 프로젝트): `GET /api/v1/decks/:shareCode` 단일 계약.
|
||||
### `users`
|
||||
|
||||
### 9.2 덱 공유 방식 (확정)
|
||||
| 컬럼 | 설명 |
|
||||
|---|---|
|
||||
| `id` | 기본 키 |
|
||||
| `email`, `username` | 고유 로그인·표시 식별자 |
|
||||
| `password_hash` | bcrypt 해시 |
|
||||
| `display_name` | 레거시 표시명, 현재 `username`과 함께 갱신 |
|
||||
| `avatar_url` | 아바타 API 경로 |
|
||||
| `role` | `user` 또는 `admin` |
|
||||
| `is_active` | 계정 활성 상태 |
|
||||
| `default_deck_public` | 덱 저장 기본 공개 여부 |
|
||||
| `skip_deck_public_prompt` | 저장 시 공개 확인 생략 여부 |
|
||||
| `created_at`, `updated_at` | 생성·수정 시각 |
|
||||
|
||||
기존 `?cards=<JSON>` URL 방식은 **폐기**한다. 공유는 공유 코드 + API만 사용한다.
|
||||
### `decks`
|
||||
|
||||
| 종류 | `share_type` | 코드 생성 | URL 예시 | 설명 |
|
||||
|---|---|---|---|---|
|
||||
| 개인 덱 | `user` | nanoid 8자 (저장마다 고유) | `/d/x7Kp9m2Q` | 같은 카드 구성이라도 저장자마다 URL이 다름 |
|
||||
| 메타 덱·프리셋 | `preset` | 내용 해시 8자 (동일 구성 = 동일 URL) | `/d/a7f3b2c1` | 관리자 등록 공식 템플릿, 중복 저장 없음 |
|
||||
| 컬럼 | 설명 |
|
||||
|---|---|
|
||||
| `share_code` | 고유 공유 코드 |
|
||||
| `share_type` | `user` 또는 레거시 `preset` |
|
||||
| `content_hash` | 레거시 프리셋 중복 판별 |
|
||||
| `user_id` | 소유 회원, 비회원은 `null` |
|
||||
| `series`, `series_name` | 시리즈 키와 표시명 |
|
||||
| `name` | 덱 이름 |
|
||||
| `cards_json` | 최소 카드 구성 JSON |
|
||||
| `is_public` | 공개 여부 |
|
||||
| `view_count` | 상세 조회 횟수 |
|
||||
| `expires_at` | 비회원 공유 만료 시각 |
|
||||
| `preview_image` | 저장된 미리보기 파일 정보 |
|
||||
| `created_at`, `updated_at` | 생성·수정 시각 |
|
||||
|
||||
브라우저 진입:
|
||||
- `/d/:shareCode` → `cardList.html?d=:shareCode` 리다이렉트 또는 동일 페이지에서 API 로드
|
||||
- `cardList.html`은 `?d=` 파라미터가 있으면 API에서 덱 복원
|
||||
## API
|
||||
|
||||
### 9.3 DB 저장 vs API 응답
|
||||
### 공통
|
||||
|
||||
**DB에 저장하는 최소 덱 본문 (`cards_json`):**
|
||||
```json
|
||||
[
|
||||
{ "number": "UA36BT/MCR-1-052", "count": 4, "key": true },
|
||||
{ "number": "EX14BT/MCR-2-011", "count": 2, "key": false }
|
||||
]
|
||||
```
|
||||
| Method | Path | 권한 | 기능 |
|
||||
|---|---|---|---|
|
||||
| GET | `/api/v1/health` | 공개 | API 상태와 버전 |
|
||||
|
||||
**API 응답 시 서버가 `datas/<series>.js`를 lookup하여 풀 카드 정보를 합성한다.**
|
||||
### 인증
|
||||
|
||||
```json
|
||||
{
|
||||
"v": 1,
|
||||
"shareCode": "x7Kp9m2Q",
|
||||
"shareType": "user",
|
||||
"series": "ua36",
|
||||
"seriesName": "마크로스",
|
||||
"name": "블루 aggro",
|
||||
"totalCards": 50,
|
||||
"cards": [
|
||||
{
|
||||
"number": "UA36BT/MCR-1-052",
|
||||
"count": 4,
|
||||
"key": true,
|
||||
"imgSrc": "/ua36/UA36BT_MCR-1-052.png",
|
||||
"color": "Blue",
|
||||
"rarity": "SR",
|
||||
"trigger": null,
|
||||
"parallel": false
|
||||
}
|
||||
],
|
||||
"author": {
|
||||
"id": 12,
|
||||
"displayName": "닉네임",
|
||||
"avatarUrl": "/uploads/avatars/12.png"
|
||||
},
|
||||
"createdAt": "2026-06-19T12:00:00Z",
|
||||
"updatedAt": "2026-06-19T14:30:00Z",
|
||||
"viewCount": 42,
|
||||
"isPublic": true
|
||||
}
|
||||
```
|
||||
| Method | Path | 권한 | 기능 |
|
||||
|---|---|---|---|
|
||||
| POST | `/api/v1/auth/register` | 공개 | 회원가입 |
|
||||
| POST | `/api/v1/auth/login` | 공개 | 로그인과 JWT 발급 |
|
||||
| GET | `/api/v1/auth/me` | 회원 | 내 정보 |
|
||||
| PUT | `/api/v1/auth/me` | 회원 | 프로필·덱 저장 설정 변경 |
|
||||
| POST | `/api/v1/auth/me/password` | 회원 | 비밀번호 변경 |
|
||||
| GET | `/api/v1/auth/avatar/:userId` | 공개 | PNG 아바타 |
|
||||
|
||||
비회원 임시 공유: `author`는 `null`, `expiresAt` 필드로 만료 관리.
|
||||
### 덱
|
||||
|
||||
### 9.4 DB 스키마 (초안)
|
||||
| Method | Path | 권한 | 기능 |
|
||||
|---|---|---|---|
|
||||
| GET | `/api/v1/decks/presets` | 공개 | 공개 사용자 덱 목록 |
|
||||
| GET | `/api/v1/decks/:shareCode/preview` | 공개 | 덱 미리보기 PNG |
|
||||
| GET | `/api/v1/decks/:shareCode` | 조건부 | 공개 덱 또는 소유 덱 상세 |
|
||||
| GET | `/api/v1/decks` | 회원 | 내 덱 목록 |
|
||||
| POST | `/api/v1/decks` | 공개 | 회원 또는 비회원 덱 저장 |
|
||||
| PUT | `/api/v1/decks/:shareCode` | 소유자·관리자 | 덱 수정 |
|
||||
| DELETE | `/api/v1/decks/:shareCode` | 소유자·관리자 | 덱 삭제 |
|
||||
|
||||
#### `users`
|
||||
| 컬럼 | 타입 | 설명 |
|
||||
### 관리자
|
||||
|
||||
| Method | Path | 기능 |
|
||||
|---|---|---|
|
||||
| `id` | INTEGER PK | |
|
||||
| `email` | TEXT UNIQUE | 로그인 ID |
|
||||
| `username` | TEXT UNIQUE | 표시용 닉네임 |
|
||||
| `password_hash` | TEXT | bcrypt |
|
||||
| `display_name` | TEXT | 프로필 이름 |
|
||||
| `avatar_url` | TEXT | 프로필 썸네일 |
|
||||
| `default_deck_public` | INTEGER | 덱 저장 기본 공개 여부 (1=공개, 0=비공개) |
|
||||
| `skip_deck_public_prompt` | INTEGER | 저장 시 공개 확인 생략 (1=생략) |
|
||||
| `role` | TEXT | `user` \| `admin` |
|
||||
| `is_active` | INTEGER | 0=비활성, 1=활성 |
|
||||
| `created_at` | TEXT | 가입일 |
|
||||
| `updated_at` | TEXT | |
|
||||
|
||||
#### `decks`
|
||||
| 컬럼 | 타입 | 설명 |
|
||||
|---|---|---|
|
||||
| `id` | INTEGER PK | |
|
||||
| `share_code` | TEXT UNIQUE | URL에 쓰이는 8자 코드 |
|
||||
| `share_type` | TEXT | `user` \| `preset` |
|
||||
| `content_hash` | TEXT | preset 타입용. user 타입은 null 가능 |
|
||||
| `user_id` | INTEGER FK | null=비회원 임시 공유 |
|
||||
| `series` | TEXT | 예: `ua36` |
|
||||
| `name` | TEXT | 덱 이름 |
|
||||
| `cards_json` | TEXT | `[{number,count,key}]` JSON |
|
||||
| `is_public` | INTEGER | 0=비공개, 1=공개 |
|
||||
| `view_count` | INTEGER | 조회수 |
|
||||
| `expires_at` | TEXT | null=만료 없음 |
|
||||
| `created_at` | TEXT | 생성일 |
|
||||
| `updated_at` | TEXT | 최종 수정일 |
|
||||
|
||||
덱 1건 용량 추정: 카드 50장 × 약 60바이트 ≈ 3KB. 수만 건이어도 DB 부담은 경미하다.
|
||||
|
||||
### 9.5 API 엔드포인트 (초안)
|
||||
|
||||
#### 인증
|
||||
| Method | Path | 설명 |
|
||||
|---|---|---|
|
||||
| POST | `/api/v1/auth/register` | 회원가입 |
|
||||
| POST | `/api/v1/auth/login` | 로그인 → JWT |
|
||||
| GET | `/api/v1/auth/me` | 내 정보 |
|
||||
| PUT | `/api/v1/auth/me` | 프로필 수정 (이메일, 사용자명, 아바타, `defaultDeckPublic`, `skipDeckPublicPrompt`) |
|
||||
| POST | `/api/v1/auth/me/password` | 비밀번호 변경 |
|
||||
| GET | `/api/v1/auth/avatar/:userId` | 프로필 이미지 (PNG) |
|
||||
|
||||
#### 덱
|
||||
| Method | Path | 설명 |
|
||||
|---|---|---|
|
||||
| GET | `/api/v1/decks/:shareCode` | 덱 조회 (외부 연동 공식 계약) |
|
||||
| GET | `/api/v1/decks` | 내 덱 목록 (인증 필요) |
|
||||
| POST | `/api/v1/decks` | 덱 저장·공유 코드 발급 |
|
||||
| PUT | `/api/v1/decks/:shareCode` | 덱 수정 (소유자만) |
|
||||
| DELETE | `/api/v1/decks/:shareCode` | 덱 삭제 (소유자만) |
|
||||
|
||||
#### 관리자
|
||||
| Method | Path | 설명 |
|
||||
|---|---|---|
|
||||
| GET | `/api/v1/admin/users` | 회원 목록 |
|
||||
| GET | `/api/v1/admin/users` | 회원 목록·검색 |
|
||||
| GET | `/api/v1/admin/users/:id` | 회원 상세 |
|
||||
| PUT | `/api/v1/admin/users/:id` | 회원 정보·역할·활성 수정 |
|
||||
| PUT | `/api/v1/admin/users/:id` | 회원 정보·역할·활성 상태 변경 |
|
||||
| POST | `/api/v1/admin/users/:id/reset-password` | 비밀번호 초기화 |
|
||||
| POST | `/api/v1/admin/decks/preset` | 메타 덱(프리셋) 등록 |
|
||||
| GET | `/api/v1/admin/decks` | 전체 덱 목록 |
|
||||
| DELETE | `/api/v1/admin/decks/:shareCode` | 덱 삭제 |
|
||||
| POST | `/api/v1/admin/decks/preset` | 레거시 프리셋 생성 |
|
||||
|
||||
### 9.6 프론트 변경 (예정)
|
||||
- `script/auth-header.js`: 헤더 프로필 드롭다운 (로그인/회원가입, 내 덱 관리)
|
||||
- `login.html`, `signup.html`, `deckHistory.html`, `admin.html` 신규
|
||||
- `cardList.html`: `?cards=` 제거, `?d=` API 로드로 전환
|
||||
- `localStorage.selectedCards`: 편집 중 임시 저장만 (공유와 분리)
|
||||
## 스타일
|
||||
|
||||
### 9.7 배포
|
||||
- `docker-compose.yml`: API만 (기존 웹서버 사용 시)
|
||||
- `docker-compose.prod.yml`: Nginx + API 풀스택 (NAS 권장)
|
||||
- `docs/deploy.md`: NAS 배포 상세 가이드
|
||||
- `docs/api-integration.md`: 옆 프로젝트 연동 가이드
|
||||
- `integrations/uadeck-deck-client.js`: 연동용 JS 클라이언트
|
||||
- `nginx/default.conf`: `/api/`, `/d/` 라우팅
|
||||
- 현재는 Tailwind 유틸리티, 공통 CSS, 페이지 전용 CSS, 일부 인라인 스타일을 혼용한다.
|
||||
- 이번 단계에서는 기존 CSS를 Tailwind로 일괄 전환하지 않는다.
|
||||
- `style/output.css`는 `style/input.css`에서 생성하는 빌드 산출물이다.
|
||||
- 헤더 로고 크기는 `style/brand.css`만 기준으로 사용한다.
|
||||
- 기본 글꼴은 Pretendard 계열을 우선 사용한다.
|
||||
|
||||
## 알려진 제약
|
||||
|
||||
- `cardList.html`, `result.html`의 인라인 스크립트가 커서 변경 영향 범위가 넓다.
|
||||
- `admin.html`은 3개 언어 번역 적용이 완료되지 않았다.
|
||||
- 즐겨찾기는 브라우저별 로컬 상태라 계정 간 동기화되지 않는다.
|
||||
- Homepage 전용 `GET /api/homepage` 엔드포인트는 아직 구현되지 않았다.
|
||||
- 현재 Compose의 컨테이너명(`uniare-api`, `uniare-web`)과 데이터 볼륨명(`uniare-data`)은 고정값이며 환경 변수화되지 않았다.
|
||||
|
||||
57
docs/todo.md
57
docs/todo.md
@@ -1,47 +1,22 @@
|
||||
# 할 일 및 이슈
|
||||
# 할 일
|
||||
|
||||
## 현재 이슈
|
||||
- `images/` 디렉터리가 약 `1.7G`까지 증가해 저장소 clone, fetch, checkout 비용이 커졌다.
|
||||
- 이미지 파일 수가 `9,000+` 단위라서 카드 추가 빈도가 올라갈수록 Git 객체 수와 히스토리 부담이 계속 누적될 가능성이 높다.
|
||||
- `script/navigation.js`에는 현재 프로젝트에 없는 페이지(`deckHistory.html`, `preset.html`, `myDatabase.html`)로 이동하는 코드가 남아 있다.
|
||||
- `cardList.html`과 `result.html` 내부 스크립트 비중이 커서 기능 확장 시 유지보수 난도가 올라갈 수 있다.
|
||||
- 데이터 파일 네이밍과 샘플 파일(`datas/ua99 sample.js`, `datas/ua99 sample mini.js`) 처리 기준이 문서상 명확하지 않았다.
|
||||
## 운영
|
||||
|
||||
## v4.0.0 구현 완료 항목
|
||||
- [x] Docker API 서버 + DB (`users`, `decks` 테이블)
|
||||
- [x] 인증: 회원가입, 로그인, JWT, 헤더 프로필 드롭다운
|
||||
- [x] 덱 API: 저장, 조회, 공유 코드 (`GET /api/v1/decks/:shareCode`)
|
||||
- [x] `cardList.html`: `?d=` API 로드, 저장·공유 버튼
|
||||
- [x] `deckHistory.html`, `login.html`, `signup.html`, `preset.html`
|
||||
- [x] `admin.html`: 회원 목록·수정, 비밀번호 초기화
|
||||
- [x] `index.html` 버전 `v4.0.0`
|
||||
- NAS에서 `uniare-web`, `uniare-api` 스택의 재기동과 헬스 체크를 최종 확인한다.
|
||||
- 운영 `.env`의 `JWT_SECRET`, 관리자 계정, CORS 허용 도메인을 실제 환경 기준으로 점검한다.
|
||||
- DB와 업로드 이미지(`api/data/previews`, `api/data/avatars`)의 정기 백업·복구 절차를 검증한다.
|
||||
- 원격 저장소의 Git LFS 업로드 상태와 용량 정책을 확인한다.
|
||||
- Compose의 컨테이너명과 데이터 볼륨명을 환경 변수로 관리할지 결정하고, 적용 시 운영 마이그레이션 절차를 작성한다.
|
||||
|
||||
## v4 후속 작업
|
||||
- [ ] NAS에서 구 컨테이너(`uniare-web`) 정리 후 신규 스택(`uniare-web`, `uniare-api`) 재기동 확인
|
||||
- [ ] `.env` 설정 및 기존 리버스 프록시 연동
|
||||
- [ ] 옆 프로젝트에 `integrations/uadeck-deck-client.js` 적용
|
||||
- [ ] `cardList.html` 인라인 스크립트 모듈 분리
|
||||
- [x] i18n: 모달·홈 버튼·auth·덱 관리·프로필·로그인/가입 3개국어 (v4.0.0 후속)
|
||||
- [x] 덱 즐겨찾기·필터 (내 덱·공개 덱)
|
||||
- [x] 공개 덱: 홀드 미리보기 + 하단 즐겨찾기/불러오기 버튼
|
||||
- [x] 내 덱: 썸네일 클릭·홀드 확대, 저장 썸네일 1200px 정규화
|
||||
- [ ] i18n: `admin.html` 문구
|
||||
## 기능
|
||||
|
||||
## v4.0.0 최근 완료
|
||||
- [x] `profile.html`: 일반 사용자 프로필 화면 (아바타·닉네임·이메일·비밀번호)
|
||||
- [x] 헤더 드롭다운「내 프로필」링크
|
||||
- [x] 아바타 업로드 시 PNG 리사이즈 (`script/image-utils.js`) — JPEG 등 형식 깨짐 방지
|
||||
- [x] 아바타 URL `updatedAt` 캐시 무효화
|
||||
- [x] 내 덱 관리: `/d/코드` 경로 표시 제거
|
||||
- [x] `result.html` 하단 버튼 개편 + 공개 확인 모달 + 홈 이동
|
||||
- [x] 프로필·API: 덱 저장 공개 기본값 설정
|
||||
- `admin.html`의 고정 한국어 문구를 `ko`, `ja`, `en` 번역 체계에 연결한다.
|
||||
- 옆 프로젝트에 `integrations/uadeck-deck-client.js`를 적용하고 실제 공유 덱 조회를 검증한다.
|
||||
- Homepage 연동이 필요해지면 `GET /api/homepage`와 운영 지표 응답을 구현한다.
|
||||
|
||||
## 다음 작업 제안
|
||||
- 원격 저장소에서 `Git LFS` 업로드가 정상 완료되었는지 확인한다.
|
||||
- 협업자가 있다면 히스토리 재작성 이후 동기화 방법을 공지한다.
|
||||
- `Git LFS` 도입 후에도 저장소 운영 비용이 크면 이미지 저장소 분리/CDN 이전을 재검토한다.
|
||||
- `cardList.html`의 인라인 스크립트를 별도 JS 모듈로 분리하는 리팩터링을 검토한다.
|
||||
## 유지보수
|
||||
|
||||
## 판단 가이드
|
||||
- 신규 이미지 추가 관리가 목적이면 `Git LFS` 추적만으로도 충분하다.
|
||||
- 기존 clone 크기까지 줄이는 것이 목적이면 `Git LFS` 히스토리 마이그레이션 또는 자산 저장소 분리가 필요하다.
|
||||
- `cardList.html`, `result.html`의 대형 인라인 스크립트를 기능 단위 모듈로 분리한다.
|
||||
- `datas/ua99 sample.js`, `datas/ua99 sample mini.js`의 보존 목적과 배포 제외 여부를 확정한다.
|
||||
- 즐겨찾기의 계정 간 동기화가 필요하면 사용자별 API 저장 방식으로 확장한다.
|
||||
- 이미지 규모가 Git LFS 운영 한도를 넘으면 자산 저장소 또는 CDN 분리를 검토한다.
|
||||
|
||||
@@ -1,5 +1,22 @@
|
||||
# 업데이트 이력
|
||||
|
||||
## 2026-06-23 / v4.0.1 — 프로젝트 문서 체계 정비
|
||||
|
||||
### 수행 작업
|
||||
- 실제 코드와 Git 이력을 기준으로 `spec.md`, `todo.md`, `history.md`, `map.md`, `convention.md`를 현재 상태에 맞게 재작성
|
||||
- 과거 주요 구현 결정을 `history.md` 형식에 맞춰 날짜·버전·결정 이유·적용 내용으로 복원
|
||||
- 누락된 `VERSION`, `docs/widget.md`, `docs/changelog.md` 추가
|
||||
- `deploy.md`를 최초 설치·실행·업데이트·백업·롤백 순서의 운영 가이드로 전면 재작성
|
||||
- `deploy.md`에서 로컬 개발과 NAS 운영을 분리하고 이미지·`datas/`·API 변경별 반영 명령을 구분
|
||||
- `AGENTS.md` 재검수 결과를 반영해 SQLite WAL 안전 백업·복구 절차와 Compose 고정 이름 제약을 문서화
|
||||
- Homepage Custom API YAML을 공식 문서 기준으로 재확인하고 참고 URL 추가
|
||||
- `.cursorrules`의 잘못된 `.AGENTS.md` 참조를 `AGENTS.md`로 수정하고 배포 문서의 개인 로컬 절대 경로를 제거
|
||||
- `deploy.md`의 중복 설명을 제거하고 초심자가 상황별 절차를 바로 선택할 수 있도록 간소화
|
||||
- `deploy.md`의 Markdown 표를 제거하고 불필요한 줄바꿈을 줄여 명령 중심으로 재정리
|
||||
- `deploy.md`의 한 줄 명령은 인라인 코드로 변경하고 여러 명령을 연속 실행하는 경우만 코드 블록 유지
|
||||
- 완료 항목이 남아 있던 `todo.md`를 미완료 작업만 유지하도록 정리
|
||||
- 현재 단계에서는 기존 CSS 구조를 유지하고 Tailwind 일괄 전환을 제외한다고 명시
|
||||
|
||||
## 2026-06-22 / v4.0.0 — 헤더 로고 크기 통일
|
||||
|
||||
### 수행 작업
|
||||
|
||||
78
docs/widget.md
Normal file
78
docs/widget.md
Normal file
@@ -0,0 +1,78 @@
|
||||
# 외부 서비스 연동
|
||||
|
||||
## 기본 정보
|
||||
|
||||
- 운영 URL: `https://uniare.sori.studio`
|
||||
- API 상태: `https://uniare.sori.studio/api/v1/health`
|
||||
- 공유 덱: `https://uniare.sori.studio/api/v1/decks/<shareCode>`
|
||||
- Homepage 전용 `GET /api/homepage`는 아직 구현되지 않았다.
|
||||
|
||||
## 상태 확인
|
||||
|
||||
```bash
|
||||
curl -fsS https://uniare.sori.studio/api/v1/health
|
||||
```
|
||||
|
||||
```json
|
||||
{
|
||||
"ok": true,
|
||||
"version": "4.0.0"
|
||||
}
|
||||
```
|
||||
|
||||
로컬 Docker에서는 다음 주소를 사용한다.
|
||||
|
||||
```bash
|
||||
curl -fsS http://localhost:44082/api/v1/health
|
||||
```
|
||||
|
||||
## Homepage 위젯
|
||||
|
||||
현재 구현된 헬스 API를 사용하는 예시다.
|
||||
|
||||
Homepage Custom API 공식 문서:
|
||||
|
||||
```text
|
||||
https://gethomepage.dev/widgets/services/customapi/
|
||||
```
|
||||
|
||||
```yaml
|
||||
- UNIARE:
|
||||
icon: mdi-cards-playing
|
||||
href: https://uniare.sori.studio
|
||||
description: Union Arena Deck Builder
|
||||
widget:
|
||||
type: customapi
|
||||
url: https://uniare.sori.studio/api/v1/health
|
||||
refreshInterval: 30000
|
||||
mappings:
|
||||
- field: ok
|
||||
label: 상태
|
||||
format: text
|
||||
- field: version
|
||||
label: 버전
|
||||
format: text
|
||||
```
|
||||
|
||||
Docker 내부의 Homepage 컨테이너에서 같은 Compose 네트워크를 사용할 경우 공개 도메인 대신 `http://uniare-web/api/v1/health`를 사용할 수 있다. 서로 다른 Compose 프로젝트라면 공통 외부 네트워크 연결이 필요하다.
|
||||
|
||||
## 공유 덱 조회
|
||||
|
||||
```bash
|
||||
curl -fsS https://uniare.sori.studio/api/v1/decks/x7Kp9m2Q
|
||||
```
|
||||
|
||||
브라우저와 Node.js 연동은 `integrations/uadeck-deck-client.js` 또는 `docs/api-integration.md`를 사용한다.
|
||||
|
||||
## 운영 지표 제안
|
||||
|
||||
`GET /api/homepage`를 구현할 때 다음 지표를 우선 제공한다.
|
||||
|
||||
- API 상태와 현재 버전
|
||||
- 전체 회원 수
|
||||
- 전체 덱 수와 공개 덱 수
|
||||
- 최근 24시간 생성 덱 수
|
||||
- DB 백업 시각과 성공 여부
|
||||
- 미리보기·아바타 저장 공간 사용량
|
||||
|
||||
회원 수나 덱 수처럼 운영 정보가 포함된 엔드포인트는 공개 범위를 검토하고, 필요하면 내부 네트워크 또는 별도 토큰으로 제한한다.
|
||||
Reference in New Issue
Block a user