Files
uniare.sori.studio/docs/spec.md
zenn 63aabf61c4 버전 4.0.18과 변경 문서 동기화
VERSION과 API 버전을 4.0.18로 올리고 spec/update/changelog에 관리자 덱 복구 및 탈퇴 동선 변경 내용을 반영한다.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-25 18:10:55 +09:00

14 KiB

기술 명세

프로젝트 개요

  • 서비스명: UNIARE - Union Arena Deck Builder
  • 현재 버전: v4.0.18
  • 프론트: 정적 HTML, CSS, JavaScript
  • 백엔드: Express, SQLite(better-sqlite3)
  • 운영: Docker Compose 기반 Nginx + API
  • 지원 언어: 한국어, 일본어, 영어
  • 표시 브랜드는 UNIARE이며 UaDeckApi, uadeck_token, uadeck.db 등 내부 레거시 식별자는 호환을 위해 유지한다.

화면 기능

경로 기능
/ 시리즈 선택, 언어 전환, 인증 헤더
/cardList.html?series=<key> 카드 검색·필터, 수량 조절, 키 카드 지정, 덱 편집
/cardList.html?d=<shareCode> API 공유 덱 불러오기 및 편집
/result.html 덱 미리보기, PNG 다운로드, 회원 덱 저장
/login.html 로그인
/signup.html 회원가입
/verify-email.html 가입 이메일 인증·인증 메일 재전송
/forgot-password.html 비밀번호 재설정 메일 요청
/reset-password.html?token=<token> 1회용 토큰으로 비밀번호 재설정
/profile.html 이메일·사용자명·아바타·비밀번호·덱 공개 기본값·회원 탈퇴 관리
/deckHistory.html 내 덱 검색·필터·이름 변경·공개 설정·삭제·불러오기
/preset.html 공개 사용자 덱 검색·필터·즐겨찾기·불러오기
/admin.html 회원과 덱 관리
/d/<shareCode> Nginx에서 cardList.html?d=<shareCode>로 연결

검색 노출

  • 메인 검색 제목은 UNIARE | 유니온 아레나 덱 빌더를 사용한다.
  • 메인 설명은 카드 검색, 덱 제작·저장·공유, 공개 덱과 즐겨찾기 기능을 안내한다.
  • JavaScript 실행 전에도 메인 제목과 설명이 HTML에 표시된다.
  • canonical, Open Graph, Twitter Card, WebSite·WebApplication 구조화 데이터를 제공한다.
  • 번역 파일 로딩 실패 시 내부 오류 대신 새로고침 안내를 표시한다.

