Skip to content

Instantly share code, notes, and snippets.

@joonseokhu
Last active March 3, 2021 08:52
Show Gist options
  • Save joonseokhu/47b254776bd1c90772482341b3a856a7 to your computer and use it in GitHub Desktop.
Save joonseokhu/47b254776bd1c90772482341b3a856a7 to your computer and use it in GitHub Desktop.

TL;TR

async 에서 try-catch 로 전체 코드를 묶고 catch 문에서 throw e 하는 것과 try-catch문을 아예 쓰지 않는것은 흐름상으로나 결과적으로나 완전히 같은 결과를 만들어냅니다.

테스트할 코드

const makeError = async () => { throw new Error('에러 클래스에 의한 에러') }

const withTryCatch = async () => {
  try {
    console.log('try-cath 를 사용한 async')
    const result = await makeError();
    console.log('withTryCatch - 에러가 발생하는 위치 아래에 있는 코드 (실행되면 안됨)');
    return result;
  } catch (err) {
    throw err;
  }
}

const withoutTryCatch = async () => {
  console.log('try-cath 없는 async')
  const result = await makeError();
  console.log('withoutTryCatch - 에러가 발생하는 위치 아래에 있는 코드 (실행되면 안됨)');
  return result;
}

withTryCatch()
.then(res => {
  console.log('withTryCatch - 성공결과', res)
}).catch(err => {
  console.log('withTryCatch - 실패결과', err.message)
});

withoutTryCatch()
.then(res => {
  console.log('withoutTryCatch - 성공결과', res)
})
.catch(err => {
  console.log('withoutTryCatch - 실패결과', err.message)
})

콘솔 결과

'try-cath 를 사용한 async'
Promise { <pending> }
'try-cath 없는 async'
Promise { <pending> }
'withTryCatch - 실패결과' '에러 클래스에 의한 에러'
'withoutTryCatch - 실패결과' '에러 클래스에 의한 에러'

결과분석

try-catch 를 쓰든 안쓰든 상관없이

  • 에러가 발생하는 코드 다음에 있던 코드들은 실행되지 않습니다.
  • 발생한 에러는 Promise.reject 처리되어 상위 컨텍스트에서 비동기 에러로 처리됩니다.

따라서 상위 컨텍스트로 에러를 전파하기 위해 async 함수의 내부를 try-catch로 묶을 필요는 없습니다.

Node.js / Express.js 에서의 적용

try-catch 는 라우터에 의해 실행되는 최상위 실행 컨텍스트인 미들웨어 레이어에서만 수행하면 됩니다. 미들웨어에서는 에러를 상위 컨텍스트로 전달해도 받을 곳이 없으며, 에러발생시 next(error)res.status(500).send 와 같이 클라이언트로 에러 결과를 응답하기 위한 로직을 수행하는게 더 바람직하기 때문입니다.

하지만 그 외의 async 함수에선 try-catch 를 매번 작성할 필요가 없습니다.

app.get('/foo', async (req, res, next) => {
  try {
    // 컨트롤러 로직
    
    // 비동기 결과가 reject 라면 catch 문으로 갑니다.
    const some = await something();
    
    // 비동기 결과가 reject 라면 catch 문으로 갑니다.
    const another = await anotherThing();
    
    if (another.result > 30) {
      // Promise.reject를 리턴해도 async 함수에선 에러가 던져진것과 동등한것으로 간주됩니다.
      return Promise.reject({
        message: '뭔가 비즈니스적으로 30을 초과하면 안되는 그런 것',
        status: 403,
      })
    }
    
    // 앞쪽에서 아무런 문제도 없어야 성공결과가 응답됩니다.
    res.json(another);
    
  } catch (err) {
    // 여기에선 따로 throw err를 하지 않습니다.
    // 상위 컨텍스트로 에러를 전파하는게 아니라 클라이언트로 에러를 응답해야 하는 레이어입니다.
    res.status(err.status || 500).json({
      message: err.message || 'unknown error'
    })
  }
});
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment