에러
@asapjs/error는 타입 안전한 HTTP 에러 생성과 Express 에러 처리를 위한 패키지입니다. error() 팩토리로 에러 생성자를 정의하면 구조화된 에러 응답과 Swagger 스키마 자동 등록을 함께 사용할 수 있습니다.
임포트
import {
HttpError,
HttpErrorBody,
error,
ErrorCreator,
makeHttpError,
errorToResponse,
} from '@asapjs/error';이 페이지에서 찾을 수 있는 것
| 심볼 | 타입 | 설명 |
|---|---|---|
HttpErrorBody | Interface | HTTP 에러 응답 본문 형식 |
HttpError | Class | 구조화된 HTTP 에러 클래스 |
ErrorCreator<T> | Interface | error() 가 반환하는 호출 가능한 에러 생성자 |
error() | Factory Function | 재사용 가능한 에러 생성자를 반환하는 팩토리 |
makeHttpError() | Function | HttpErrorBody 객체를 직접 생성 |
errorToResponse() | Function | 에러 객체를 Express 응답으로 변환 |
Effect 라이브러리와의 통합이 필요하면 Effect Integration을 참고하세요.
HttpErrorBody
HTTP 에러 응답 본문의 형식을 정의하는 인터페이스입니다.
interface HttpErrorBody {
status: number;
errorCode: string;
message: string;
data?: Record<string, any>;
}| 필드 | 타입 | 필수 | 설명 |
|---|---|---|---|
status | number | 필수 | HTTP 상태 코드 |
errorCode | string | 필수 | 에러 식별 코드 (예: 'USER_NOT_FOUND') |
message | string | 필수 | 사람이 읽을 수 있는 에러 메시지 |
data | Record<string, any> | 선택 | 에러에 첨부할 추가 데이터 |
HttpError
구조화된 HTTP 에러를 나타내는 클래스입니다. Error를 확장하고 HttpErrorBody를 구현합니다.
class HttpError extends Error implements HttpErrorBody {
readonly status: number;
readonly errorCode: string;
readonly message: string;
readonly data?: Record<string, any>;
constructor(
status: number,
errorCode: string,
message: string,
data?: Record<string, any>
);
toJSON(): HttpErrorBody;
}생성자
new HttpError(status: number, errorCode: string, message: string, data?: Record<string, any>)| 파라미터 | 타입 | 설명 |
|---|---|---|
status | number | HTTP 상태 코드 |
errorCode | string | 에러 식별 코드 |
message | string | 에러 메시지 |
data | Record<string, any> | 추가 데이터 (선택) |
const err = new HttpError(404, 'NOT_FOUND', '리소스를 찾을 수 없습니다');
throw err;toJSON()
error.toJSON(): HttpErrorBody에러를 직렬화 가능한 HttpErrorBody 객체로 변환합니다.
const err = new HttpError(404, 'NOT_FOUND', '리소스를 찾을 수 없습니다');
console.log(err.toJSON());
// { status: 404, errorCode: 'NOT_FOUND', message: '리소스를 찾을 수 없습니다' }HttpException과의 관계
@asapjs/router에는 레거시 에러 클래스인 HttpException이 있습니다. HttpException은 status와 message만 가지며 errorCode가 없습니다. errorToResponse()는 HttpException을 errorCode: 'LEGACY_HTTP_EXCEPTION'으로 매핑합니다. 새 코드에서는 HttpError와 error() 팩토리 사용을 권장합니다.
ErrorCreator
error() 팩토리가 반환하는 호출 가능한 에러 생성자 인터페이스입니다. 호출하면 HttpError 인스턴스를 생성하며, Swagger 문서화를 위한 메타데이터를 함께 보관합니다.
interface ErrorCreator<T = any> {
(data: T): HttpError;
_status: number;
_code: string;
_message: string;
_schema: Record<string, any>;
}| 필드 | 타입 | 설명 |
|---|---|---|
(data) | (data: T) => HttpError | 호출 시 HttpError 인스턴스 생성 |
_status | number | HTTP 상태 코드 |
_code | string | 에러 식별 코드 |
_message | string | 메시지 템플릿 |
_schema | Record<string, any> | data 필드의 타입 스키마 |
라우트 데코레이터의 errors 옵션에 ErrorCreator를 전달하면 Swagger 에러 응답이 자동 생성됩니다.
error()
import { error } from '@asapjs/error';
function error<T extends Record<string, any>>(
status: number,
code: string,
message: string,
schema: T
): ErrorCreator<InferTypeFromSchema<T>>재사용 가능한 에러 생성자(ErrorCreator)를 반환하는 팩토리 함수입니다.
| 파라미터 | 타입 | 설명 |
|---|---|---|
status | number | HTTP 상태 코드 |
code | string | 에러 식별 코드. Swagger 스키마 이름으로도 사용 |
message | string | 메시지 템플릿. {key} 형식으로 data 값을 보간 |
schema | Record<string, SchemaType> | data 필드의 타입 스키마. TypeIs 타입 사용 |
메시지 보간
message 파라미터에 {key} 플레이스홀더를 넣으면 data의 값으로 자동 치환됩니다:
const UserNotFound = error(
404,
'USER_NOT_FOUND',
'사용자 {userId}를 찾을 수 없습니다',
{ userId: TypeIs.INT() }
);
throw UserNotFound({ userId: 42 });
// → message: '사용자 42를 찾을 수 없습니다'데이터 매핑
schema에 정의된 TypeIs 타입에 따라 data 값이 자동 매핑됩니다. 예를 들어 TypeIs.INT()는 문자열 "1"을 숫자 1로 변환합니다. 이 매핑은 @asapjs/schema의 mapDataWithSchema()에 의해 수행됩니다.
Swagger 지연 등록
error()로 정의한 에러의 Swagger 스키마는 지연(deferred) 방식으로 등록됩니다:
- Swagger가 아직 준비되지 않은 경우 → 스키마를 내부 큐(
pendingSchemas)에 보관 markSwaggerAsReady()호출 시 → 대기 중인 스키마를 일괄 등록- Swagger 준비 후 정의된 에러 → 즉시 등록
이 메커니즘 덕분에 에러를 모듈 최상위 레벨에서 정의해도 Swagger 초기화 순서에 관계없이 스키마가 올바르게 등록됩니다.
Swagger 에러 응답 등록
라우트 데코레이터의 errors 옵션에 ErrorCreator를 전달하면 해당 엔드포인트의 Swagger 에러 응답이 자동 생성됩니다:
import { Get, ExecuteArgs, RouterController } from '@asapjs/router';
import { error } from '@asapjs/error';
import { TypeIs } from '@asapjs/schema';
const PostNotFound = error(404, 'POST_NOT_FOUND', '게시글 {postId}을 찾을 수 없습니다', {
postId: TypeIs.INT(),
});
export default class PostController extends RouterController {
@Get('/:postId', {
title: '게시글 상세 조회',
response: PostInfoDto,
errors: [PostNotFound], // Swagger에 에러 응답 자동 등록
})
async getPost({ path }: ExecuteArgs) {
const postId = parseInt((path as any)?.postId as string, 10);
const post = await this.postService.getPost(postId);
if (!post) {
throw PostNotFound({ postId });
}
return post;
}
}makeHttpError()
import { makeHttpError } from '@asapjs/error';
function makeHttpError(
status: number,
errorCode: string,
message: string,
data?: Record<string, any>
): HttpErrorBodyHttpErrorBody 형태의 순수 객체를 생성합니다. HttpError 클래스 인스턴스가 아닌 일반 객체가 필요한 경우에 사용합니다.
| 파라미터 | 타입 | 설명 |
|---|---|---|
status | number | HTTP 상태 코드 |
errorCode | string | 에러 식별 코드 |
message | string | 에러 메시지 |
data | Record<string, any> | 추가 데이터 (선택) |
const body = makeHttpError(400, 'INVALID_INPUT', '잘못된 입력입니다');
// { status: 400, errorCode: 'INVALID_INPUT', message: '잘못된 입력입니다' }errorToResponse()
import { errorToResponse } from '@asapjs/error';
function errorToResponse(error: unknown, res: Response): void에러 객체의 종류를 판별하여 적절한 HTTP 응답을 생성합니다. @asapjs/router의 Wrapper 함수가 catch 블록에서 이 함수를 호출합니다.
| 파라미터 | 타입 | 설명 |
|---|---|---|
error | unknown | 처리할 에러 객체 |
res | Response | Express Response 객체 |
에러 분류 규칙
errorToResponse()는 에러 객체의 종류에 따라 다른 응답을 생성합니다:
| 에러 유형 | 조건 | 응답 형식 |
|---|---|---|
HttpError | error instanceof HttpError | { status, errorCode, message, data? } |
HttpException (레거시) | status와 message는 있지만 errorCode가 없음 | { status, errorCode: 'LEGACY_HTTP_EXCEPTION', message } |
HttpErrorBody 형태 객체 | status, errorCode, message 프로퍼티가 모두 있음 | 객체를 그대로 전달 |
| 일반 에러 / 알 수 없는 에러 | 위 조건에 해당하지 않음 | { status: 500, errorCode: 'INTERNAL_SERVER_ERROR', message: '알 수 없는 서버 오류가 발생했습니다.' } |
// Wrapper 내부에서의 호출 흐름 (packages/router/src/utils/wrapper.ts)
try {
const output = await cb(args);
// ...
} catch (err) {
errorToResponse(err, res);
}전체 예제
// src/errors/user.errors.ts
import { error } from '@asapjs/error';
import { TypeIs } from '@asapjs/schema';
export const UserNotFound = error(
404,
'USER_NOT_FOUND',
'사용자 {userId}를 찾을 수 없습니다',
{ userId: TypeIs.INT() }
);
export const Unauthorized = error(
401,
'UNAUTHORIZED',
'인증이 필요합니다',
{}
);
export const InvalidInput = error(
400,
'INVALID_INPUT',
'필드 {field}이(가) 유효하지 않습니다',
{ field: TypeIs.STRING() }
);// 사용
throw UserNotFound({ userId: 42 });
// → { status: 404, errorCode: 'USER_NOT_FOUND',
// message: '사용자 42를 찾을 수 없습니다', data: { userId: 42 } }
throw Unauthorized({});
// → { status: 401, errorCode: 'UNAUTHORIZED',
// message: '인증이 필요합니다' }