- 파일 경로에서 react/ 접두사 제거 - SubscriptionManagement.tsx 수정 대상 추가 - buildApiUrl() 패턴 적용 - 'use server' 제약 반영 (브라우저 API 사용 불가) - HttpOnly 쿠키 + API proxy 인증 패턴 반영 - toast 메시지 변경 대상 명시
9.2 KiB
[프론트엔드] 구독관리 내보내기 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, 파일 없음):
{
"success": true,
"message": "내보내기 요청이 접수되었습니다.",
"data": {
"id": 1,
"status": "pending",
"file_path": null,
"file_name": null,
"file_size": null
}
}
변경 후 응답 (status: completed, 파일 즉시 생성):
{
"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:
export async function requestDataExport(
exportType: string = 'all'
): Promise<ActionResult<{ id: number; status: string }>> {
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 (동일):
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으로 가져와 반환한다.
import { buildApiUrl } from '@/lib/api/query-params';
// ===== 데이터 내보내기 요청 =====
export async function requestDataExport(
exportType: string = 'all'
): Promise<ActionResult<{ id: number; status: string; file_name: string | null }>> {
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<ActionResult<{ base64: string; fileName: string }>> {
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 수정
두 파일 모두 동일하게 수정한다.
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