Carryduo

Carryduo | 비동기와 try, catch 에러 핸들링

차가운에스프레소 2023. 1. 27. 18:45

1. 개요

- carryduo로 개발을 하던 중, try-catch로 에러 핸들링이 되지 않는 문제를 겪었다.

- nodeJS에서 try-catch로 에러 핸들링을 하는 일반적인 사례는 다음과 같다.

try{
	throw new Error('a')
} catch(err){
	if (err.message === 'a'){ 
		doSomething
	} else {
		return
	}
}

- 하지만 필자는 당혹스럽게도 다음과 같은 에러 문구를 만나게 되었다.

uncaughtException: <ErrorMessage>

- 이에, 자연스럽게 throw new Error와 try catch에 대해 좀 더 알아보게 되었다.


2. Error() 객체

- error 객체는 name, message, stack으로 구성되어 있고, Error를 생성할 때는 message 값만을 매개변수로 받는다.

- error 객체로 error를 생성하는 사례는 다음과 같다.

// 예시 1
throw new Error('hello')
// 결과물 = Error: hello 

// 예시 2
throw new Error({name: 'hi', message: 'hello'})
// 결과물 = Error: [Object Object]

- 요컨대, 일반적인 throw new Error()는 Error name을 'Error'로 지칭해서 생성하고, 매개변수 값을 message로 생성한다.

- 만약 에러 이름을 특정해주고 싶다면 다음과 같이 할 수 있다.

const error = new Error('hi')
error.name = 'hello'
throw error
// 결과물: hello: hi

3. try-catch에서 uncaughtedException error가 발생한 이유

- 필자가 uncaughtException을 목격한 코드는 다음과 같다.

- aws S3의 특정 버킷에 있는 Object를 조회하고, 해당 버킷에 Object가 있으면 에러를 발생시키고자 한 것이었다.

    try {
        s3.listObjectsV2({ Bucket: `${process.env.BUCKET}`, Prefix: `${oldVersion}/` }, (err, data) => {
                if (data.Contents) {
                    throw new Error('hi')
                } else {
                    return
                }
        })
    } catch (err) {
        console.error(err.name)
        return 'hi'
    }

 

- nodeJS 공식문서에서 uncaughtedExcpetion에 대해 설명을 소개하자면 다음과 같다.

Indicates if the exception originates from an unhandled rejection or from a synchronous error. Can either be 
'uncaughtException' or 'unhandledRejection' The latter is used when an exception happens in a Promise based async context (or if a Promise is rejected) and --unhandled-rejections flag set to strict or throw (which is the default) and the rejection is not handled, or when a rejection happens during the command line entry point's ES module static loading phase.

  - 요컨대, uncaughtedException은 예외처리 되지 않은 거부나 동기 에러에서 발생하고, 이와 유사한 unhandledRejection은 Prmoise에 async 환경에서 발생하는 에러인 것으로 파악된다.

- 즉, 위 코드 구문에서 try-catch 구문이 throw new Error('hi')를 catch 하지 못한 까닭은 throw new Error('hi')가 비동기 처리가 되어있는 메소드의 콜백함수에서 실행되기 때문에, throw new Error()가 실행되기 전에 try catch 구문은 종료되기 때문인 것으로 보인다.

 

- 이를 해결하기 위해서는 두 가지 방법이 있다.

1) 비동기 처리된 메소드의 콜백함수에 try-catch를 붙인다.

2) process.on()에 uncaughtedException에 대한 이벤트 핸들러를 등록하여 핸들링한다.

 

- 1)의 예시코드는 다음과 같다.

    try {
        s3.listObjectsV2({ Bucket: `${process.env.BUCKET}`, Prefix: `${oldVersion}/` }, (err, data) => {
            try {
                if (data.Contents) {
                    throw new Error('hi')
                } else {
                    return
                }
            } catch (err) {
                if (err.message === 'hi') {
                    console.log('hi error handler')
                }
                console.log(err)
                return err
            }
        })
    } catch (err) {
        console.error(err.name)
        return 'hi'
    }

- 2)는 다음과 같다.

process.on('uncaughtedException', (err) => {
	console.log(err)
    doSomething()
    return err.message
})

 

- nodeJS의 공식문서에 따르면, 2)번 은 권장되지 않는 방식이다. uncaughtedException은 말그대로 예기치 않게 예외처리되지 않은 예외 상황이므로, 개발자가 생각하지 못한 예외에 대응하기 위해, nodeJS에서 이에 대한 이벤트 핸들러로 기본적으로 stderr로 해당 에러를 출력하고, exitCode 1로 프로세스를 종료하도록 하고 있기 때문이다.


4. 소감

- 비동기 처리된 함수들은 대부분 async/await을 이용해서 동기적으로 사용하는 경우가 빈번했기 때문에, 위와 같은 사례에 적잖아 당황했었다.

- async/await은 비동기 처리된 함수들의 응답을 동기적으로 활용할 수 있게끔 지원해주는 기능이기 때문에, 비동기 함수에대한 try-catch에 대해 미숙했던 듯 하다.

- 비동기 함수에 대한 이해가 좀 더 필요하다고 느낀 경험이었다.


5. 참고 자료

- 비동기 코드에서의 에러 핸들링

 

[NodeJS] try/catch는 모든 에러를 잡을 수 있을까?

들어가며 node.js에서의 오류 처리에 대한 글을 공유받아 읽으며 테스트해보고 알게 된 점들을 정리해서 포스팅한다. try/catch는 모든 에러를 잡을 수 있을까? 이제부터 하나하나 파헤쳐보자. 동기

programmingsummaries.tistory.com

- uncaughtedException - nodeJS 공식문서

 

Process | Node.js v19.5.0 Documentation

Process# Source Code: lib/process.js The process object provides information about, and control over, the current Node.js process. import process from 'node:process';const process = require('node:process'); Process events# The process object is an instance

nodejs.org