개요
API 에러 규격의 핵심은 에러를 안정적으로 식별할 수 있는 Code 체계 확보임 HTTP Status만으로는 부족하고 메시지는 언어·맥락에 따라 바뀔 수 있음 실제로 계약으로서 신뢰할 수 있는 값은 error.code 임 아래는 다수의 글로벌 서비스에서 공통적으로 쓰는 Error Code 설계 원칙 정리
설계 원칙
- error.code 는 서비스 전반의 안정적인 식별자여야 함
- 메시지나 HTTP Status는 변경 가능하지만 error.code 는 변경 불가
- 구버전 클라이언트도 동일 코드를 신뢰해야 하므로 호환성 보장 필수
- 숫자형 대신 의미가 드러나는 문자열 기반 Enum 권장
- 숫자형은 의미 파악 어려움, 매뉴얼 의존, 협업 비용 증가
- 문자열 Enum은 가독성, 검색성, 커뮤니케이션 효율 우수
예시
"USER_ALREADY_EXISTS"
"INVALID_COUPON"
"PAYMENT_INSUFFICIENT_BALANCE"네이밍 규칙
- 대문자 + 언더스코어 형태 CONSTANT_CASE 권장
- 언어/플랫폼 비종속, 문서화 시 표현 깔끔함
- 도메인 + 문제 유형 조합 권장
- 패턴: {DOMAIN}_{CONDITION}
예시
- USER_NOT_FOUND 해당 유저를 찾을 수 없음
- USER_ALREADY_EXISTS 이미 가입된 유저
- AUTH_TOKEN_EXPIRED 인증 토큰 만료
- ORDER_NOT_CANCELABLE 현재 상태에서는 주문 취소 불가
- PAYMENT_INSUFFICIENT_BALANCE 잔액 부족
- FILE_TOO_LARGE 허용 파일 크기 초과
카테고리 분리 전략
A. Prefix로 도메인 구분 권장
- AUTH_, USER_, ORDER_, PAYMENT_, COMMON_, FILE_ 등으로 그룹화
- 코드만 보고도 영역 식별 가능, 문서 구조화 용이
B. HTTP Status와 비즈니스 코드는 분리
- 400_USER_INVALID_EMAIL 같은 결합은 지양
- HTTP Status는 정책·맥락에 따라 변할 수 있으나 code는 절대 불변이어야 함
- 원칙 HTTP Status와 Error Code는 결합하지 않음
ErrorCode Enum 예시
NestJS/TypeScript 기준 예시
export enum ErrorCode {
// ===== 공통 =====
INTERNAL_SERVER_ERROR = 'INTERNAL_SERVER_ERROR',
INVALID_INPUT = 'INVALID_INPUT',
VALIDATION_FAILED = 'VALIDATION_FAILED',
RESOURCE_NOT_FOUND = 'RESOURCE_NOT_FOUND',
// ===== Auth =====
AUTH_REQUIRED = 'AUTH_REQUIRED',
AUTH_TOKEN_EXPIRED = 'AUTH_TOKEN_EXPIRED',
AUTH_INVALID_TOKEN = 'AUTH_INVALID_TOKEN',
// ===== User =====
USER_NOT_FOUND = 'USER_NOT_FOUND',
USER_ALREADY_EXISTS = 'USER_ALREADY_EXISTS',
USER_EMAIL_NOT_VERIFIED = 'USER_EMAIL_NOT_VERIFIED',
// ===== Order =====
ORDER_NOT_FOUND = 'ORDER_NOT_FOUND',
ORDER_NOT_CANCELABLE = 'ORDER_NOT_CANCELABLE',
// ===== Payment =====
PAYMENT_INSUFFICIENT_BALANCE = 'PAYMENT_INSUFFICIENT_BALANCE',
PAYMENT_METHOD_INVALID = 'PAYMENT_METHOD_INVALID',
// ===== File Upload =====
FILE_TOO_LARGE = 'FILE_TOO_LARGE',
FILE_UNSUPPORTED_TYPE = 'FILE_UNSUPPORTED_TYPE',
}이 구성의 효과
- 프론트·앱에서 조건 분기 용이
- 의미 중심 네이밍으로 직관성 확보
- 도메인 확장에 따른 추가가 간단
- 유지보수 비용 낮음
안티 패턴
- 숫자 코드만 사용
- 예시 “E4302” 의미 불명확, 협업 비용 증가
- HTTP Status를 code에 포함
- 예시 “400_INVALID_EMAIL” Status 변경 시 code 불변 원칙과 충돌
- 지나치게 포괄적 네이밍
- BAD_REQUEST, CONFLICT 등은 비즈니스 의미 전달 부족
- 전역 중복 이름
- NOT_FOUND 단일 정의는 도메인 식별 불가
- 메시지 문구를 code에 포함
- 예시 “EMAIL_HAS_INVALID_FORMAT” 미묘한 정책 변경 시 관리 비용 증가, 번역·UX 변경 어려움
코드·메시지·HTTP Status 매핑
명확한 맵핑 구조를 두면 운영과 문서화가 단순해짐
export const ErrorMeta: Record<ErrorCode, { httpStatus: number; message: string }> = {
INTERNAL_SERVER_ERROR: { httpStatus: 500, message: '서버 내부 오류가 발생했습니다.' },
INVALID_INPUT: { httpStatus: 400, message: '입력값이 잘못되었습니다.' },
VALIDATION_FAILED: { httpStatus: 400, message: '입력값 검증에 실패했습니다.' },
RESOURCE_NOT_FOUND: { httpStatus: 404, message: '리소스를 찾을 수 없습니다.' },
AUTH_REQUIRED: { httpStatus: 401, message: '인증이 필요합니다.' },
AUTH_TOKEN_EXPIRED: { httpStatus: 401, message: '인증 토큰이 만료되었습니다.' },
AUTH_INVALID_TOKEN: { httpStatus: 401, message: '잘못된 인증 토큰입니다.' },
USER_NOT_FOUND: { httpStatus: 404, message: '사용자를 찾을 수 없습니다.' },
USER_ALREADY_EXISTS: { httpStatus: 409, message: '이미 가입된 유저입니다.' },
USER_EMAIL_NOT_VERIFIED: { httpStatus: 403, message: '이메일 인증이 필요합니다.' },
ORDER_NOT_FOUND: { httpStatus: 404, message: '주문을 찾을 수 없습니다.' },
ORDER_NOT_CANCELABLE: { httpStatus: 409, message: '현재 주문은 취소할 수 없습니다.' },
PAYMENT_INSUFFICIENT_BALANCE: {
httpStatus: 402,
message: '잔액이 부족합니다.',
},
PAYMENT_METHOD_INVALID: {
httpStatus: 400,
message: '결제 수단이 유효하지 않습니다.',
},
FILE_TOO_LARGE: { httpStatus: 400, message: '파일이 너무 큽니다.' },
FILE_UNSUPPORTED_TYPE: { httpStatus: 400, message: '지원하지 않는 파일 형식입니다.' },
};전역 예외 필터 예시 사용법
const meta = ErrorMeta[exception.code];
return res.status(meta.httpStatus).json({
error: {
code: exception.code,
httpStatus: meta.httpStatus,
message: meta.message,
details: exception.details ?? null,
traceId,
timestamp,
}
});요점
- ErrorCode는 비즈니스 의미 유지
- HTTP Status, 메시지는 환경·정책에 맞게 별도 관리
- 관측성 지표로 traceId 등 메타데이터 포함 권장
도입 체크리스트
- Code가 불변 계약인지 확인 API 계약 준수
- Code만 보고 의미 파악 가능한지 확인 YES 기준
- 모든 코드에 고유 도메인 접두가 있는지 확인 USER, AUTH 등
- HTTP Status와 code 결합 금지 여부 확인 결합 금지
- 메시지와 code의 독립성 확인 번역·UX 변경 용이성
- Validation 오류 코드 일관성 확인 VALIDATION_FAILED 등 공용 코드 사용
- 로그에서 traceId 기반 추적 가능 여부 확인 운영 디버깅 용이성
마무리
좋은 Error Code 체계는 프론트·앱·백엔드 간 계약이자 디버깅·모니터링·UX 일관성을 좌우하는 핵심 설계 요소
- code의 안정성 확보
- 비즈니스 의미 중심의 도메인 기반 네이밍
- Status와 메시지의 분리된 관리
- 팀 합의 컨벤션으로 문서화와 운영 일관성 유지 이 네 가지를 충족하면 확장성과 유지보수성이 뛰어난 API 에러 설계로 평가 가능