서버 어플리케이션에서 에러처리는 굉장히 중요한 부분이다.
서버는 적게는 수십, 많게는 수천 수만명의 사람들이 동시에 이용하는 것이기 때문에 적절한 에러처리를 하지 못했을 경우에는 많은 사람들이 서버를 이용하지 못하는 심각한 상황이 발생 할 수 있다.
클라이언트가 요청한 request를 제대로 처리하지 못한 경우, 적절한 에러메세지를 작성하여 클라이언트에 보내어 에러에 대한 친절한 메세지를 보내는 것.
시스템 내부적으로 큰 문제가 발생하였어도, 서버가 죽지 않고 문제 상황에서 빠르게 복귀 될 수 있도록 예외 처리를 잘 하는 것.
동기적이란 호출된 코드가 완전히 다 처리가 되어야 다음으로 넘어 갈 수 있다.
예를 들어 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' });
});
비동기적인 코드는 완료될 때까지 기다리는 것이 아니라 호출만 해놓고 넘어가는 가는데 이러한 경우에는 내부적으로 에러가 발생한다. 즉 외부에서 에러를 감지할 수 있는 방법이 없다는 것이다.
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' });
});
promise도 대표적인 비동기이다.
promise는 콜백함수를 등록하지는 않지만 then과 catch를 통해서 처리한다.
app.get('/file2', (req, res) => {
fsAsync
.readFile('/file2.txt')
.then((data) => {})
.catch((error) => {
res.status(404).send('File not found')
});
});
app.get('/file3', async (req, res) => {
try {
const data = await fsAsync.readFile('/file2.txt'); // 이 코드는 동기적
// 파일을 다 읽을때까지 기다렸다가 파일이 읽어지면 data에 할당이 된다.
//하지만 이 코드에서 에러가 발생했을 시 안전망에 포착되지 않는다.
} catch {
res.status(404).send('File not found')
}
});
비동기일때도 에러처리 미들웨어 즉 마지막 안전망에 포착될 수 있게 할 순 없을까? 방법은 있다.
- npm i express-async-errors 를 통해 라이브러리를 설치해준다.
- 그 후에 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');
});
하지만 마지막 에러 핸들러에서 처리하도록 하는 방법 보다는, 에러가 발생했을 시 적절한 에러메세지를 사용자 전달해 주기 위한 에러처리를 하는것이 가장 좋은 방법이다.