/** * 페이지네이션 조회 전용 Server Action 래퍼 * * executeServerAction + toPaginationMeta 조합을 통합하여 * 50+ 파일에서 반복되는 15~25줄 패턴을 5~8줄로 줄입니다. * * 적용 범위: 신규 코드만 (기존 코드 마이그레이션 없음) * * @example * ```typescript * // Before: ~20줄 * const result = await executeServerAction({ * url: `${API_URL}/api/v1/bills?${queryString}`, * transform: (data: BillPaginatedResponse) => ({ * items: (data?.data || []).map(transformApiToFrontend), * pagination: { currentPage: data?.current_page || 1, ... }, * }), * errorMessage: '어음 목록 조회에 실패했습니다.', * }); * return { success: result.success, data: result.data?.items || [], ... }; * * // After: ~5줄 * return executePaginatedAction({ * url: buildApiUrl('/api/v1/bills', params), * transform: transformApiToFrontend, * errorMessage: '어음 목록 조회에 실패했습니다.', * }); * ``` */ import { executeServerAction } from './execute-server-action'; import { toPaginationMeta, type PaginatedApiResponse, type PaginationMeta } from './types'; // ===== 반환 타입 ===== export interface PaginatedActionResult { success: boolean; data: T[]; pagination: PaginationMeta; error?: string; __authError?: boolean; } // ===== 옵션 타입 ===== interface PaginatedActionOptions { /** API URL (전체 경로) */ url: string; /** 개별 아이템 변환 함수 (API 응답 아이템 → 프론트엔드 타입) */ transform: (item: TApi) => TResult; /** 실패 시 기본 에러 메시지 */ errorMessage: string; } const DEFAULT_PAGINATION: PaginationMeta = { currentPage: 1, lastPage: 1, perPage: 20, total: 0, }; /** * 페이지네이션 조회 Server Action 실행 * * executeServerAction으로 API 호출 → data 배열에 transform 적용 → toPaginationMeta 변환 */ export async function executePaginatedAction( options: PaginatedActionOptions ): Promise> { const { url, transform, errorMessage } = options; const result = await executeServerAction>({ url, errorMessage, }); if (!result.success || !result.data) { return { success: result.success, data: [], pagination: DEFAULT_PAGINATION, error: result.error, __authError: result.__authError, }; } return { success: true, data: (result.data.data || []).map(transform), pagination: toPaginationMeta(result.data), }; }