// API 에러 핸들링 헬퍼 유틸리티 // API 요청 실패 시 에러 처리 및 사용자 친화적 메시지 생성 /** * API 에러 클래스 * - 표준 Error를 확장하여 HTTP 상태 코드와 validation errors 포함 */ export class ApiError extends Error { constructor( public status: number, public message: string, public errors?: Record ) { super(message); this.name = 'ApiError'; } } /** * 품목코드 중복 에러 클래스 * - 백엔드에서 400 에러와 함께 duplicate_id, duplicate_code 반환 시 사용 * - 2025-12-11: 백엔드 DuplicateCodeException 대응 */ export class DuplicateCodeError extends ApiError { constructor( public message: string, public duplicateId: number, public duplicateCode: string ) { super(400, message); this.name = 'DuplicateCodeError'; } } /** * API 응답 에러를 처리하고 ApiError를 throw * @param response - fetch Response 객체 * @throws {ApiError} HTTP 상태 코드, 메시지, validation errors 포함 */ export const handleApiError = async (response: Response): Promise => { const data = await response.json().catch(() => ({})); // 401 Unauthorized - 토큰 만료 또는 인증 실패 // ✅ 자동으로 로그인 페이지로 리다이렉트 if (response.status === 401) { console.warn('⚠️ 401 Unauthorized - 로그인 페이지로 이동합니다.'); // 클라이언트 사이드에서만 리다이렉트 if (typeof window !== 'undefined') { window.location.href = '/login'; } throw new ApiError( 401, data.message || '인증이 필요합니다. 로그인 상태를 확인해주세요.', data.errors ); } // 403 Forbidden - 권한 없음 if (response.status === 403) { throw new ApiError( 403, data.message || '접근 권한이 없습니다.', data.errors ); } // 400 Bad Request - 품목코드 중복 에러 체크 // 백엔드 DuplicateCodeException이 duplicate_id, duplicate_code 반환 if (response.status === 400 && data.duplicate_id) { console.warn('⚠️ 품목코드 중복 감지:', { duplicateId: data.duplicate_id, duplicateCode: data.duplicate_code, message: data.message }); throw new DuplicateCodeError( data.message || '해당 품목코드가 이미 존재합니다.', data.duplicate_id, data.duplicate_code ); } // 422 Unprocessable Entity - Validation 에러 if (response.status === 422) { // 상세 validation 에러 로그 출력 console.error('🔴 [API 422 Validation Error]', { message: data.message, errors: data.errors, fullResponse: data }); throw new ApiError( 422, data.message || '입력값을 확인해주세요.', data.errors ); } // 기타 에러 throw new ApiError( response.status, data.message || '서버 오류가 발생했습니다', data.errors ); }; /** * 디버그 모드 설정 * - true: 에러 코드 표시 (개발/테스트) * - false: 메시지만 표시 (프로덕션) * * TODO: 프로덕션 배포 시 false로 변경하거나 환경변수 사용 */ const SHOW_ERROR_CODE = true; /** * 에러 객체에서 사용자 친화적인 메시지 추출 * @param error - 발생한 에러 객체 (ApiError, Error, unknown) * @param includeCode - 에러 코드 포함 여부 (기본값: SHOW_ERROR_CODE 설정 따름) * @returns 사용자에게 표시할 에러 메시지 */ export const getErrorMessage = (error: unknown, includeCode?: boolean): string => { const showCode = includeCode ?? SHOW_ERROR_CODE; if (error instanceof DuplicateCodeError) { return showCode ? `[${error.status}] ${error.message} (코드: ${error.duplicateCode})` : error.message; } if (error instanceof ApiError) { return showCode ? `[${error.status}] ${error.message}` : error.message; } if (error instanceof Error) { return error.message; } return '알 수 없는 오류가 발생했습니다'; };