Express/SQLite API와 Docker 구성을 추가하고 로그인·덱 저장·공개 덱·관리자·프로필 화면을 구현했다. 결과 화면 저장 UX, 헤더 프로필, 애드센스용 app-shell 레이아웃(1280px) 및 문서를 반영했다. Co-authored-by: Cursor <cursoragent@cursor.com>
9.5 KiB
9.5 KiB
기술 명세
1. 프로젝트 개요
- 프로젝트명:
UADECK V4.0.0 - Union Arena Deck Builder - 형태: 정적 HTML/CSS/JavaScript 프론트 + Docker API 백엔드
- 패키지 의존성 (프론트):
tailwindcss,@tailwindcss/cli - 패키지 의존성 (API):
express,better-sqlite3,bcryptjs,jsonwebtoken,cors - 빌드 산출 스타일:
style/output.css
2. 화면 구조
/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) - 카드 수량 증감, 키 카드 지정, 초기화, 상세 오버레이, 결과 페이지 이동을 담당한다.
/result.html
- 선택한 카드 목록을 미리보기하고 이미지로 저장한다.
- 스타일 모드:
type1: 키 카드 강조형type2: 클래식 5열형html-to-imageCDN을 사용해 덱 이미지를 PNG로 저장한다.
3. 데이터 구조
카드 데이터 파일
- 위치:
datas/*.js - 파일 수: 현재
52개 - 각 파일은
export const cardList = [...]형태를 사용한다.
카드 객체 스키마
{
imgSrc: '/ua24/UA24BT_SHY-1-001.png',
Number: 'UA24BT/SHY-1-001',
count: 0,
key: false,
color: 'Red',
parallel: false,
rarity: 'C',
trigger: null,
}
필드 의미
imgSrc: 원본 상대 식별자. 실제 렌더링 시images/${seriesKey}/${fileName}형태로 재구성한다.Number: 카드 번호. 동일 카드 판본 묶음과 필터 기준으로 사용한다.count: 현재 덱에 담긴 수량.key: 키 카드 여부.color: 카드 색상 또는 일부 특수 분류 값.parallel: 패러렐 여부.rarity: 희귀도 표기.trigger:null,COLOR,FINAL,SPECIAL등 트리거 정보.maxCount: 일부 카드만 선택적으로 사용하며 기본값은4로 동작한다.
4. 이미지 규칙
- 기본 위치:
images/<seriesKey>/ - 저장 정책:
images/**는Git LFS대상으로 관리한다. - 렌더링 규칙:
- 카드 목록 화면:
images/${seriesKey}/${fileName} - 결과 화면:
/images/${seriesKey}/${fileName} - 폴백 이미지:
/images/uapr/comingsoon.png - 현재 용량 현황:
images/약1.7G- 이미지 파일 약
9,370개
5. 다국어
- 지원 언어:
ko,ja,en - 번역 파일:
i18n/translations.ko.jsoni18n/translations.ja.jsoni18n/translations.en.jsonindex.html은 번역 JSON을 직접 읽는다.- 공통 텍스트 치환은
i18n/i18n.js의data-i18n기반 로직을 사용한다.
6. 상태 저장
localStorage.lang: 현재 언어localStorage.selectedCards: 편집 중인 덱 (임시,encodeURIComponentJSON)localStorage.selectedSeries: 현재 시리즈 키localStorage.uadeck_token/uadeck_user: 로그인 세션- 덱 공유·영구 저장: API + DB (
GET /api/v1/decks/:shareCode)
7. 스타일 및 자산
- Tailwind 유틸리티 클래스와 페이지별 인라인 스타일을 혼용한다.
- 공통 스타일 파일:
style/output.cssstyle/style.cssstyle/navigation.css
8. 알려진 제약
cardList.html과result.html의 스크립트가 페이지 내부에 크게 포함되어 있어 모듈 분리 전까지는 변경 영향 범위가 넓다.script/navigation.js의 일부 이동 대상 페이지는 현재 저장소에 존재하지 않는다.- 이미지가 Git 일반 객체로 계속 누적되면 저장소 히스토리 크기가 지속 증가한다.
9. v4.0.0 계획 — 회원·덱 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단일 계약.
9.2 덱 공유 방식 (확정)
기존 ?cards=<JSON> URL 방식은 폐기한다. 공유는 공유 코드 + API만 사용한다.
| 종류 | share_type |
코드 생성 | URL 예시 | 설명 |
|---|---|---|---|---|
| 개인 덱 | user |
nanoid 8자 (저장마다 고유) | /d/x7Kp9m2Q |
같은 카드 구성이라도 저장자마다 URL이 다름 |
| 메타 덱·프리셋 | preset |
내용 해시 8자 (동일 구성 = 동일 URL) | /d/a7f3b2c1 |
관리자 등록 공식 템플릿, 중복 저장 없음 |
브라우저 진입:
/d/:shareCode→cardList.html?d=:shareCode리다이렉트 또는 동일 페이지에서 API 로드cardList.html은?d=파라미터가 있으면 API에서 덱 복원
9.3 DB 저장 vs API 응답
DB에 저장하는 최소 덱 본문 (cards_json):
[
{ "number": "UA36BT/MCR-1-052", "count": 4, "key": true },
{ "number": "EX14BT/MCR-2-011", "count": 2, "key": false }
]
API 응답 시 서버가 datas/<series>.js를 lookup하여 풀 카드 정보를 합성한다.
{
"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
}
비회원 임시 공유: author는 null, expiresAt 필드로 만료 관리.
9.4 DB 스키마 (초안)
users
| 컬럼 | 타입 | 설명 |
|---|---|---|
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/:id |
회원 상세 |
| PUT | /api/v1/admin/users/:id |
회원 정보·역할·활성 수정 |
| POST | /api/v1/admin/users/:id/reset-password |
비밀번호 초기화 |
| 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/라우팅