v4.0.0: 회원·덱 API, 인증 UI, 프로필 및 레이아웃 통일

Express/SQLite API와 Docker 구성을 추가하고 로그인·덱 저장·공개 덱·관리자·프로필 화면을 구현했다. 결과 화면 저장 UX, 헤더 프로필, 애드센스용 app-shell 레이아웃(1280px) 및 문서를 반영했다.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-06-22 16:59:36 +09:00
parent f8c954f24b
commit dbf02241e2
56 changed files with 6732 additions and 183 deletions

174
docs/api-integration.md Normal file
View File

@@ -0,0 +1,174 @@
# 옆 프로젝트 API 연동 가이드
UADECK v4부터 덱 데이터는 **공유 코드 + REST API**로 제공합니다.
URL 쿼리(`?cards=`) 방식은 사용하지 않습니다.
## 공식 엔드포인트
```http
GET https://uniare.sori.studio/api/v1/decks/:shareCode
```
### 응답 예시
```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": 1,
"displayName": "닉네임",
"avatarUrl": null
},
"createdAt": "2026-06-19T12:00:00Z",
"updatedAt": "2026-06-19T12:00:00Z",
"viewCount": 42,
"isPublic": true
}
```
## shareCode 얻는 방법
| 방법 | 예시 |
|---|---|
| UADECK 공유 버튼 | `https://uniare.sori.studio/d/x7Kp9m2Q` |
| URL 파라미터 | `cardList.html?d=x7Kp9m2Q` |
| 코드만 | `x7Kp9m2Q` |
## 클라이언트 라이브러리
프로젝트에 포함된 파일:
```
integrations/uadeck-deck-client.js
```
### 브라우저
```html
<script src="https://uniare.sori.studio/integrations/uadeck-deck-client.js"></script>
<script>
const client = new UaDeckDeckClient({
baseUrl: 'https://uniare.sori.studio/api/v1',
imageBase: 'https://uniare.sori.studio',
});
// 공유 링크에서 덱 로드
const deck = await client.fetchFromUrl('https://uniare.sori.studio/d/x7Kp9m2Q');
deck.cards.forEach((card) => {
console.log(card.number, card.count, card.imageUrl);
});
</script>
```
### Node.js / ESM
```javascript
import { UaDeckDeckClient } from './integrations/uadeck-deck-client.js';
const client = new UaDeckDeckClient({
baseUrl: 'https://uniare.sori.studio/api/v1',
});
const deck = await client.fetchDeck('x7Kp9m2Q');
```
### fetch 직접 사용
```javascript
const res = await fetch('https://uniare.sori.studio/api/v1/decks/x7Kp9m2Q');
const deck = await res.json();
if (!res.ok) throw new Error(deck.error);
```
## 이미지 URL 규칙
API의 `imgSrc`는 상대 경로입니다. 전체 URL:
```
https://uniare.sori.studio/images{imgSrc}
```
예: `imgSrc: "/ua36/UA36BT_MCR-1-052.png"`
→ `https://uniare.sori.studio/images/ua36/UA36BT_MCR-1-052.png`
`UaDeckDeckClient`는 `card.imageUrl`에 자동 변환합니다.
## CORS
다른 도메인에서 브라우저 `fetch`를 쓰려면 UADECK 서버 `.env`의 `CORS_ORIGINS`에 해당 origin을 추가하세요.
```
CORS_ORIGINS=https://your-app.sori.studio,https://uniare.sori.studio
```
서버 간 호출(Node, Python 등)은 CORS가 필요 없습니다.
## 마이그레이션 (기존 ?cards= 코드)
**Before (폐기):**
```javascript
const params = new URLSearchParams(location.search);
const cards = JSON.parse(decodeURIComponent(params.get('cards')));
```
**After:**
```javascript
const code = UaDeckDeckClient.parseShareCode(location.href)
|| new URLSearchParams(location.search).get('d');
const client = new UaDeckDeckClient({ baseUrl: '/api/v1' });
const deck = await client.fetchDeck(code);
const cards = deck.cards;
```
## 프리셋(메타 덱) 목록
```http
GET /api/v1/decks/presets
```
```json
{
"presets": [
{
"share_code": "a7f3b2c1",
"series": "ua36",
"series_name": "마크로스",
"name": "공식 블루 템플릿",
"view_count": 120
}
]
}
```
프리셋도 동일하게 `GET /decks/:shareCode`로 상세 조회합니다.
## 에러 코드
| HTTP | 의미 |
|---|---|
| 404 | shareCode 없음 |
| 403 | 비공개 덱 |
| 410 | 만료된 임시 공유 (비회원 30일) |