모던 자바스크립트 Deep Dive : 47장 에러 처리

EdLee·2022년 12월 4일

javascript

목록 보기
37/37

47장 에러 처리

1. 에러처리의 필요성


console.log('[Start]');

foo(); // ReferenceError: foo is not defined
// 발생한 에러를 방치하면 프로그램은 강제 종료된다.

// 에러에 의해 프로그램이 강제 종료되어 아래 코드는 실행되지 않는다.
console.log('[End]');
  • 에러가 전혀 없는 프로그래밍은 불가능
  • 따라서 에러 대책을 미리 구비해둬야 한다
  • 방치된 에러는 프로그램을 강제로 종료 시킨다

console.log('[Start]');
try{
  foo();
} catch (error) {
  console.error('[에러 발생]', error)
  // ReferenceError
}
// 발생한 에러에 적절한 대응을 하면 프로그램이 강제 종료되지 않는다.
console.log('[End]');
  • 따라서 위처럼 try...catch 문으로 에러를 대응하곤 했다

// DOM에 button 요소가 존재하지 않으면 querySelector 메서드는 에러를 발생시키지 않고 null을 반환한다.
const $button = document.querySelector('button'); // null

$button.classList.add('disalbed');
// TypeError : Cannot read property 'classList' of null
  • 그런데 위 querySelector처럼 직접적으로 발생하지 않을 수도 있다
  • 위 경우 직후 코드에서 null을 받고선 TypeError가 발생해 프로그램이 죽었다

// 1. querySelector는 문법이 틀리면 에러를 내지만,
const $elem = document.querySelector('#1');
// DOMException : Failed to excute 'querySelector' on 'Document': '#1' is not a valid selector.

// 2. button이 null을 반환한다. 다음 코드가 죽지 않게 하려면 단축 평가나 옵셔널 체이닝 연산자가 필요 하다
const $button = document.querySelector('button'); // null
$button?.classList.add('disalbed'); // button이 null이 아닐 때만 실행

2. try... catch... finally 문


  • 기본적으로 에러 처리를 구현하는 방법은 크게 두 가지
    1. if 문이나 단춘 평가 또는 옵셔널 체이닝 연산자를 통해 확인해서 처리
    2. try...catch...finally 문 사용(일반적으로 에러 처리라고 하면 이것을 의미)
try{
  // 실행할 코드(에러가 발생할 가능성이 있는 코드)
} catch (err) {
  // try 코드 블록에서 에러가 발생하면 이 코드 블록의 코드가 실행된다.
  // err에는 try 코드 블록에서 발생한 Error 객체가 전달된다.
} finally {
  // 에러 발생과 상관없이 반드시 한 번 실행된다.  
}
  • 위와 같이 사용한다

console.log('[Start]');
try {
  // 에러가 발생할 가능성이 있는 코드
  foo();
} catch (err) {
  // try 코드 블록에서 에러가 발생하면 실행될 코드
  // err에는 try 코드 블록에서 발생한 Error 객체가 전달된다
  console.error(err); // ReferenceError: foo is not defined
} finally {
  // 에러 발생과 상관없이 반드시 한 번 실행
  console.log('finally');
}

// try... catch... finally문으로 에러를 처리하면 프로그램이 강제 종료되지는 않는다
console.log('[End]');

3. Error 객체


const error = new Error('invalid');
  • Error 생성자 함수는 에러 객체를 생성한다
  • Error 생성자 함수에는 에러를 상세히 설명하는 에러 메세지를 인수로 전달할 수 있다
  • 생성된 Error에는 앞서 인수로 전달한 message 프로퍼티와 함수 호출 스택 정보인 stack 프로퍼티가 포함된다.
생성자 함수인스턴스
Error일반적 에러 객체
SyntaxError자바스크립트 문법에 맞지 않는 문을 해석할 때 발생하는 에러 객체
ReferenceError참조할 수 없는 식별자를 참조했을 때 발생하는 에러 객체
TypeError피연산자 또는 인수의 데이터 타입이 유효하지 않을 때 발생하는 에러 객체
RangeError숫자값의 허용 범위를 벗어났을 때 발생하는 에러 객체
URIErrorencodeURI 또는 decodeURI 함수에 부적절한 인수를 전달했을 때 발생하는 에러 객체
EvalErroreval 함수에서 발생하는 에러 객체
1 @ 1;    // SyntaxError: Invalid or unexpected token
foo();    // ReferenceError: foo is not defined
null.foo; // TypeError: Cannot read property 'foo' of null
new Array(-1); // RangeError: Invalid array length
decodeURIComponent('%'); // URIError: URI malformed

4. throw 문


try {
  // 에러 객체를 생성한다고 에러가 발생하는 것은 아니다.
  new Error('something wrong');
} catch (error) {
  console.log(error);
}
  • Error 객체를 만들었다고 에러가 발생하는 것은 아니다
  • Error를 발생시키려면 rty 코드 블록에서 throw 문으로 에러 객체를 던져야 한다

try {
  // 에러 객체를 던지면 catch 코드 블록이 실행되기 시작한다.
  throw new Error('something wrong');
} catch (error) {
  console.log(error); // Error: something wrong
}

에러 생성 예시

// 외부에서 전달받은 콜백 함수를 n번만큼 반복 호출
const repat = (n, f) => {
  // 매개변수 f에 전달된 인수가 함수가 아니면 TypeError를 발생시킨다
  if (typeof f !== 'function') throw new TypeError('f must be a function');
  
  for (var i = 0; i < n ; i++) {
    f(i); // i를 전달하면서 f를 호출
  }
};

try {
  repeat(2, 1); // 두 번째 인수가 함수가 아니므로 TypeError가 발생(throw)한다
} catch (err) {
  console.error(err); // typeError: f must be a function
}

5. 에러의 전파


  • 에러는 호출자 방향으로 전파된다
    => 즉, 콜 스택의 아래 방향(실행 중인 실행 컨텍스트가 푸시되기 직전에 푸시된 실행 컨텍스트 방향)으로 전파
const foo = () => {
  throw Error('foo에서 발생한 에러'); // ④
};

const bar = () => {
  foo(); // ③
};

const baz = () => {
  bar(); // ②
};

try {
  baz(); // ①
} catch (err) {
  console.error(err);
}

①에서 baz 함수를 호출하면
②에서 bar 함수가 호출된 후,
③에서 foo함수가 호출된다
④에서 foo 함수가 에러를 throw한다
=> 이 때 foo 함수가 throw한 에러는 다음과 같이 호출자에게 전파되어 전역에서 캐치

0개의 댓글