# Implementation Plan: Studio Wallet (지갑) Pages

## Task Type
- [x] Frontend
- [ ] Backend (API endpoints assumed to exist or will be created separately)
- [ ] Fullstack

---

## Research Summary

### Korean Creator Platform Patterns (CHZZK, SOOP, Toonation, YouTube)
- All major platforms use **separate sub-pages** under a "Studio" hub: 수익 요약 / 후원 내역 / 정산 내역 / 정산 정보
- CHZZK: 수익창출 → 정산내역, 정산정보, 후원내역, 구독내역, 광고수익
- SOOP: 별풍선 환전, 광고수익 환전, 구독수익 (각각 별도 플로우)
- Toonation: 대시보드(잔액) + 정산등록 + 정산신청(별도)

### Tax & Fee Structure (Korean regulations)
- **원천징수 3.3%** (소득세 3% + 지방소득세 0.3%) — 사업소득 기준
- **부가세**: 물적시설 없는 1인 크리에이터 = 면세사업자 (VAT 없음)
- **플랫폼 수수료**: 설정값으로 관리 (backend config, 추후 결정)

### Design Decision: 3-Page Structure
현재 사이드바에 이미 정의된 3개 메뉴를 그대로 유지:

| 메뉴 | 경로 | 역할 |
|------|------|------|
| **잔액** | `/studio/wallet/balance` | 현재 잔액 요약 + 최근 내역 |
| **수익** | `/studio/wallet/revenue` | 후원 수입 상세 내역 (기간별) |
| **출금** | `/studio/wallet/withdraw` | 출금 신청 + 출금 내역 |

---

## Technical Solution

### Architecture
- 각 페이지는 `'use client'` 클라이언트 컴포넌트
- `fetchApi<T>()` → `/api/studio/wallet/[...path]` Route Handler → Backend
- 기존 `charge-logs` 패턴 기반 (상태 관리, 기간 필터, 페이지네이션)
- BEM 클래스 네이밍, studio-page 공통 스타일 활용
- 반응형: PC 그리드 테이블 + 모바일 DL 레이아웃

### Balance Data Model (DropdownData 확장)
기존 `DropdownData`의 `spendableBalance`(P), `withdrawableBalance`(M) 활용.
각 페이지별 전용 API 응답 타입 신규 생성.

---

## Implementation Steps

### Step 1: Type Definitions

**File: `types/response/wallet/balance.ts`** (NEW)
```typescript
export interface WalletBalanceResponse {
  spendableBalance: number;       // 포인트 (P) 잔액
  withdrawableBalance: number;    // 머니 (M) 잔액
  totalEarned: number;            // 누적 수익
  totalWithdrawn: number;         // 누적 출금
  pendingWithdrawal: number;      // 출금 대기 중 금액
  recentTransactions: {
    id: number;
    type: 'donation_received'|'withdrawal'|'fee'|'adjustment';
    amount: number;
    balance: number;
    description: string;
    createdAt: string;
  }[];
}
```

**File: `types/response/wallet/revenue.ts`** (NEW)
```typescript
export interface WalletRevenueResponse {
  total: number;
  summary: {
    grossAmount: number;         // 총 후원 금액
    platformFee: number;         // 플랫폼 수수료
    netAmount: number;           // 순수익 (수수료 차감)
  };
  list: {
    id: number;
    donorName: string;
    donorSID: string|null;
    grossAmount: number;
    platformFee: number;
    netAmount: number;
    type: 'donation'|'crew_donation';
    crewName: string|null;
    createdAt: string;
  }[];
}
```

**File: `types/response/wallet/withdraw.ts`** (NEW)
```typescript
export interface WalletWithdrawHistoryResponse {
  total: number;
  withdrawableBalance: number;
  hasBankAccount: boolean;
  list: {
    id: number;
    requestedAmount: number;     // 신청 금액
    withholdingTax: number;      // 원천징수 (3.3%)
    netAmount: number;           // 실수령액
    status: 'Pending'|'Processing'|'Completed'|'Rejected';
    bankName: string;
    accountNumber: string;       // 마스킹 처리 (뒤 4자리만)
    requestedAt: string;
    completedAt: string|null;
    rejectedReason: string|null;
  }[];
}

export interface WithdrawRequest {
  amount: number;
}
```

---

### Step 2: Shared Layout & Constants

