From a9bb682c5bc1fbb66733380a3c86718223332003 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EB=B3=B4=EA=B3=A4?= Date: Wed, 18 Mar 2026 14:14:53 +0900 Subject: [PATCH] =?UTF-8?q?docs:=20[changes]=20=EA=B5=AC=EB=8F=85=EA=B4=80?= =?UTF-8?q?=EB=A6=AC=20=EB=82=B4=EB=B3=B4=EB=82=B4=EA=B8=B0=20=ED=94=84?= =?UTF-8?q?=EB=A1=A0=ED=8A=B8=EC=97=94=EB=93=9C=20=EC=88=98=EC=A0=95=20?= =?UTF-8?q?=EA=B0=80=EC=9D=B4=EB=93=9C=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...0318_subscription_export_frontend_guide.md | 236 ++++++++++++++++++ 1 file changed, 236 insertions(+) create mode 100644 changes/20260318_subscription_export_frontend_guide.md diff --git a/changes/20260318_subscription_export_frontend_guide.md b/changes/20260318_subscription_export_frontend_guide.md new file mode 100644 index 0000000..304dc3e --- /dev/null +++ b/changes/20260318_subscription_export_frontend_guide.md @@ -0,0 +1,236 @@ +# [프론트엔드] 구독관리 내보내기 API 변경 안내 + +**날짜:** 2026-03-18 +**작성자:** Claude Code +**대상:** React 프론트엔드 개발자 +**배포:** 개발서버 반영 완료 + +--- + +## 1. 변경 배경 + +구독관리 페이지의 "자료 내보내기" 버튼 클릭 시 백엔드에서 `400 (이미 진행 중인 내보내기가 있습니다)` 오류가 반복 발생했다. + +**원인**: 비동기 처리 Job이 미구현 상태로, `DataExport` 레코드가 `pending`에서 영원히 변경되지 않아 후속 요청이 차단되었다. + +**수정**: 비동기 → **동기 처리**로 전환. `POST /export` 호출 시 즉시 Excel 파일이 생성되고, 응답에 `completed` 상태와 파일 정보가 포함된다. + +--- + +## 2. API 변경 사항 + +### 2.1 변경된 엔드포인트 + +| Method | Path | 변경 내용 | +|--------|------|----------| +| `POST` | `/api/v1/subscriptions/export` | 응답에 `status: 'completed'` + 파일 정보 포함 (기존: `status: 'pending'`) | + +### 2.2 신규 엔드포인트 + +| Method | Path | 설명 | +|--------|------|------| +| `GET` | `/api/v1/subscriptions/export/{id}/download` | 생성된 Excel 파일 다운로드 | + +### 2.3 POST /export 응답 비교 + +**기존 응답** (status: pending, 파일 없음): +```json +{ + "success": true, + "message": "내보내기 요청이 접수되었습니다.", + "data": { + "id": 1, + "status": "pending", + "file_path": null, + "file_name": null, + "file_size": null + } +} +``` + +**변경 후 응답** (status: completed, 파일 즉시 생성): +```json +{ + "success": true, + "message": "내보내기 요청이 접수되었습니다.", + "data": { + "id": 2, + "status": "completed", + "export_type": "all", + "file_path": "exports/subscriptions_287_20260318_141023.xlsx", + "file_name": "subscriptions_287_20260318_141023.xlsx", + "file_size": 4523, + "started_at": "2026-03-18T14:10:23.000000Z", + "completed_at": "2026-03-18T14:10:23.000000Z" + } +} +``` + +> **실패 시** `status: 'failed'`와 `error_message`가 포함된다. + +--- + +## 3. 프론트엔드 수정 가이드 + +### 3.1 현재 코드 (수정 대상) + +**파일:** `react/src/components/settings/SubscriptionManagement/actions.ts` + +```typescript +// 현재: POST 후 toast만 표시 +export async function requestDataExport( + exportType: string = 'all' +): Promise> { + return executeServerAction({ + url: `${API_URL}/api/v1/subscriptions/export`, + method: 'POST', + body: { export_type: exportType }, + transform: (data: { id: number; status: string }) => ({ id: data.id, status: data.status }), + errorMessage: '내보내기 요청에 실패했습니다.', + }); +} +``` + +**파일:** `react/src/components/settings/SubscriptionManagement/SubscriptionClient.tsx` + +```typescript +// 현재: "완료되면 알림을 보내드립니다" (실제로 알림 없음) +const handleExportData = useCallback(async () => { + setIsExporting(true); + try { + const result = await requestDataExport('all'); + if (result.success) { + toast.success('내보내기 요청이 등록되었습니다. 완료되면 알림을 보내드립니다.'); + } else { + toast.error(result.error || '내보내기 요청에 실패했습니다.'); + } + } catch (_error) { + toast.error('서버 오류가 발생했습니다.'); + } finally { + setIsExporting(false); + } +}, []); +``` + +### 3.2 수정 방향 + +POST 응답에서 `id`를 받아 **즉시 다운로드 URL로 리다이렉트**하는 방식으로 변경한다. + +#### actions.ts 수정 + +```typescript +// ===== 데이터 내보내기 요청 ===== +export async function requestDataExport( + exportType: string = 'all' +): Promise> { + return executeServerAction({ + url: `${API_URL}/api/v1/subscriptions/export`, + method: 'POST', + body: { export_type: exportType }, + transform: (data: { id: number; status: string; file_name: string | null }) => ({ + id: data.id, + status: data.status, + file_name: data.file_name, + }), + errorMessage: '내보내기 요청에 실패했습니다.', + }); +} + +// ===== 내보내기 파일 다운로드 URL 생성 ===== +export function getExportDownloadUrl(exportId: number): string { + return `${API_URL}/api/v1/subscriptions/export/${exportId}/download`; +} +``` + +#### SubscriptionClient.tsx 수정 + +```typescript +const handleExportData = useCallback(async () => { + setIsExporting(true); + try { + const result = await requestDataExport('all'); + if (result.success && result.data) { + if (result.data.status === 'completed') { + // 즉시 다운로드 + const downloadUrl = getExportDownloadUrl(result.data.id); + window.open(downloadUrl, '_blank'); + toast.success('Excel 파일이 다운로드됩니다.'); + } else if (result.data.status === 'failed') { + toast.error('내보내기 처리 중 오류가 발생했습니다.'); + } + } else { + toast.error(result.error || '내보내기 요청에 실패했습니다.'); + } + } catch (_error) { + toast.error('서버 오류가 발생했습니다.'); + } finally { + setIsExporting(false); + } +}, []); +``` + +> **참고**: 다운로드 URL은 인증이 필요한 API이므로, `window.open` 대신 `fetch` + `blob` 패턴이 필요할 수 있다. 기존 프로젝트의 파일 다운로드 패턴을 확인하여 적용한다. + +### 3.3 인증이 필요한 경우 (fetch + blob 패턴) + +API가 `auth:sanctum` 미들웨어 아래에 있으므로, 토큰 전달이 필요하다면: + +```typescript +// actions.ts에 다운로드 함수 추가 +export async function downloadExportFile(exportId: number): Promise { + const response = await fetch( + `${API_URL}/api/v1/subscriptions/export/${exportId}/download`, + { + headers: { + 'Authorization': `Bearer ${token}`, // 기존 인증 헤더 패턴 참고 + 'X-API-KEY': apiKey, + }, + } + ); + + if (!response.ok) throw new Error('다운로드 실패'); + + const blob = await response.blob(); + const url = URL.createObjectURL(blob); + const a = document.createElement('a'); + a.href = url; + a.download = `구독관리_${new Date().toISOString().slice(0, 10)}.xlsx`; + a.click(); + URL.revokeObjectURL(url); +} +``` + +--- + +## 4. Excel 파일 내용 + +생성되는 Excel 파일의 컬럼 구조: + +| No | 요금제 | 요금제 코드 | 월 요금 | 결제주기 | 시작일 | 종료일 | 상태 | 취소일 | 취소 사유 | +|----|--------|-----------|---------|---------|--------|--------|------|--------|----------| +| 1 | 스탠다드 | STD | 50,000 | 월간 | 2026-01-01 | 2026-02-01 | 활성 | - | - | + +--- + +## 5. 테스트 체크리스트 + +- [ ] "자료 내보내기" 버튼 클릭 → Excel 파일 다운로드 확인 +- [ ] 다운로드된 파일 열기 → 데이터 정상 확인 +- [ ] 연속 클릭 시 중복 차단 메시지 없이 재다운로드 가능 확인 +- [ ] 구독 데이터 없는 테넌트에서 빈 Excel 생성 확인 +- [ ] 개발서버 URL: `https://dev.codebridge-x.com` → 설정 → 구독관리 + +--- + +## 6. 관련 파일 + +| 위치 | 파일 | 설명 | +|------|------|------| +| React | `src/components/settings/SubscriptionManagement/actions.ts` | 서버 액션 (수정 대상) | +| React | `src/components/settings/SubscriptionManagement/SubscriptionClient.tsx` | 컴포넌트 (수정 대상) | +| API | `app/Http/Controllers/Api/V1/SubscriptionController.php` | 컨트롤러 | +| API | `app/Services/SubscriptionService.php` | 서비스 (동기 처리 로직) | + +--- + +**최종 업데이트**: 2026-03-18