오늘의 명언
“ 당신은 소프트웨어 품질을 추구할 수도 있고, 포인터 연산을 할 수도 있다. 그러나 두 개를 동시에 할 수는 없다. ”
-
베르트랑 마이어 (Bertrand Meyer)
300x250
이전 포스팅(링크) 에서 설정한 내용을 토대로 진행되니 한번 참고하시면 됩니다.
오류 처리 미들웨어
오류 처리 미들웨어에는 항상 4개의 인수가 필요합니다. 어떠한 함수를 오류 처리 미들웨어 함수로 식별하려면 4개의 인수를 제공해야 합니다. (err, req, res, next) 4개의 매개변수로 사용됩니다.
app.use(function(err, req, res, next) {
console.error(err.stack);
res.status(500).send('500 에러');
});
에러 핸들러
에러 핸들러는 일종의 미들웨어라고 볼 수 있는데요, app.use() 함수를 사용하여 인스턴스를 등록해 두면 모든 에러가 한 곳을 통해서 에러를 관리할 수 있습니다. 그래서 저는 Express로 프로젝트를 진행하면서 자주 사용하는 모듈에 대한 에러를 미들웨어로 등록하여 한 곳에서 관리하게 구현해 보겠습니다.
먼저 에러 히스토리와 에러 관련 코드 및 메시지를 생성할 수 있는 클래스 파일을 작성해 봅니다.
// ErrorThrow.ts
class ErrorThrow extends Error {
constructor(public type: string = 'Default', public status: number = 400, ...params: any[]) {
super(...params);
if (Error.captureStackTrace) {
Error.captureStackTrace(this, ErrorThrow);
}
this.type = type;
this.status = status;
}
}
export { ErrorThrow }
ErrorHandler.ts 파일을 생성하여 사용하는 모듈에 관해서 에러를 관리해 봅니다.
- 404 에러 핸들러
- mongo DB 에러 핸들러
- mysql sequelize 에러 핸들러
- jwt 에러 핸들러
- ErrorThrow 커스텀 에러 핸들러
// ErrorHandler.ts
import express from 'express'
import createError from 'http-errors'
import mongoose from 'mongoose';
import sequelize from 'sequelize'
import jsonwebtoken from 'jsonwebtoken'
import {ErrorThrow} from "../ErrorThrow";
export default (app: express.Application) =>{
//TODO: 404에러 핸들러
app.use((req: express.Request, res: express.Response, next: express.NextFunction) => {
next(createError(404));
});
//TODO: mysql sequelize 에러
app.use(function handleSequelizeError(
error: Error,
req: express.Request,
res: express.Response,
next: express.NextFunction,
) {
if (error instanceof sequelize.Error) return res.status(400).json({ type: 'MysqlError', message: error.message });
next(error);
});
//TODO: mongo 에러
app.use(function handleMongoError(
error: Error,
req: express.Request,
res: express.Response,
next: express.NextFunction,
) {
if (error instanceof mongoose.Error) return res.status(400).json({ type: 'MongoError', message: error.message });
next(error);
});
//TODO: jwt 에러
app.use(function handleJwtError(
error: Error,
req: express.Request,
res: express.Response,
next: express.NextFunction,
) {
if (error instanceof jsonwebtoken.JsonWebTokenError)
return res.status(401).json({ type: 'JsonWebTokenError', message: error.message });
if (error instanceof jsonwebtoken.TokenExpiredError)
return res.status(401).json({ type: 'TokenExpiredError', message: error.message });
next(error);
});
// ErrorThrow 핸들러
app.use(function handleCustomError(
error: Error,
req: express.Request,
res: express.Response,
next: express.NextFunction,
) {
if (error instanceof ErrorThrow) {
const { status, type, message } = error;
return res.status(status).send({ type, message });
}
next(error);
});
app.use(function (error: Error, req: express.Request, res: express.Response, next: express.NextFunction) {
return res.status(400).json({ message: error.message });
});
}
비동기 에러 핸들러
비동기 에러에 대해서 에러를 잡으려면 try, catch를 사용했는데요, 저희는 에러 핸들러를 만들어서 간단하게 처리하는 작업을 하고 있기 때문에 이 부분 또한 Wrapper 함수로 만들어서 사용하는 것을 작성해 봅니다.
ErrorAsyncWrapper.ts를 생성하여 작성합니다.
// ErrorAsyncWrapper.ts
import { Request, Response, NextFunction } from 'express';
function ErrorAsyncWrapper(fn: any) {
return function (req: Request, res: Response, next: NextFunction) {
fn(req, res, next).catch(next);
};
}
export { ErrorAsyncWrapper };
이제 재료가 다 모였으니 함수들을 등록하여 어떻게 사용하는지 알아보겠습니다.
// loaders 폴더 -> index.ts
import express from "express";
import expressLoader from './express'
import ErrorHandler from './ErrorHandler'
export default (app:express.Application)=>{
expressLoader(app) // express 관련 설정
ErrorHandler(app) // 에러 핸들러
}
// src 폴더 server.ts
import express from 'express'
import config from './config/index'
import loaders from "./loaders/index";
const app:express.Application = express()
loaders(app) //등록
const server = app.listen(config.server_port,()=>{
console.log(`Server listening on port: ${config.server_port}`)
}) .on('error', (err) => {
console.log(`${config.server_port} server error: ${err}`)
})
export default { server }
이제 이전에 만들어둔 auth.route.ts 파일에서 작성해 봅니다.
import {NextFunction, Request, Response, Router} from "express";
import {ErrorThrow} from "@/ErrorThrow";
import {ErrorAsyncWrapper} from "@/ErrorAsyncWrapper";
export default (router:Router)=>{
router.use('/auth',router)
// 비동기 에러 wrapper를 사용하여 try catch문 사용 안해도됨
router.get('/',ErrorAsyncWrapper(async (req:Request,res:Response,next:NextFunction)=>{
const {userName} = req.query
// ErrorThrow 클래스를 사용하여 type과 StatusCode, Message를 입력하여 에러 발생
if(!userName) throw new ErrorThrow('ParameterError',401,'userName Not Found')
res.status(200).json({
message: useName
})
}))
}
이제 정리하면 실제로 프로젝트에서 진행할 때 ErrorAsyncWrapper로 라우터를 감싸서 try catch 문 없이 비동기 에러를 처리하면 됩니다. 그리고 ErrorThrow 클래스로 에러를 커스텀하여 관리할 수 있습니다. 물론 ErrorHandler에서 저희가 모듈에 관한 에러들을 미들웨어로 처리했기 때문에관련 모듈을 사용하다가 설정해 둔 에러가 발생 시 그 미들웨어에서 설정한 값으로 리턴되니 한결 깔끔하게 에러 관련 코드를 관리할 수 있게 되었습니다.
반응형
잘못된 내용이 있으면 댓글 부탁드립니다. 감사합니다.