카드 데이터

  • 데이터 위치: datas/*.js
  • 실제 시리즈 데이터: ua01~ua54 중 현재 존재하는 파일과 uapr.js
  • 샘플 데이터: ua99 sample.js, ua99 sample mini.js
  • 모듈 형식: 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,
    maxCount: 4,
}
  • Number가 같은 판본은 동일 카드 수량 그룹으로 처리한다.
  • parallel은 패러렐 판본 구분에 사용한다.
  • trigger는 null, COLOR, FINAL, SPECIAL 값을 사용한다.
  • maxCount가 없으면 기본 최대 수량은 4장이다.

이미지

  • 카드 이미지: images/<seriesKey>/<fileName>
  • 폴백 이미지: /images/uapr/comingsoon.png
  • 브랜드 자산: images/assets/
  • images/**는 Git LFS로 관리한다.
  • 카드 목록·덱 결과 화면에서 카드 이미지 로드 실패 시 폴백 이미지를 표시한다.
  • 아바타와 덱 미리보기는 API 데이터 디렉터리에 저장하며 Git에 포함하지 않는다.
  • 덱 저장 미리보기는 1200px 기준 PNG로 정규화한다.
  • 덱 삭제 시 해당 공유 코드의 미리보기 PNG도 함께 삭제한다.
  • 관리자는 DB에서 참조하지 않는 미리보기 파일을 조회하고 일괄 삭제할 수 있다.

브라우저 상태

키 용도
lang 선택 언어
selectedCards 편집 중인 덱
selectedSeries 현재 시리즈
uadeck_token JWT. 로그인 유지 선택 시 localStorage, 미선택 시 sessionStorage
uadeck_user 로그인 사용자 캐시. 토큰과 같은 저장소 사용
uadeck.deckFavorites 비로그인 즐겨찾기 및 로그인 시 최초 이전 대상

인증 및 권한

  • 비밀번호는 bcrypt 비용 12로 해시한다.
  • 신규 회원은 Resend 인증 링크로 이메일 소유 확인을 완료해야 로그인할 수 있다.
  • 기존 회원은 이메일 인증 기능 배포 시 인증 완료 상태로 마이그레이션한다.
  • 인증 링크는 24시간, 비밀번호 재설정 링크는 30분 동안 한 번만 사용할 수 있다.
  • 인증·재설정 원문 토큰은 DB에 저장하지 않고 SHA-256 해시만 저장한다.
  • 인증 메일 링크와 인증 화면은 버전 쿼리로 최신 HTML·API 클라이언트를 로드하고, 캐시된 구 클라이언트에서도 직접 인증 API를 호출한다.
  • 인증 폼은 요청 중 제출 버튼을 잠그고 로딩 상태를 표시하며, 메일 재요청은 화면과 API에서 1분 간격으로 제한한다.
  • 가입 인증·인증 재전송·비밀번호 재설정 메일은 계정별 한국 시간 기준 월 3회까지만 발송한다.
  • 월간 한도 초과 시 자동 발송을 중단하고 관리자 이메일 zenn@sori.studio 문의를 안내한다.
  • 미인증 사용자가 로그인하면 로그인 화면에서 인증 메일을 즉시 재전송할 수 있으며, 최초 발송 포함 월 3회 제한과 관리자 문의 방법을 함께 표시한다.
  • 인증 완료 안내와 비밀번호 찾기·재설정 화면은 한국어를 기본값으로 한국어·일본어·영어 번역을 제공한다.
  • 사용자명은 대소문자를 구분하지 않고 중복을 차단한다.
  • 인증된 이메일은 프로필에서 직접 변경할 수 없다.
  • JWT 기본 만료는 7일이며 JWT_EXPIRES로 변경할 수 있다.
  • 로그인 유지 선택 시 브라우저를 닫아도 인증 정보를 유지하고, 미선택 시 현재 브라우저 세션에서만 유지한다.
  • 인증된 화면 진입 시 /auth/me 검증과 함께 last_login_at을 갱신한다.
  • user는 자신의 프로필과 덱만 수정·삭제할 수 있다.
  • 일반 회원은 프로필 최하단에서 DELETE 확인 입력 후 직접 탈퇴할 수 있다.
  • admin은 회원 상태·역할·비밀번호·탈퇴와 전체 덱을 관리할 수 있다.
  • 관리자는 자신의 관리자 역할을 해제하거나 자신의 계정을 비활성화할 수 없다.
  • 관리자는 현재 로그인한 자신의 계정을 관리자 화면에서 탈퇴시킬 수 없다.
  • 비공개 덱은 소유자와 관리자만 조회할 수 있다.
  • 비회원은 덱을 서버에 저장하거나 공개 링크를 생성할 수 없다.
  • 로그인 사용자의 즐겨찾기는 계정 DB에 저장되어 브라우저와 기기가 달라도 유지된다.
  • 비로그인 즐겨찾기는 브라우저에 저장하고 로그인 시 유효한 덱만 계정으로 이전한다.
  • 계정 탈퇴 시 사용자·인증 토큰·메일 기록·즐겨찾기·아바타를 삭제한다.
  • 탈퇴 회원의 저장 덱과 썸네일은 다른 사용자가 계속 참고할 수 있도록 유지하고 user_id만 null로 변경해 작성자를 익명 처리한다.
  • 같은 사용자명으로 새로 가입해도 새 사용자 ID가 부여되어 기존 익명 덱이나 개인정보와 연결되지 않는다.

덱 저장 정책

  • 공유 코드는 저장 시 생성되는 8자 코드다.
  • 덱 저장과 공개 링크 생성은 로그인한 회원만 사용할 수 있다.
  • 비회원은 덱 구성과 PNG 이미지 다운로드만 사용할 수 있다.
  • 기존 비회원 공유 덱은 호환을 위해 원래 만료일까지 조회할 수 있다.
  • DB의 cards_json에는 {imgSrc, number, count, key}를 저장한다.
  • API 응답 시 datas/<series>.js에서 이미지·색상·희귀도·트리거 정보를 합성한다.
  • 같은 Number에 여러 판본이 있을 경우 API는 저장된 imgSrc를 기준으로 카드 메타를 복원한다.
  • 공개 덱 목록은 share_type='user', is_public=1, 미만료 조건을 사용한다.
  • preset 타입과 관리자 프리셋 API는 레거시 호환용으로 코드에 남아 있으며 현재 공개 덱 화면에서는 사용하지 않는다.

데이터베이스

users

컬럼 설명
id 기본 키
email, username 고유 로그인·표시 식별자
password_hash bcrypt 해시
display_name 레거시 표시명, 현재 username과 함께 갱신
avatar_url 아바타 API 경로
role user 또는 admin
is_active 계정 활성 상태
default_deck_public 덱 저장 기본 공개 여부
skip_deck_public_prompt 저장 시 공개 확인 생략 여부
last_login_at 마지막 로그인 또는 인증 세션 확인 시각
email_verified_at 이메일 인증 완료 시각. 기존 회원은 마이그레이션 시 채움
created_at, updated_at 생성·수정 시각

auth_tokens

컬럼 설명
user_id 인증 대상 사용자
type verify_email 또는 reset_password
token_hash 원문 토큰의 SHA-256 해시
expires_at, used_at 만료·사용 시각
created_at 발급 시각

email_send_logs

컬럼 설명
user_id 발송 대상 사용자
type verify_email 또는 reset_password
month_key 한국 시간 기준 YYYY-MM 월
status 동시 요청 방지용 reserved, 발송 완료 sent
created_at, sent_at 예약·발송 시각

decks

컬럼 설명
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 생성·수정 시각

deck_favorites

컬럼 설명
user_id 즐겨찾기 사용자, 사용자 삭제 시 함께 삭제
share_code 즐겨찾기 덱, 덱 삭제 시 함께 삭제
created_at 즐겨찾기 등록 시각

API

공통

Method Path 권한 기능
GET /api/v1/health 공개 API 상태와 버전

인증

Method Path 권한 기능
POST /api/v1/auth/register 공개 미인증 회원 생성·가입 인증 메일 발송
POST /api/v1/auth/resend-verification 공개 가입 인증 메일 재전송
POST /api/v1/auth/verify-email 공개 이메일 인증 토큰 확인
POST /api/v1/auth/login 공개 로그인과 JWT 발급, last_login_at 갱신
POST /api/v1/auth/forgot-password 공개 비밀번호 재설정 메일 요청
POST /api/v1/auth/reset-password 공개 1회용 토큰으로 비밀번호 변경
GET /api/v1/auth/me 회원 내 정보와 인증 세션 확인, last_login_at 갱신
PUT /api/v1/auth/me 회원 프로필·덱 저장 설정 변경
POST /api/v1/auth/me/password 회원 비밀번호 변경
DELETE /api/v1/auth/me 회원 DELETE 확인 후 본인 계정 탈퇴
GET /api/v1/auth/avatar/:userId 공개 PNG 아바타

덱

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 소유자·관리자 덱 삭제
GET /api/v1/decks/favorites 회원 내 즐겨찾기 코드 목록
POST /api/v1/decks/favorites 회원 브라우저 즐겨찾기 일괄 이전
PUT /api/v1/decks/favorites/:shareCode 회원 즐겨찾기 추가
DELETE /api/v1/decks/favorites/:shareCode 회원 즐겨찾기 제거

관리자

Method Path 기능
GET /api/v1/admin/users 회원 목록·검색 (deckPublic, deckTotal, lastLoginAt 포함)
GET /api/v1/admin/users/:id 회원 상세 (deckPublic, deckTotal, deckCount, lastLoginAt 포함)
PUT /api/v1/admin/users/:id 회원 정보·역할·활성 상태 변경
POST /api/v1/admin/users/:id/verify-email 미인증 회원 이메일 수동 승인
POST /api/v1/admin/users/:id/reset-password 비밀번호 초기화
DELETE /api/v1/admin/users/:id DELETE 확인 후 회원 탈퇴 처리
GET /api/v1/admin/decks 전체 덱 목록
POST /api/v1/admin/decks/bulk-delete 선택한 덱 최대 100개와 썸네일 일괄 삭제
DELETE /api/v1/admin/decks/:shareCode 덱 삭제
GET /api/v1/admin/previews/orphans 미참조 덱 미리보기 목록
DELETE /api/v1/admin/previews/orphans 미참조 덱 미리보기 일괄 삭제
POST /api/v1/admin/decks/preset 레거시 프리셋 생성

스타일

  • 현재는 Tailwind 유틸리티, 공통 CSS, 페이지 전용 CSS, 일부 인라인 스타일을 혼용한다.
  • 기존 CSS는 todo.md의 화면별 체크리스트 순서에 따라 점진적으로 Tailwind로 전환한다.
  • 관리자와 로그인·회원가입 화면은 Tailwind 전환을 완료하고 전용 CSS를 제거했다.
  • 관리자 탭·모달 상태는 JavaScript에서 border-*, text-*, hidden, flex 유틸리티를 전환한다.
  • 덱 카드·툴바·프로필·앱 셸 스타일은 style/components.css(@layer components)에 통합되어 output.css로 빌드된다.
  • 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)은 고정값이며 환경 변수화되지 않았다.
  • Resend 발송은 운영 .env의 API 키·발신 주소와 Resend에서 인증된 발신 도메인이 필요하다.