Express에서 앱의 오류를 처리하는 방법

Express에서 애플리케이션의 오류를 처리하는 방법은 중요하며, 이를 통해 사용자 경험을 개선하고 보안을 강화할 수 있습니다. Express는 기본적으로 에러 핸들링을 지원하지만, 개발자가 커스터마이징하여 적절히 처리해야 합니다. 아래에 단계별로 오류 처리 방법을 자세히 설명하겠습니다.


1. 기본 에러 핸들링

Express에서는 미들웨어로 에러 핸들링을 구현할 수 있습니다. 에러 핸들링 미들웨어는 일반 미들웨어와 비슷하지만, err 인수를 추가로 받습니다.

app.use((err, req, res, next) => {
  console.error(err.stack); // 에러 로그 출력
  res.status(500).send('Something broke!');
});

2. 전역 에러 핸들링

애플리케이션 전체에서 발생할 수 있는 에러를 중앙 집중적으로 처리하려면, 다음과 같이 전역 에러 핸들러를 설정합니다:

기본 사용

app.use((err, req, res, next) => {
  res.status(err.status || 500);
  res.json({
    error: {
      message: err.message,
    },
  });
});

사용자 정의 에러 객체 활용

class AppError extends Error {
  constructor(message, status) {
    super(message);
    this.status = status;
  }
}

// 예외 발생 시
app.get('/', (req, res, next) => {
  next(new AppError('Route not found', 404));
});

3. 404 에러 처리

존재하지 않는 경로로 요청이 들어왔을 때를 처리하기 위해, 별도의 404 핸들러를 설정할 수 있습니다.

app.use((req, res, next) => {
  res.status(404).send('Sorry, we cannot find that!');
});

4. 비동기 에러 처리

Express 5 이전 버전에서는 비동기 코드에서 발생한 에러를 next()로 전달해야 합니다.

예제:

app.get('/async-route', async (req, res, next) => {
  try {
    const data = await someAsyncFunction();
    res.json(data);
  } catch (err) {
    next(err);
  }
});

Express 5부터는 비동기 함수에서 던진 에러를 자동으로 처리합니다.

app.get('/async-route', async (req, res) => {
  const data = await someAsyncFunction(); // 에러 발생 시 자동 전달
  res.json(data);
});

5. 환경별 에러 메시지

개발 환경과 프로덕션 환경에서 다른 에러 메시지를 보여주는 것이 중요합니다.

app.use((err, req, res, next) => {
  const isDevelopment = process.env.NODE_ENV === 'development';

  res.status(err.status || 500);
  res.json({
    message: isDevelopment ? err.message : 'Internal Server Error',
    ...(isDevelopment && { stack: err.stack }),
  });
});

6. 로그 관리

에러 로그를 콘솔에 출력하는 대신 파일, 데이터베이스, 혹은 클라우드 서비스(Sentry 등)에 저장할 수 있습니다.

예제: morgan을 사용한 로그 기록

const morgan = require('morgan');
app.use(morgan('combined'));

예제: Sentry 통합

const Sentry = require('@sentry/node');
Sentry.init({ dsn: 'https://example.sentry.io/your-dsn' });

app.use(Sentry.Handlers.requestHandler());

// 에러 발생 시 Sentry에 보고
app.use(Sentry.Handlers.errorHandler());

7. 모든 에러 잡기

Express 외부에서 발생한 에러를 처리하기 위해 process 이벤트를 사용할 수 있습니다.

process.on('uncaughtException', (err) => {
  console.error('Uncaught Exception:', err);
  process.exit(1); // 필요 시 프로세스 종료
});

process.on('unhandledRejection', (reason, promise) => {
  console.error('Unhandled Rejection at:', promise, 'reason:', reason);
});

8. 최종 예제

아래는 다양한 에러 처리 방식을 종합한 Express 애플리케이션의 예제입니다:

const express = require('express');
const app = express();

// 미들웨어
app.use(express.json());

// 정상 라우트
app.get('/', (req, res) => {
  res.send('Hello World!');
});

// 비정상 라우트
app.get('/error', (req, res, next) => {
  next(new Error('Something went wrong!'));
});

// 404 핸들러
app.use((req, res, next) => {
  res.status(404).send('Route not found');
});

// 에러 핸들러
app.use((err, req, res, next) => {
  console.error(err.stack);
  res.status(err.status || 500).json({
    message: err.message,
  });
});

// 서버 시작
app.listen(3000, () => {
  console.log('Server running on port 3000');
});

위와 같이 설정하면 다양한 유형의 오류를 효과적으로 처리하고, 사용자와 개발자 모두에게 유용한 환경을 제공합니다. 추가적인 질문이 있다면 댓글로 알려주세요! 😊

Categories:

Updated:

Leave a comment