[express] Error 처리

dk.han·2022년 10월 13일
0

동기적, 비동기적 코드의 에러처리

서버 어플리케이션에서 에러처리는 굉장히 중요한 부분이다.
서버는 적게는 수십, 많게는 수천 수만명의 사람들이 동시에 이용하는 것이기 때문에 적절한 에러처리를 하지 못했을 경우에는 많은 사람들이 서버를 이용하지 못하는 심각한 상황이 발생 할 수 있다.

에러 처리를 잘 한다는건 무슨 의미일까?

  1. 클라이언트가 요청한 request를 제대로 처리하지 못한 경우, 적절한 에러메세지를 작성하여 클라이언트에 보내어 에러에 대한 친절한 메세지를 보내는 것.

  2. 시스템 내부적으로 큰 문제가 발생하였어도, 서버가 죽지 않고 문제 상황에서 빠르게 복귀 될 수 있도록 예외 처리를 잘 하는 것.

동기적인 경우 에러 처리

동기적이란 호출된 코드가 완전히 다 처리가 되어야 다음으로 넘어 갈 수 있다.
예를 들어 const data = fs.readFileSync('/file1.txt'); 코드가 다 끝나야 다음 코드를 수행 할 수 있는 것이다. 때문에 동기적인 코드는 try - catch로 감싸서 시도하고 에러를 처리할 수 있다.
실수로 try - catch로 에러를 처리하지 못했더라도 에러 핸들러 미들웨어를 작성해 놓았다면 마지막에서 에러를 포착하여 처리할 수 있다.

app.get('/file1', (req, res) => {
   try{
     const data = fs.readFileSync('/file1.txt');
   } catch (error) {
     res.status(404).send('File not found')
   }
  )}
  .
  .
  .
  // 에러 핸들러 미들웨어.
  app.use((error, req, res, next) => {
  console.error(error);
  res.status(500).json({ message: 'Something went wrong' });
});

비동기적인 경우 에러 처리

1. 비동기적 API를 쓰는 경우

비동기적인 코드는 완료될 때까지 기다리는 것이 아니라 호출만 해놓고 넘어가는 가는데 이러한 경우에는 내부적으로 에러가 발생한다. 즉 외부에서 에러를 감지할 수 있는 방법이 없다는 것이다.

  • 때문에 try-catch로 잡아도 에러가 잡히지 않는다.
  • 또한 마지막 안전망인 에러 핸들러 미들웨어에도 포착되지 않는다.
  • 비동기적 코드에서 에러를 처리하기 가장 적합한 정소는 콜백함수 내부이다.
app.get('/file1', (req, res) => {
 fs.readFile('/file1.txt', (err, data) => {
   if(err) {
     res.status(404).send('File not found')
   }
 });
});
 .
 .
 .
 // 에러처리 미들웨어.
 app.use((error, req, res, next) => {
 console.error(error);
 res.status(500).json({ message: 'Something went wrong' });
});

2. promise를 사용하는 경우

promise도 대표적인 비동기이다.
promise는 콜백함수를 등록하지는 않지만 then과 catch를 통해서 처리한다.

app.get('/file2', (req, res) => {
 fsAsync
   .readFile('/file2.txt')
   .then((data) => {})
   .catch((error) => {
     res.status(404).send('File not found')
   });
});

3. async-await을 사용하는 경우

  • async - await 코드는 동기적으로 작동하지만 프로미스를 리턴하기 때문에 에러 핸들러 미들웨어에 포착되지 않는다.
  • 하지만 프로미스를 리턴한다고 해서 then - catch로 에러를 처리할 수는 없다.
  • 동기적으로 작동하는 코드이기 때문에 try-catch를 이용해서 에러를 처리할 수 있다.
app.get('/file3', async (req, res) => {
  try {
    const data = await fsAsync.readFile('/file2.txt'); // 이 코드는 동기적
    // 파일을 다 읽을때까지 기다렸다가 파일이 읽어지면 data에 할당이 된다.
    //하지만 이 코드에서 에러가 발생했을 시 안전망에 포착되지 않는다.
  } catch {
    res.status(404).send('File not found')
  }
});

express-async-errors

비동기일때도 에러처리 미들웨어 즉 마지막 안전망에 포착될 수 있게 할 순 없을까? 방법은 있다.

  1. npm i express-async-errors 를 통해 라이브러리를 설치해준다.
  2. 그 후에 import를 해주고 사용하면 된다.
import {} from 'express-async-errors'

미들웨어 안에서 프로미스를 사용한다면 return을 해주어야 한다!
async-await은 자동으로 리턴하기 때문에 return 안써줘도 됨!
return만 해주게 되면 마지막 에러 핸들러 미들웨어에서 에러를 포착해 낼 수 있다.

app.get('/file2', (req, res) => {
  return fsAsync.readFile('/file2.txt') // 리턴해주기
});

app.get('/file3', async (req, res) => {
    const data = await fsAsync.readFile('/file2.txt'); 
});

하지만 마지막 에러 핸들러에서 처리하도록 하는 방법 보다는, 에러가 발생했을 시 적절한 에러메세지를 사용자 전달해 주기 위한 에러처리를 하는것이 가장 좋은 방법이다.

Reference

노드로 배우는 백엔드 A-Z

0개의 댓글