[express] 에러 핸들링 로직 분리 및 Custom Error 제작

DaiVernon·2022년 1월 3일
2

Node.js / Express

목록 보기
2/2

모든 서비스 로직을 asyncWrapper가 감싸게되며 모든 에러가 asyncWrapper에서 감지되어
ErrorHandlerMiddleware 함수 하나에서 전부 핸들링을 하게 만들었습니다.
또한 기존의 express는 정상 응답과 에러 응답이 오로지 status code로만 분류가 되어 코드에서 한눈에 구분하기 어려웠지만 모든 에러는 커스텀 에러로 제작되어 message는 인자로 따로 전달하게 되며 에러에 대한 응답 전송은 ErrorHandlerMiddleware 함수 하나에서 따로 전송하게 되어 있어 에러 핸들링이 더 쉽고 용이하며 정상 응답과 에러 응답의 구분이 한눈에 들어오게 됩니다.

// src/lib/errors/custom-error.js

class CustomError extends Error {
	constructor(message) {
		super(message)
	}
}

module.exports = CustomError

// src/lib/errors/bad-request.js
const CustomError = require("./custom-error")

// 400 code
class BadRequestError extends CustomError {
	constructor(message) {
		super(message)
		// StatusCodes는 열거형(enum) 데이터, BAD_REQUEST: 400
		this.statusCode = StatusCodes.BAD_REQUEST
	}
}

module.exports = BadRequestError

// src/lib/middlewares/async.js

// async/await 구문에서 에러 핸들링을 위해 필수적으로 사용해야하는 try/catch 문 문법을
// 감싸는 함수 형태로 구현하여 무분별한 try/catch 문의 사용을 줄여 코드를 깔끔하게 만들고,
// 에러 핸들링을 한 곳에서 처리 가능하게 만듬
const asyncWrapper = (fn) => {
	return async (req, res, next) => {
		try {
			await fn(req, res, next)
		} catch (error) {
			next(error)
		}
	}
}

module.exports = { asyncWrapper }

const { StatusCodes } = require("http-status-codes")

const ErrorHandlerMiddleware = (err, req, res, next) => {
	let error = {
		//* 에러가 발생했지만, 기본적인 에러 코드나 에러 메세지가 존재하지 않을 경우를 대비한
		//* default 에러 코드 및 에러 메세지
		statusCode: err.statusCode || StatusCodes.INTERNAL_SERVER_ERROR,
		message: err.message || "Internal server error",
	}

	if (error.statusCode && (error.message === "jwt malformed" || error.message === "invalid token")) {
		error = { statusCode: StatusCodes.UNAUTHORIZED, message: "유효하지 않은 링크입니다." }
	}

	// 에러 관련된 응답
	return res.status(error.statusCode).json({ message: error.message })
}

module.exports = ErrorHandlerMiddleware

// 사용 예시
const { StatusCodes } = require("http-status-codes");

const { asyncWrapper } = require("./lib/middlewares/async");
const { BadRequestError } = require("./lib/errors");

// asyncWrapper가 try/catch 문을 대신하게 됩니다.
const sendUserId = asyncWrapper(async (req, res, next) => {
	const { userId } = req.params;

	if (!userId) {
		throw new BadRequestError("유효하지 않은 userId 입니다.");
	}

	res.status(StatusCodes.OK).json({ message: "올바른 userId 입니다." });
});

module.exports = sendUserId;
profile
클린 코드, 클린 아키텍처

0개의 댓글