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, }, }); };