프론트엔드 개발자가 소소한 취미로 백앤드를 개발해 본 경험 글로, 틀린 점이 있다면 댓글로 부탁드립니다 🙏
에러 처리를 어떻게 하면 좋을까 찾아보다가 에러 핸들링 미들웨어를 만들어 사용하는 예시들을 발견했다!
이를 활용해 에러 처리를 한 곳에서 관리하고 응답의 일관성을 유지할 수 있다는 점이 깔끔해 보여 적용해 봤다.
에러 처리 미들웨어
기본적으로 에러 처리 미들웨어는 반드시 4개의 인수(err, req, res, next)를 가져야 한다!
app.use(function(err, req, res, next) {
console.error(err.stack);
res.status(500).send('Something broke!');
});
커스텀 에러 처리 핸들링
기존 Error 클래스를 상속받아 커스텀 에러 클래스를 만들어준다.
import { ErrorResponse, ErrorType, ErrorValidation } from '../types/error';
export class CustomError extends Error {
private httpStatusCode: number; // HTTP 상태 코드
private errorType: ErrorType; // 에러의 종류
private errors: string[] | null; // 에러 메시지를 저장하는 배열
private errorRaw: any; // 에러의 원시 정보
private errorsValidation: ErrorValidation[] | null; // 유효성 검사 에러를 저장하는 배열
constructor(
httpStatusCode: number,
errorType: ErrorType,
message: string,
errors: string[] | null = null,
errorRaw: any = null,
errorsValidation: ErrorValidation[] | null = null
) {
super(message);
this.name = this.constructor.name;
this.httpStatusCode = httpStatusCode;
this.errorType = errorType;
this.errors = errors;
this.errorRaw = errorRaw;
this.errorsValidation = errorsValidation;
}
get HttpStatusCode() {
return this.httpStatusCode;
}
get JSON(): ErrorResponse { // JSON 형식의 응답을 정의
return {
errorType: this.errorType,
errorMessage: this.message,
errors: this.errors,
errorRaw: this.errorRaw,
errorsValidation: this.errorsValidation,
stack: this.stack,
};
}
}
관련 타입 정의는 아래와 같다.
export type ErrorResponse = {
errorType: ErrorType;
errorMessage: string;
errors: string[] | null;
errorRaw: any;
errorsValidation: ErrorValidation[] | null;
stack?: string;
};
export type ErrorType = 'General' | 'Raw' | 'Validation' | 'Unauthorized' | 'Forbidden';
export type ErrorValidation = { [key: string]: string };
에러를 처리하는 미들웨어를 만들어 보겠다.
middlewares 폴더 아래에 파일을 생성해 준다.
error로는 위에서 만들어 준 CustomError를 첫 번째 매개변수로 받는다.
여기서는 error, req, res, next가 모두 있어야 한다!!
응답값으로는 http 상태코드와 JSON 형태의 에러를 리턴해준다.
middlewares/errorHandler.ts
import { Request, Response, NextFunction } from 'express';
import { CustomError } from '../libs/customError';
const errorHandler = (error: CustomError, req: Request, res: Response, next: NextFunction) => {
return res.status(error.HttpStatusCode).json(error.JSON);
};
export default errorHandler;
이렇게 만들어 준 error 미들웨어를 적용해 주면! 끝이다!
src/index.ts
app.use(errorHandler);
사용 시에는 아래와 같이 사용해 주면 간단하고 깔끔하다!
import { Request, Response, NextFunction } from 'express';
import { CustomError } from '../../libs/customError';
import { AppDataSource } from '../../data-source';
import { User } from '../../entities/User';
import { Portfolio } from '../../entities/Portfolio';
import { prependCloudinaryBaseUrl } from '../../libs/utils';
/**
* 포트폴리오 metadata
* GET /v1/portfolio/metadata
*/
export const metadataPortfolio = async (req: Request, res: Response, next: NextFunction) => {
const { username } = req.query;
if (typeof username !== 'string')
return next(new CustomError(400, 'Validation', 'username이 string 타입이 아닙니다.'));
try {
const UserRepository = AppDataSource.getRepository(User);
const user = await UserRepository.findOne({
where: {
username: username,
},
select: ['id'],
});
if (!user) {
return next(new CustomError(404, 'General', '해당 user가 존재하지 않습니다.'));
}
...
} catch (error) {
return next(new CustomError(400, 'Raw', 'Error', null, error));
}
};
참고자료
'Project > foliohub' 카테고리의 다른 글
[Next.js] fill을 사용한 이미지에 sizes 추가 (Image with src "~~" has "fill" but is missing "sizes" prop. Please add it to improve page performance.) (0) | 2024.04.14 |
---|---|
[Next.js] 이미지 next/image 적용해보기 (0) | 2024.04.12 |
[Next.js] NotFound / Error 페이지 Custom하기 (0) | 2024.04.06 |
프로젝트 1차 완성 🎉(+검색엔진에 내 사이트가 뜬다!) (0) | 2024.04.04 |
axios의 onUploadProgress를 활용한 progressBar 로딩 처리 (0) | 2024.04.03 |