오늘도 개발

에러/오류 핸들링 본문

웹 프로그래밍/Javascript

에러/오류 핸들링

Sueeeeeee 2022. 9. 28. 12:35

1. 에러(Error) VS 예외(Exception)

에러

컴퓨터가 내는 것.

ex) 문법 에러, 통신 장애 등.

consloe.log(1)
// Uncaught ReferenceError: consloe is not defined

 

예외

개발자가 의도적으로 내는 것.

코드 실행은 되지만 개발자가 봤을 때 정상이 아닌 상황.

ex) 비밀번호 정책이 10자인데 5자만 들어온 경우

 

2. 예외 핸들링

new 키워드와 Error() 생성자 사용.

throw 예외 오브젝트는 try-catch로 핸들링하지 않으면 전역 에러가 됨 => 프로그램 실행을 멈춤

const username = 'sue'

if (username.length < 5){
  throw new Error('USERNAME_INVALID')
 }
 
console.log(1)

// Error: USERNAME_INVALID at Object.<anonymous>

return 예외 오브젝트는 함수 실행만 중단하고 프로그램 실행은 멈추지 않음

const username = 'sue'

function validateUser(user){
  if (username.length < 5){
    throw new Error('USERNAME_INVALID')
   }
}

validateUser(username)
console.log(1)

// 1

 

3. try-catch

예외도 에러도 try-catch문으로 처리할 수 있다.

try-catch를 사용하면 예외나 에러가 발생해도 프로그램을 중단하지 않는다. 

try에는 에러가 발생할 수도 있는 코드를 작성하고, catch에는 에러가 발생했을 때 동작을 작성한다.

try {
  const username = 'sue'

  if (username.length < 5){
    const error = new Error('USERNAME_INVALID')
    error.statusCode = 400
    throw error
  }
} catch(error) {
  console.log(error)
}

async 함수에서도 try-catch를 사용할 수 있는데, 이 때는 await을 같이 사용해야 한다.

또는 try-catch나 await 없이 promise-catch를 사용할 수도 있다.  

async 함수의 반환값을 제대로 얻으려면 await이나 promise-then을 사용해야 하는 것과 같은 이유 때문이다.

 

우선 async 함수에 await, promise-catch를 사용하지 않은 경우를 살펴보자.

async function example(param){
  if (!param){
    throw new Error('NoParameterError');
  }
  return param
}

try {
    const someValue = example(); 
    // someValue에는 rejected된 Promise 객체가 할당됨
    console.log(someValue);
  }
catch(error){
  // example의 NoParmeterError가 안 잡힘
  console.log('--Handling error--') // 실행 안 됨
  console.log(error); // 실행 안 됨
}

console.log('프로그램 실행 끝')

// 1. Promise 객체 반환
// Promise { <rejected> Error: NoParameterError...
// 2. 프로그램을 끝까지 실행
// 프로그램 실행 끝
// 3. Promise 실행 중 에러 발생, 핸들링할 catch 구문이 없음 => 전역 에러가 됨 => 프로그램 실행 중단 
// Error: NoParameterError

await을 사용하면 catch가 제대로 async 함수의 에러/오류를 잡아서 핸들링할 수 있다.

async function example(param){
  if (!param){
    throw new Error('NoParameterError');
  }
  return param
}

// await은 async 함수 내에서만 쓸 수 있으므로 함수로 감싸줌
async function caller(){
  try {
    const someValue = await example(); 
    // Promise 객체 실행 중 에러 발생 => catch 블록으로 넘어감 
    console.log(someValue);
  }
  catch(error){
    console.log('--Handling error--') 
    console.log(error); 
  }
}
caller()

console.log('프로그램 실행 끝')

// 프로그램 실행 끝
// --Handling error--
// Error: NoParameterError

 try-catch와 await 대신 promise의 catch() 함수를 사용할 수도 있다.

async function example(param){
  if (!param){
    throw new Error('NoParameterError');
  }
  return param
}

example()
.then((result) => {
  console.log(result)
})
.catch((error) => {
  console.log('--Handling error--') 
  console.log(error); 
})

console.log('프로그램 실행 끝')

// 프로그램 실행 끝
// --Handling error--
// Error: NoParameterError

 

4. express 미들웨어로 에러 핸들링

express에는 에러를 핸들링하는 자체 코드가 있어서

명시적으로 에러를 핸들링하지 않으면 express가 알아서 처리해준다.

// <func.js>
function example(param){
    if (!param){
      throw new Error('NoParameterError');
    }
    return param
}

async function asyncExample(param){
    if (!param){
      throw new Error('NoParameterError');
    }
    return param
}

module.exports = { example, asyncExample }
// <server.js>
const express = require('express');
const { example, asyncExample } = require('./func');

const app = express();

app.get('/example', (req, res) => {
    const someValue = example();
    res.json({result : someValue})
})

app.get('/async_example', async (req, res) => {
    const someValue = await asyncExample();
    res.json({result: someValue})
})

app.listen(8000)

// /example에 요청을 보내면 자동으로 500 에러와 에러 메시지(Error: NoParameterError)를 응답한다.
// /async_example에 요청을 보내면 응답을 보내지 않고 프로그램을 종료한다. 비동기 함수의 에러는 자동으로 핸들링할 수 없는 것 같다.

 

미들웨어를 추가하면 에러를 편리하게 일괄적으로 핸들링할 수 있다.

const express = require('express');
const { example, asyncExample } = require('./func');

const app = express();

app.get('/example', (req, res) => {
    const someValue = example();
    res.json({result : someValue})
})

app.get('/async_example', async (req, res) => {
    const someValue = await asyncExample();
    res.json({result: someValue})
})

app.use((err, req, res, next) => {
    if (err.message === 'NoParameterError'){
        res.status(400).json({message: '오류 발생! param이 없음'})
        return;
    }
    res.status(500).json({message: '오류 발생! 원인 모름'})
})

app.listen(8000, () => {
    console.log('listening at 8000')
})

// /example에 요청을 보내면 400 에러와 {message: '오류 발생! param이 없음'}을 응답한다.
// /async_example에 요청을 보내면 응답을 보내지 않고 프로그램을 종료한다.

 

미들웨어나 express 자체 기능을 사용하지 않고 수동으로 try-catch문을 사용할 수도 있다.

const express = require('express');
const { example, asyncExample } = require('./func');

const app = express();

app.get('/example', (req, res) => {
    const someValue = example();
    res.json({result : someValue})
})

app.get('/async_example', async (req, res) => {
    try{
        const someValue = await asyncExample();
        res.json({result: someValue})
    } 
    catch(err){
        res.status(400).json({message: '오류 발생! param이 없음'})
        return;
    }
    
})

app.listen(8000, () => {
    console.log('listening at 8000')
})

// async_example에 요청을 보내면 400 에러와 {message: '오류 발생! param이 없음'}을 응답함