**File: `app/studio/wallet/layout.tsx`** (NEW)
```typescript
// 단순 children 패스스루 + style import
import './style.scss';
export default function WalletLayout({ children }) {
  return children;
}
```

**File: `app/studio/wallet/constants.ts`** (NEW)
```typescript
export const PERIOD_TABS = [
  { label: '오늘', value: 0 },
  { label: '1주일', value: 1 },
  { label: '1개월', value: 2 },
  { label: '3개월', value: 3 },
  { label: '6개월', value: 4 },
];

export const WITHDRAW_STATUS_MAP: Record<string, { label: string; cls: string }> = {
  Pending:    { label: '대기',   cls: 'status--pending' },
  Processing: { label: '처리중', cls: 'status--processing' },
  Completed:  { label: '완료',   cls: 'status--completed' },
  Rejected:   { label: '거절',   cls: 'status--rejected' },
};

export const REVENUE_TYPE_MAP: Record<string, string> = {
  donation: '후원',
  crew_donation: '크루 후원',
};

export const WITHHOLDING_TAX_RATE = 0.033; // 3.3%
export const MIN_WITHDRAW_AMOUNT = 40000;  // 최소 출금 금액
```

---

### Step 3: Balance Page (잔액)

**File: `app/studio/wallet/balance/page.tsx`** (NEW)

**Layout:**
```
┌─────────────────────────────────────────────┐
│  잔액 현황                                   │
├───────────┬───────────┬────────────────────────┤
│  머니(M)  │ 누적수익  │ 누적출금               │
│  80,000   │ 350,000   │ 270,000                │
├───────────┴───────────┴────────────────────────┤
│                           [출금하기]            │
├─────────────────────────────────────────────────┤
│  최근 거래 내역                                  │
│  일시 | 유형 | 내용 | 금액 | 잔액                │
│  ...                                            │
│  ...                                            │
└─────────────────────────────────────────────────┘
```

**구현 내용:**
- 3개 Summary Card: M잔액(출금 가능), 누적수익, 누적출금
- 빠른 액션: 출금하기 (`/studio/wallet/withdraw` 이동)
- 최근 거래 10건 (페이지네이션 없음, 간략 리스트)
- API: `GET /api/studio/wallet/balance`

---

### Step 4: Revenue Page (수익)

**의존성 추가:** `npm install recharts` (React 친화적 차트 라이브러리, ~45KB gzip)

**File: `app/studio/wallet/revenue/page.tsx`** (NEW)

**Layout:**
```
┌─────────────────────────────────────────────────┐
│  수익 내역                                       │
├────────────────┬────────────────┬─────────────────┤
│ 총 후원 금액   │ 플랫폼 수수료  │ 순수익          │
│ 500,000원      │ -50,000원      │ 450,000원       │
├───────────────────────────────────────────────────┤
│  [오늘] [1주일] [1개월] [3개월] [6개월]            │
├───────────────────────────────────────────────────┤
│  수익 추이 (AreaChart)                            │
│  ┌─────────────────────────────────────────────┐ │
│  │    ╱\                                       │ │
│  │   ╱  \      ╱\   ╱\                        │ │
│  │  ╱    \    ╱  ╱    \                       │ │
│  │ ╱      \  ╱  ╱      ───                    │ │
│  │╱        ╲╱  ╱                               │ │
│  └──┬──┬──┬──┬──┬──┬──┬──┬──┬──┬──┬──┬──┬──┘ │
│     4/1 4/2 4/3 4/4 ...                         │
│  ■ 순수익  ■ 수수료                              │
├───────────────────────────────────────────────────┤
│  합계: 128                                        │
├───────────────────────────────────────────────────┤
│  일시 | 후원자 | 유형 | 후원금액 | 수수료 | 순수익  │
│  04.14 | 유저A | 후원 | 10,000  | -1,000 | 9,000  │
│  ...                                              │
├───────────────────────────────────────────────────┤
│  < 1 2 3 4 5 >                                    │
└───────────────────────────────────────────────────┘
```

**차트 구현:**
- `recharts`의 `AreaChart` + `ResponsiveContainer` 사용
- X축: 날짜 (기간별 자동 그룹핑 — 일간/주간/월간)
- Y축: 금액 (toLocaleString 포맷)
- Area 2개: 순수익 (primary color, fill opacity 0.3), 수수료 (muted color, fill opacity 0.15)
- Tooltip: 날짜 + 순수익 + 수수료 금액 표시
- 높이 고정 280px, 반응형 너비
- API: `GET /api/studio/wallet/revenue/chart?type={period}` (차트용 집계 데이터)

