본문 바로가기

항해99_10기/105일의 TIL & WIL

[3주차] [20221129] 에러 핸들링

에러 핸들링이란?

스크립트가 돌다가, 에러를 만나면 프로그램이 죽으면서 콘솔에 에러와 메세지가 출력된다. 프로그램이 에러를 만나도, 꺼지는 것을 방지하기 위해 에러 핸들링을 한다. 

 

자바스크립트에서는 try...catch를 사용하여 "런타임 에러" 핸들링이 가능하다.

 

  • try... catch 구문 리터럴
try {

	// 에러가 날 가능성이 있는 코드
    // 에러가 없다면, catch 블럭으로 넘어가지 않고 끝까지 코드를 실행

} catch (err){

	// try 구문에서 발생한 에러를 핸들링하고, 프로그램을 죽이지는 않음
	// 발생한 에러를 err 객체로 받아옴.
    // 에러의 주요 프로퍼티는 name과 message가 있으며, 비표준 프로퍼티로 에러가 발생한 스택 번호를 알 수 있는 stack 등이 있음

}

 

프로그램에서 알아서 에러를 캐치할 수도 있지만, 사용자가 원하는 곳에서 직접 에러 객체를 만들어 던질 수도 있다.

const error = new Error(message)

throw error
throw new Error("에러를 던지자")

/* 표준 에러 객체 생성자 
Error
SyntaxError
ReferenceError
TypeError
*/

 

catch는 처리하도록 정의 된 에러만 처리하고, 그 외의 나머지 에러는 다시 던져야 한다.

function test() {
	try {
        throw new SyntaxError("catch에서 처리하도록 정의 된 에러")
    } catch (err) {
        if (err instanceof SyntaxError) {
            console.log(err.message)
        } else {
            throw e	// 에러 다시 던지기
        }
    }
}

try {
	test();
} catch (e) {
	console.log(e) // test() 함수 안의 try..cat문에서 다시 던진 에러를 여기서 잡음
}

이렇게 다시 던져진 에러는 try...catch문 밖으로 던져지고, 상위 스코프에 try...catch가 없다면 스크립트는 죽는다.

이렇게, 에러를 다시 던지는 방법을 사용하면, 블록 단위로 알고 있는 에러만 처리하고, 알 수 없는 에러는 밖의 블록으로 던질 수 있다.

 

마지막으로, try..catch..finally가 있다.

finally는 try -> error -> catch -> finally의 순으로 실행되거나, try -> finally 순으로 실행된다.

즉, error 발생의 유무와는 상관 없이, 무조건 finally 블록을 실행한다.

let num = +prompt("양의 정수를 입력해주세요.", 35)

let diff, result;

function fib(n) {
  if (n < 0 || Math.trunc(n) != n) {
    throw new Error("음수나 정수가 아닌 값은 처리할 수 없습니다.");
  }
  return n <= 1 ? n : fib(n - 1) + fib(n - 2);
}

let start = Date.now();

try {
  result = fib(num);
} catch (e) {
  result = 0;
} finally {
  diff = Date.now() - start;
}

alert(result || "에러 발생");

alert( `연산 시간: ${diff}ms` );

finally를 사용할 때, 주의 점!

  • try...catch 블록에 return이 있어도 finally가 실행된다. =>  return 값이 밖으로 반환 되기 전, finally 블록을 먼저 호출하고 return을 반환함
  • try...finally 문도 가능함 => 이 경우, try 문에서 에러가 발생하면 스크립트가 죽지만, 죽더라도 finally 블록을 완료함

 

전역 catch

자바스크립트 호스트 환경에서는 대부분 try...catch에서 처리하지 못한 에러를 잡는 방법을 제공한다.

import process from 'node:process';

process.on('uncaughtException', (err, origin) => {
  fs.writeSync(
    process.stderr.fd,
    `Caught exception: ${err}\n` +
    `Exception origin: ${origin}`
  );
});

setTimeout(() => {
  console.log('This will still run.');
}, 500);

// Intentionally cause an exception, but don't catch it.
nonexistentFunc();
console.log('This will not run.');

 

  • 브라우저 환경에서는 window.onerror를 이용해 에러를 처리할 수 있다. window.onerror 프로퍼티에 함수를 할당하면, 에러를 만났을 때 해당 함수를 실행한다. 하지만, 보통 해당 방식으로 에러를 핸들링하지는 않고, 디버깅 용도로만 사용한다고 한다.
// 뜻밖의 에러를 만났을 때 처리 함수 할당
window.onerror = function(message, url, line, col, error) {
  // ...
};

// 보통 에러 디버깅을 위한 사용
window.onerror = function(message, url, line, col, error) {
    alert(`${message}\n At ${line}:${col} of ${url}`);
  };