104 lines
3.0 KiB
TypeScript
104 lines
3.0 KiB
TypeScript
import { Request, Response, NextFunction } from "express";
|
|
import { logger } from "../utils/logger";
|
|
|
|
// 커스텀 에러 클래스
|
|
export class AppError extends Error {
|
|
public statusCode: number;
|
|
public isOperational: boolean;
|
|
|
|
constructor(message: string, statusCode: number = 500) {
|
|
super(message);
|
|
this.statusCode = statusCode;
|
|
this.isOperational = true;
|
|
|
|
Error.captureStackTrace(this, this.constructor);
|
|
}
|
|
}
|
|
|
|
// 에러 핸들러 미들웨어
|
|
export const errorHandler = (
|
|
err: Error | AppError,
|
|
req: Request,
|
|
res: Response,
|
|
next: NextFunction
|
|
): void => {
|
|
let error = { ...err };
|
|
error.message = err.message;
|
|
|
|
// PostgreSQL 에러 처리 (pg 라이브러리)
|
|
if ((err as any).code) {
|
|
const pgError = err as any;
|
|
// 원본 에러 메시지 로깅 (디버깅용)
|
|
console.error("🔴 PostgreSQL Error:", {
|
|
code: pgError.code,
|
|
message: pgError.message,
|
|
detail: pgError.detail,
|
|
hint: pgError.hint,
|
|
table: pgError.table,
|
|
column: pgError.column,
|
|
constraint: pgError.constraint,
|
|
});
|
|
// PostgreSQL 에러 코드 참조: https://www.postgresql.org/docs/current/errcodes-appendix.html
|
|
if (pgError.code === "23505") {
|
|
// unique_violation
|
|
error = new AppError("중복된 데이터가 존재합니다.", 400);
|
|
} else if (pgError.code === "23503") {
|
|
// foreign_key_violation
|
|
error = new AppError("참조 무결성 제약 조건 위반입니다.", 400);
|
|
} else if (pgError.code === "23502") {
|
|
// not_null_violation
|
|
error = new AppError("필수 입력값이 누락되었습니다.", 400);
|
|
} else if (pgError.code.startsWith("23")) {
|
|
// 기타 무결성 제약 조건 위반
|
|
error = new AppError("데이터 무결성 제약 조건 위반입니다.", 400);
|
|
} else {
|
|
error = new AppError(`데이터베이스 오류: ${pgError.message}`, 500);
|
|
}
|
|
}
|
|
|
|
// JWT 에러 처리
|
|
if (err.name === "JsonWebTokenError") {
|
|
const message = "유효하지 않은 토큰입니다.";
|
|
error = new AppError(message, 401);
|
|
}
|
|
|
|
if (err.name === "TokenExpiredError") {
|
|
const message = "토큰이 만료되었습니다.";
|
|
error = new AppError(message, 401);
|
|
}
|
|
|
|
// 기본 상태 코드 설정
|
|
const statusCode = (error as AppError).statusCode || 500;
|
|
const message = error.message || "서버 내부 오류가 발생했습니다.";
|
|
|
|
// 에러 로깅
|
|
logger.error({
|
|
message: error.message,
|
|
stack: error.stack,
|
|
url: req.url,
|
|
method: req.method,
|
|
ip: req.ip,
|
|
userAgent: req.get("User-Agent"),
|
|
});
|
|
|
|
// 응답 전송
|
|
res.status(statusCode).json({
|
|
success: false,
|
|
error: {
|
|
message: message,
|
|
...(process.env.NODE_ENV === "development" && { stack: error.stack }),
|
|
},
|
|
});
|
|
};
|
|
|
|
// 404 에러 핸들러
|
|
export const notFoundHandler = (req: Request, res: Response): void => {
|
|
res.status(404).json({
|
|
success: false,
|
|
error: {
|
|
message: "요청한 리소스를 찾을 수 없습니다.",
|
|
path: req.originalUrl,
|
|
},
|
|
});
|
|
};
|