현재 사이드바에 이미 정의된 3개 메뉴를 그대로 유지:
| 메뉴 | 경로 | 역할 |
|---|---|---|
| 잔액 | /studio/wallet/balance |
현재 잔액 요약 + 최근 내역 |
| 수익 | /studio/wallet/revenue |
후원 수입 상세 내역 (기간별) |
| 출금 | /studio/wallet/withdraw |
출금 신청 + 출금 내역 |
'use client' 클라이언트 컴포넌트fetchApi<T>() → /api/studio/wallet/[...path] Route Handler → Backendcharge-logs 패턴 기반 (상태 관리, 기간 필터, 페이지네이션)기존 DropdownData의 spendableBalance(P), withdrawableBalance(M) 활용.
각 페이지별 전용 API 응답 타입 신규 생성.
File: types/response/wallet/balance.ts (NEW)
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)
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)
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;
}
File: app/studio/wallet/layout.tsx (NEW)
// 단순 children 패스스루 + style import
import './style.scss';
export default function WalletLayout({ children }) {
return children;
}
File: app/studio/wallet/constants.ts (NEW)
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; // 최소 출금 금액
File: app/studio/wallet/balance/page.tsx (NEW)
Layout:
┌─────────────────────────────────────────────┐
│ 잔액 현황 │
├───────────┬───────────┬────────────────────────┤
│ 머니(M) │ 누적수익 │ 누적출금 │
│ 80,000 │ 350,000 │ 270,000 │
├───────────┴───────────┴────────────────────────┤
│ [출금하기] │
├─────────────────────────────────────────────────┤
│ 최근 거래 내역 │
│ 일시 | 유형 | 내용 | 금액 | 잔액 │
│ ... │
│ ... │
└─────────────────────────────────────────────────┘
구현 내용:
/studio/wallet/withdraw 이동)GET /api/studio/wallet/balance의존성 추가: 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 사용GET /api/studio/wallet/revenue/chart?type={period} (차트용 집계 데이터)차트 응답 타입 추가 (types/response/wallet/revenue.ts):
export interface RevenueChartItem {
date: string; // '2026-04-01'
grossAmount: number;
platformFee: number;
netAmount: number;
}
구현 내용:
GET /api/studio/wallet/revenue?type={period}&page={n}&perPage=20File: 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 > │
└─────────────────────────────────────────────────┘
구현 내용:
GET /api/studio/wallet/withdraw?type={period}&page={n}&perPage=20POST /api/studio/wallet/withdraw (출금 신청)File: app/studio/wallet/style.scss (NEW)
BEM Block: .wallet
// ── 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
File: app/api/studio/wallet/[...path]/route.ts (NEW — 또는 기존 /api/studio/[...path] 활용)
기존 app/api/studio/[...path]/route.ts 프록시가 있으면 별도 생성 불필요.
없으면 동일한 프록시 패턴으로 생성:
// GET/POST → fetchJson('/api/studio/wallet/{path}')
| 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개 메뉴 정의됨 |
| Risk | Mitigation |
|---|---|
| Backend API 미존재 | Mock 데이터로 UI 먼저 구현, API 연동은 후속 작업 |
| 플랫폼 수수료율 미확정 | constants.ts에 변수화, backend config에서 받아오도록 설계 |
| 원천징수 계산 정확성 | Frontend는 프리뷰만 표시, 실제 계산은 Backend에서 수행 |
| 계좌 등록 미구현 | 출금 페이지에서 정산 → 계좌관리 페이지로 유도 |
| 충전 팝업 연동 | 기존 /charge 페이지 팝업 패턴 그대로 활용 |