docs: [plans] 이용현황 요청서 프론트엔드 피드백 6건 반영
- 파일 경로에서 react/ 접두사 제거 - SubscriptionManagement.tsx + SubscriptionClient.tsx 모두 수정 대상 명시 - 모든 URL을 buildApiUrl() 사용으로 변경 - use server 파일에서 window.open 불가 안내 추가 - HttpOnly 쿠키 + API Proxy 패턴 준수 명시 - toast 메시지 정합성 수정 (handleExportData) - 체크리스트에 프로젝트 규칙 준수 항목 4개 추가
This commit is contained in:
@@ -4,6 +4,7 @@
|
||||
> **상태**: API 구현 완료, React 구현 대기
|
||||
> **대상**: sam/react
|
||||
> **메뉴 위치**: 사이드바 `이용현황` (`/usage`)
|
||||
> **수정 이력**: 2026-03-18 프론트엔드 피드백 6건 반영
|
||||
|
||||
---
|
||||
|
||||
@@ -296,7 +297,7 @@ export interface SubscriptionInfo {
|
||||
|
||||
## 5. 컴포넌트 구조
|
||||
|
||||
### 5.1 파일 구조
|
||||
### 5.1 파일 구조 및 수정 대상
|
||||
|
||||
```
|
||||
src/app/[locale]/(protected)/
|
||||
@@ -304,31 +305,91 @@ src/app/[locale]/(protected)/
|
||||
└── subscription/page.tsx ← 수정: /usage로 리다이렉트
|
||||
|
||||
src/components/settings/SubscriptionManagement/
|
||||
├── types.ts ← 수정: UsageApiData에 ai_tokens 추가
|
||||
├── actions.ts ← 수정: API_URL → buildApiUrl
|
||||
├── utils.ts ← 수정: transformApiToFrontend 개선
|
||||
├── SubscriptionManagement.tsx ← 수정 또는 교체: AI 토큰 섹션 추가
|
||||
├── SubscriptionClient.tsx ← 수정 또는 교체
|
||||
├── types.ts ← 수정: api_calls 제거, ai_tokens 추가
|
||||
├── actions.ts ← 수정: 모든 URL을 buildApiUrl() 사용
|
||||
├── utils.ts ← 수정: transformApiToFrontend에 AI 토큰 매핑
|
||||
├── SubscriptionManagement.tsx ← 수정: AI 토큰 섹션 추가, api_calls 제거, toast 메시지 수정
|
||||
├── SubscriptionClient.tsx ← 수정: AI 토큰 섹션 추가 (SubscriptionManagement.tsx와 동기화)
|
||||
└── index.ts ← 기존 유지
|
||||
```
|
||||
|
||||
> **주의**: `SubscriptionManagement.tsx`와 `SubscriptionClient.tsx` 모두 수정 대상이다. 두 파일에 동일한 `handleExportData` 로직이 있으며, 주석에 "SubscriptionManagement.tsx 사용 권장"이 있다. 통합 시 하나로 정리하는 것을 권장한다.
|
||||
|
||||
### 5.2 Server Actions 변경
|
||||
|
||||
```typescript
|
||||
// actions.ts — buildApiUrl 사용으로 변경
|
||||
import { buildApiUrl } from '@/lib/api/query-params';
|
||||
> **필수**: 모든 URL 조립에 `buildApiUrl()` 사용. `${API_URL}/api/v1/...` 직접 조합 금지.
|
||||
|
||||
```typescript
|
||||
// actions.ts
|
||||
'use server';
|
||||
|
||||
import { buildApiUrl } from '@/lib/api/query-params';
|
||||
import { executeServerAction, type ActionResult } from '@/lib/api/execute-server-action';
|
||||
|
||||
// 사용량 조회
|
||||
export async function getUsage(): Promise<ActionResult<UsageApiData>> {
|
||||
return executeServerAction({
|
||||
url: buildApiUrl('/api/v1/subscriptions/usage'),
|
||||
errorMessage: '사용량 정보를 불러오는데 실패했습니다.',
|
||||
});
|
||||
}
|
||||
|
||||
// 구독 정보 조회
|
||||
export async function getCurrentSubscription(): Promise<ActionResult<SubscriptionApiData>> {
|
||||
return executeServerAction({
|
||||
url: buildApiUrl('/api/v1/subscriptions/current'),
|
||||
errorMessage: '구독 정보를 불러오는데 실패했습니다.',
|
||||
});
|
||||
}
|
||||
|
||||
// 구독 취소
|
||||
export async function cancelSubscription(
|
||||
id: number,
|
||||
reason?: string
|
||||
): Promise<ActionResult> {
|
||||
return executeServerAction({
|
||||
url: buildApiUrl(`/api/v1/subscriptions/${id}/cancel`),
|
||||
method: 'POST',
|
||||
body: { reason },
|
||||
errorMessage: '구독 취소에 실패했습니다.',
|
||||
});
|
||||
}
|
||||
|
||||
// 데이터 내보내기 요청
|
||||
export async function requestDataExport(
|
||||
exportType: string = 'all'
|
||||
): Promise<ActionResult<{ id: number; status: string }>> {
|
||||
return executeServerAction({
|
||||
url: buildApiUrl('/api/v1/subscriptions/export'),
|
||||
method: 'POST',
|
||||
body: { export_type: exportType },
|
||||
errorMessage: '내보내기 요청에 실패했습니다.',
|
||||
});
|
||||
}
|
||||
```
|
||||
|
||||
> `getSubscriptionData()` 함수에서 `getCurrentSubscription()` + `getUsage()` 병렬 호출 유지. 단, `transformApiToFrontend()`에서 `ai_tokens` 필드를 매핑해야 한다.
|
||||
### 5.3 자료 내보내기 다운로드 처리
|
||||
|
||||
### 5.3 `/usage/page.tsx` (신규)
|
||||
> **주의**: `actions.ts`는 `'use server'` 파일이므로 `window.open()` 등 브라우저 API 사용 불가.
|
||||
> 이 프로젝트는 **HttpOnly 쿠키 + Next.js API Proxy** 패턴을 사용하므로, `Authorization: Bearer` 직접 전달도 불가.
|
||||
|
||||
**다운로드 URL 조립은 클라이언트 컴포넌트에서 처리**:
|
||||
|
||||
```typescript
|
||||
// SubscriptionManagement.tsx (클라이언트 컴포넌트) 내부에서 처리
|
||||
const handleExportData = async () => {
|
||||
const result = await requestDataExport('all');
|
||||
if (result.success) {
|
||||
// toast 메시지 수정 (기존 '완료되면 알림을 보내드립니다.' 대체)
|
||||
toast.success('자료 내보내기를 요청했습니다. 완료 시 알림으로 안내드립니다.');
|
||||
}
|
||||
};
|
||||
|
||||
// 다운로드가 필요한 경우 Next.js API proxy 경유
|
||||
// /api/proxy/subscriptions/export/{id}/download 형태로 호출
|
||||
```
|
||||
|
||||
### 5.4 `/usage/page.tsx` (신규)
|
||||
|
||||
```typescript
|
||||
'use client';
|
||||
@@ -336,7 +397,6 @@ export async function getUsage(): Promise<ActionResult<UsageApiData>> {
|
||||
import { useEffect, useState } from 'react';
|
||||
import { getSubscriptionData } from '@/components/settings/SubscriptionManagement/actions';
|
||||
import type { SubscriptionInfo } from '@/components/settings/SubscriptionManagement/types';
|
||||
// 기존 SubscriptionManagement 컴포넌트 재사용
|
||||
import { SubscriptionManagement } from '@/components/settings/SubscriptionManagement';
|
||||
|
||||
export default function UsagePage() {
|
||||
@@ -354,7 +414,7 @@ export default function UsagePage() {
|
||||
}
|
||||
```
|
||||
|
||||
### 5.4 `/subscription/page.tsx` (리다이렉트)
|
||||
### 5.5 `/subscription/page.tsx` (리다이렉트)
|
||||
|
||||
```typescript
|
||||
'use client';
|
||||
@@ -406,24 +466,44 @@ export function formatPeriod(period: string): string {
|
||||
| `types.ts` | `api_calls` 인터페이스 | `ai_tokens`로 대체 |
|
||||
| `types.ts` | `apiCallsUsed`, `apiCallsLimit` | 제거 |
|
||||
| `SubscriptionManagement.tsx` | API 호출 수 Progress Bar | AI 토큰 Progress Bar로 대체 |
|
||||
| `SubscriptionManagement.tsx` | `'완료되면 알림을 보내드립니다.'` toast | `'자료 내보내기를 요청했습니다. 완료 시 알림으로 안내드립니다.'`로 변경 |
|
||||
| `SubscriptionClient.tsx` | `handleExportData` 중복 로직 | `SubscriptionManagement.tsx`로 통합 권장 |
|
||||
| `utils.ts` | `apiCallsUsed/Limit` 변환 | `aiTokens` 변환으로 대체 |
|
||||
| `actions.ts` | `${API_URL}/...` 직접 조합 | `buildApiUrl()` 사용으로 변경 |
|
||||
|
||||
---
|
||||
|
||||
## 8. 체크리스트
|
||||
|
||||
### 타입/데이터
|
||||
|
||||
- [ ] `types.ts` — `UsageApiData`에서 `api_calls` 제거, `ai_tokens` + `subscription` 변경
|
||||
- [ ] `utils.ts` — `transformApiToFrontend()`에 AI 토큰 매핑 추가
|
||||
- [ ] `actions.ts` — 모든 URL을 `buildApiUrl()` 사용으로 변경
|
||||
|
||||
### UI 컴포넌트
|
||||
|
||||
- [ ] `SubscriptionManagement.tsx` — AI 토큰 섹션 추가, API 호출 수 섹션 제거
|
||||
- [ ] `SubscriptionManagement.tsx` — 내보내기 toast 메시지 수정
|
||||
- [ ] `SubscriptionClient.tsx` — `SubscriptionManagement.tsx`와 기능 통합 정리
|
||||
- [ ] Progress Bar — 사용율별 색상 변경 (0~60 파랑, 60~80 노랑, 80~100 주황, 100+ 빨강)
|
||||
- [ ] `by_model` 테이블 — 모델별 호출수/토큰/비용 표시
|
||||
- [ ] 한도 초과 경고 배지 — `is_over_limit`/`warning_threshold` 기반
|
||||
- [ ] `/usage/page.tsx` 신규 생성
|
||||
- [ ] `/subscription/page.tsx` → `/usage` 리다이렉트
|
||||
- [ ] `actions.ts` — `buildApiUrl()` 사용으로 변경
|
||||
- [ ] 구독 정보 null 처리 (미가입 테넌트)
|
||||
- [ ] 로딩 스켈레톤 표시
|
||||
|
||||
### 페이지/라우팅
|
||||
|
||||
- [ ] `/usage/page.tsx` 신규 생성
|
||||
- [ ] `/subscription/page.tsx` → `/usage` 리다이렉트
|
||||
|
||||
### 프로젝트 규칙 준수
|
||||
|
||||
- [ ] `buildApiUrl()` 필수 사용 (`${API_URL}` 직접 조합 금지)
|
||||
- [ ] `'use server'` 파일에서 브라우저 API (`window.open` 등) 사용 금지
|
||||
- [ ] 다운로드는 Next.js API Proxy (`/api/proxy/...`) 경유 또는 서버 액션에서 처리
|
||||
- [ ] `Authorization: Bearer` 직접 전달 금지 (HttpOnly 쿠키 패턴)
|
||||
|
||||
---
|
||||
|
||||
## 관련 문서
|
||||
|
||||
Reference in New Issue
Block a user