Files
sam-react-prod/src/lib/api/error-handler.ts

137 lines
4.0 KiB
TypeScript
Raw Normal View History

// API 에러 핸들링 헬퍼 유틸리티
// API 요청 실패 시 에러 처리 및 사용자 친화적 메시지 생성
/**
* API
* - Error를 HTTP validation errors
*/
export class ApiError extends Error {
constructor(
public status: number,
public message: string,
public errors?: Record<string, string[]>
) {
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<never> => {
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 '알 수 없는 오류가 발생했습니다';
};