**차트 응답 타입 추가 (`types/response/wallet/revenue.ts`):**
```typescript
export interface RevenueChartItem {
  date: string;           // '2026-04-01'
  grossAmount: number;
  platformFee: number;
  netAmount: number;
}
```

**구현 내용:**
- Summary 3칸: 총 후원 금액, 플랫폼 수수료, 순수익 (선택 기간 기준)
- 기간 필터 탭 (charge-logs와 동일 패턴)
- 수익 추이 차트 (AreaChart — 기간 필터 연동)
- 테이블: 일시, 후원자, 유형(후원/크루후원), 후원금액, 수수료, 순수익
- 페이지네이션 (20건/페이지)
- 반응형: PC grid + Mobile dl (차트는 모바일에서 높이 200px로 축소)
- API: `GET /api/studio/wallet/revenue?type={period}&page={n}&perPage=20`

---

### Step 5: Withdrawal Page (출금)

**File: `app/studio/wallet/withdraw/page.tsx`** (NEW)

**Layout:**
```
┌─────────────────────────────────────────────────┐
│  출금                                            │
├─────────────────────────────────────────────────┤
│  출금 가능 잔액: 80,000원 (M)                    │
│                                                  │
│  출금 금액  [_______________] 원                  │
│                                                  │
│  ┌─ 예상 차감 ──────────────────┐               │
│  │ 신청 금액      80,000원     │               │
│  │ 원천징수(3.3%) -2,640원     │               │
│  │ 실수령액       77,360원     │               │
│  └─────────────────────────────┘               │
│                                                  │
│  입금 계좌: 국민은행 ****5678 (홍길동)           │
│  [계좌 변경 →]                                   │
│                                                  │
│  ※ 최소 출금 금액: 40,000원                      │
│  ※ 원천징수 3.3% (소득세 3% + 지방소득세 0.3%)   │
│  ※ 매월 10일까지 신청 → 당월 말 입금              │
│                                                  │
│  [출금 신청]                                     │
├─────────────────────────────────────────────────┤
│  출금 내역                                       │
│  합계: 15     [오늘] [1주일] [1개월] [3개월] [6개월] │
├─────────────────────────────────────────────────┤
│  신청일 | 금액 | 원천징수 | 실수령 | 계좌 | 상태  │
│  ...                                             │
├─────────────────────────────────────────────────┤
│  < 1 2 3 4 5 >                                   │
└─────────────────────────────────────────────────┘
```

**구현 내용:**
- 상단: 출금 가능 잔액 강조 표시
- 출금 신청 폼: 금액 입력 → 실시간 원천징수/실수령액 계산 프리뷰
- 계좌 정보 표시 (마스킹) + 정산 페이지 링크
- 안내 문구 (최소 금액, 세금, 일정)
- 계좌 미등록 시: 경고 + 계좌 등록 유도
- 하단: 출금 내역 테이블 (기간 필터 + 페이지네이션)
- API: `GET /api/studio/wallet/withdraw?type={period}&page={n}&perPage=20`
- API: `POST /api/studio/wallet/withdraw` (출금 신청)

---

### Step 6: SCSS Styling

**File: `app/studio/wallet/style.scss`** (NEW)

**BEM Block: `.wallet`**

