# [프론트엔드] 구독관리 내보내기 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` | API Key 필수 | 생성된 Excel 파일 다운로드 (바이너리 응답) | > **인증**: 다운로드 엔드포인트는 `ApiKeyMiddleware` 내부에 있어 API Key가 필요하다. 브라우저에서 직접 URL 접근 불가 — **Next.js API proxy 경유 필수**. ### 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. 수정 대상 파일 | 파일 | 설명 | 수정 내용 | |------|------|----------| | `src/components/settings/SubscriptionManagement/actions.ts` | 서버 액션 | `requestDataExport` 응답 처리, 다운로드 서버 액션 추가 | | `src/components/settings/SubscriptionManagement/SubscriptionManagement.tsx` | 메인 컴포넌트 | `handleExportData` 로직 + toast 메시지 변경 | | `src/components/settings/SubscriptionManagement/SubscriptionClient.tsx` | 클라이언트 컴포넌트 | `handleExportData` 로직 + toast 메시지 변경 (동일 패턴) | > **주의**: `SubscriptionManagement.tsx`와 `SubscriptionClient.tsx` 양쪽 모두에 동일한 `handleExportData` 로직과 `'완료되면 알림을 보내드립니다'` toast 메시지가 있다. **두 파일 모두 수정 필요**. --- ## 4. 프론트엔드 수정 가이드 ### 4.1 현재 코드 **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 }) => ({ id: data.id, status: data.status }), errorMessage: '내보내기 요청에 실패했습니다.', }); } ``` **SubscriptionManagement.tsx / 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); } }, []); ``` ### 4.2 수정 방향 #### actions.ts 수정 `'use server'` 파일이므로 브라우저 API(`window`, `document`) 사용 불가. 서버 액션에서 다운로드 데이터를 blob으로 가져와 반환한다. ```typescript import { buildApiUrl } from '@/lib/api/query-params'; // ===== 데이터 내보내기 요청 ===== export async function requestDataExport( exportType: string = 'all' ): Promise> { return executeServerAction({ url: buildApiUrl('/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: '내보내기 요청에 실패했습니다.', }); } // ===== 내보내기 파일 다운로드 (서버 액션) ===== export async function downloadExportFile( exportId: number ): Promise> { return executeServerAction({ url: buildApiUrl(`/api/v1/subscriptions/export/${exportId}/download`), // 바이너리 응답 처리 — 기존 프로젝트 파일 다운로드 패턴에 맞게 구현 // HttpOnly 쿠키가 서버 액션에서 자동 전달됨 errorMessage: '파일 다운로드에 실패했습니다.', }); } ``` > **대안: Next.js API proxy 경로 추가** > > 서버 액션 대신 `/api/proxy/subscriptions/export/{id}/download` 프록시 라우트를 추가하면 `window.open(proxyUrl)` 방식으로 직접 다운로드 가능. 기존 프로젝트에서 파일 다운로드를 어떤 방식으로 처리하는지에 따라 선택한다. #### SubscriptionManagement.tsx / 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') { // 다운로드 처리 — 프로젝트 패턴에 맞게 구현 // 방법 A) 서버 액션으로 blob 가져오기 // 방법 B) Next.js API proxy URL로 window.open toast.success('Excel 파일이 다운로드됩니다.'); } else if (result.data.status === 'failed') { toast.error('내보내기 처리 중 오류가 발생했습니다.'); } } else { toast.error(result.error || '내보내기 요청에 실패했습니다.'); } } catch (_error) { toast.error('서버 오류가 발생했습니다.'); } finally { setIsExporting(false); } }, []); ``` **변경 포인트 요약:** - `requestDataExport` 응답에서 `status` 분기 처리 추가 - toast 메시지: `'완료되면 알림을 보내드립니다'` → `'Excel 파일이 다운로드됩니다.'` - 다운로드 처리 로직 추가 (프로젝트 패턴에 따라 A 또는 B 방식 선택) ### 4.3 다운로드 구현 방식 선택 | 방식 | 구현 위치 | 장점 | 단점 | |------|----------|------|------| | **A) 서버 액션** | `actions.ts` | 기존 패턴 일관성 | blob → base64 변환 필요 | | **B) API Proxy** | `app/api/proxy/` | `window.open` 간단 호출 | 프록시 라우트 추가 필요 | > 기존 프로젝트에서 파일 다운로드를 어떤 패턴으로 처리하는지 확인 후 선택한다. --- ## 5. Excel 파일 내용 생성되는 Excel 파일의 컬럼 구조: | No | 요금제 | 요금제 코드 | 월 요금 | 결제주기 | 시작일 | 종료일 | 상태 | 취소일 | 취소 사유 | |----|--------|-----------|---------|---------|--------|--------|------|--------|----------| | 1 | 스탠다드 | STD | 50,000 | 월간 | 2026-01-01 | 2026-02-01 | 활성 | - | - | --- ## 6. 테스트 체크리스트 - [ ] "자료 내보내기" 버튼 클릭 → Excel 파일 다운로드 확인 - [ ] 다운로드된 파일 열기 → 데이터 정상 확인 - [ ] 연속 클릭 시 중복 차단 메시지 없이 재다운로드 가능 확인 - [ ] 구독 데이터 없는 테넌트에서 빈 Excel 생성 확인 - [ ] `SubscriptionManagement.tsx`와 `SubscriptionClient.tsx` 양쪽 동작 확인 - [ ] 개발서버 URL: `https://dev.codebridge-x.com` → 설정 → 구독관리 --- ## 7. 관련 파일 | 위치 | 파일 | 설명 | |------|------|------| | React | `src/components/settings/SubscriptionManagement/actions.ts` | 서버 액션 (수정 대상) | | React | `src/components/settings/SubscriptionManagement/SubscriptionManagement.tsx` | 메인 컴포넌트 (수정 대상) | | React | `src/components/settings/SubscriptionManagement/SubscriptionClient.tsx` | 클라이언트 컴포넌트 (수정 대상) | | API | `app/Http/Controllers/Api/V1/SubscriptionController.php` | 컨트롤러 (수정 완료) | | API | `app/Services/SubscriptionService.php` | 서비스 — 동기 처리 로직 (수정 완료) | | API | `routes/api/v1/finance.php` | 라우트 — 다운로드 엔드포인트 추가 (수정 완료) | --- **최종 업데이트**: 2026-03-18