```scss
// ── Summary Cards ──
.wallet__cards            // display: grid; grid-template-columns: repeat(auto-fit, minmax(160px, 1fr)); gap: 16px;
.wallet__card             // border, radius 8px, padding 20px
.wallet__card-label       // 0.8125rem, muted color
.wallet__card-value       // 1.5rem, font-weight 700
.wallet__card-value--point   // P color
.wallet__card-value--money   // M color (brand)
.wallet__card-value--danger  // negative amounts

// ── Actions ──
.wallet__actions          // flex, gap 8px, justify center
.wallet__action-btn       // studio-page__btn 스타일 재사용

// ── Filter ──
.wallet__header           // flex, space-between (charge-logs__header 패턴)
.wallet__summary          // 합계 텍스트
.wallet__tabs             // flex, button 탭 (charge-logs__tabs 패턴)

// ── Table ──
.wallet__table-wrap       // studio-page__table-wrap 재사용
.wallet__table            // studio-page__table 재사용 + 커스텀 컬럼
.wallet__amount--plus     // color success
.wallet__amount--minus    // color danger

// ── Withdraw Form ──
.wallet__withdraw-form    // border, radius 8px, padding 24px, max-width 560px
.wallet__withdraw-balance // 잔액 강조 (1.25rem, font-weight 600)
.wallet__withdraw-field   // label + input 그룹
.wallet__withdraw-input   // crew-widget-form__input 패턴
.wallet__withdraw-preview // 차감 프리뷰 박스 (muted bg, border)
.wallet__withdraw-row     // flex, justify space-between
.wallet__withdraw-total   // font-weight 700, border-top
.wallet__withdraw-info    // 안내문 (0.8125rem, muted)
.wallet__withdraw-account // 계좌 정보 표시
.wallet__withdraw-submit  // studio-page__btn--primary

// ── Status Badges ──
.wallet__status--pending     // color warning
.wallet__status--processing  // color primary
.wallet__status--completed   // color success
.wallet__status--rejected    // color danger

// ── Responsive ──
@media (max-width: 768px)
  .wallet__cards: grid 2 columns
  Table: hide ol, show dl
```

---

### Step 7: API Route Handler

**File: `app/api/studio/wallet/[...path]/route.ts`** (NEW — 또는 기존 `/api/studio/[...path]` 활용)

기존 `app/api/studio/[...path]/route.ts` 프록시가 있으면 별도 생성 불필요.
없으면 동일한 프록시 패턴으로 생성:

```typescript
// GET/POST → fetchJson('/api/studio/wallet/{path}')
```

---

## Key Files

| File | Operation | Description |
|------|-----------|-------------|
| `types/response/wallet/balance.ts` | **Create** | 잔액 API 응답 타입 |
| `types/response/wallet/revenue.ts` | **Create** | 수익 API 응답 타입 |
| `types/response/wallet/withdraw.ts` | **Create** | 출금 요청/응답 타입 |
| `app/studio/wallet/layout.tsx` | **Create** | style.scss import용 레이아웃 |
| `app/studio/wallet/constants.ts` | **Create** | 공통 상수 (탭, 상태맵, 세율) |
| `app/studio/wallet/style.scss` | **Create** | 전체 wallet 스타일 |
| `app/studio/wallet/balance/page.tsx` | **Create** | 잔액 현황 페이지 |
| `app/studio/wallet/revenue/page.tsx` | **Create** | 수익 내역 페이지 |
| `app/studio/wallet/withdraw/page.tsx` | **Create** | 출금 신청 + 내역 페이지 |
| `app/api/studio/wallet/[...path]/route.ts` | **Create** (필요 시) | API 프록시 |
| `app/studio/Sidebar.tsx` | None | 이미 3개 메뉴 정의됨 |

---

## Risks and Mitigation

| Risk | Mitigation |
|------|------------|
| Backend API 미존재 | Mock 데이터로 UI 먼저 구현, API 연동은 후속 작업 |
| 플랫폼 수수료율 미확정 | `constants.ts`에 변수화, backend config에서 받아오도록 설계 |
| 원천징수 계산 정확성 | Frontend는 프리뷰만 표시, 실제 계산은 Backend에서 수행 |
| 계좌 등록 미구현 | 출금 페이지에서 정산 → 계좌관리 페이지로 유도 |
| 충전 팝업 연동 | 기존 `/charge` 페이지 팝업 패턴 그대로 활용 |

---

## Implementation Order

1. **Types** → constants 먼저 (의존성 없음)
2. **layout.tsx** + **style.scss** (공통 기반)
3. **balance/page.tsx** (가장 단순, 전체 구조 검증)
4. **revenue/page.tsx** (charge-logs 패턴 복사 + 커스터마이즈)
5. **withdraw/page.tsx** (폼 + 리스트 복합, 가장 복잡)
6. **API route handler** (필요 시)

---

## Notes
- Backend API 스펙은 별도 협의 필요 (이 계획은 Frontend만 다룸)
- 정산 메뉴 (계좌관리, 세금계산서)는 별도 계획으로 분리
- 출금 신청 시 confirm dialog 필수 (금액 + 실수령액 재확인)
- SESSION_ID: N/A (external model wrapper 미사용)