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

2024. 12. 10. 13:47·개발서적/모던 자바스크립트 Deep Dive

⭐️ 노션에서 보기

에러 처리의 필요성

  • 에러가 발생하지 않는 코드를 작성하는 것은 불가능하며 에러는 언제나 발생할 수 있음

  • 발생한 에러에 대해 대처하지 않고 방치하면 프로그램은 강제 종료됨

      console.log('[Start]');
    
      foo(); // ReferenceError: foo is not defined
      // **발생한 에러를 방치하면 프로그램은 강제 종료된다.**
    
      // **에러에 의해 프로그램이 강제 종료되어 아래 코드는 실행되지 않는다.**
      console.log('[End]');
  • try … catch 문을 사용해 발생한 에러에 적절하게 대응하면 프로그램이 강제 종료되지 않고 계속해서 코드를 실행시킬 수 있음

      console.log('[Start]');
    
      **try** {
        foo();
      } **catch** (error) {
        console.error('[에러 발생]', error);
        // [에러 발생] ReferenceError: foo is not defined
      }
    
      // 발생한 에러에 적절한 대응을 하면 프로그램이 강제 종료되지 않는다.
      console.log('[End]');
  • 직접적으로 에러를 발생하지는 않는 예외적인 상황이 발생할 수도 있음. 예외적인 상황에 적절하게 대응하지 않으면 에러로 이어질 가능성이 큼

      // DOM에 button 요소가 존재하지 않으면 querySelector 메서드는 에러를 발생시키지 않고 null을 반환한다.
      const $button = document.querySelector('button'); **// null**
    
      $button.classList.add('disabled');
      **// TypeError: Cannot read property 'classList' of null**
    • querySelector 메서드는 인수로 전달한 문자열이 CSS 선택자 문법에 맞지 않는 경우 에러를 발생시킴

        const $elem = document.querySelector('#1');
        // DOMException: Failed to execute 'querySelector' on 'Document': '#1' is not a valid selector.
    • 하지만 querySelector 메서드는 인수로 전달한 CSS 선택자 문자열로 DOM에서 요소 노드를 찾을 수 없는 경우 에러를 발생시키지 않고 null을 반환함. 이때 if 문으로 querySelector 메서드의 반환값을 확인하거나 단축 평가 또는 옵셔널 체이닝 연산자 ?.를 사용하지 않으면 다음 처리에서 에러로 이어질 가능성이 큼

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

⇒ 이처럼 에러나 예외적인 상황에 대응하지 않으면 프로그램은 강제 종료됨. 에러나 예외적인 상황은 너무나 다양하기 때문에 아무런 조치 없이 프로그램이 강제 종료된다면 원인을 파악하여 대응하기가 어려움

⇒ 우리가 작성한 코드에서는 언제나 에러나 예외적인 상황이 발생할 수 있다는 것을 전제하고 이에 대응하는 코드를 작성하는 것이 중요

try … catch … finally 문

  • 기본적으로 에러 처리를 구현하는 방법은 크게 두 가지가 있음

    1. 에외적인 상황이 발생하면 반환하는 값(null 또는 -1)을 if 문이나 단축 평가 또는 옵셔널 체이닝 연산자를 통해 확인해서 처리하는 방법

    2. 에러 처리 코드를 미리 등록해두고 에러가 발생하면 에러 처리 코드로 점프하도록 하는 방법 (에러 처리)

      ⇒ try … catch … finally문

  • try … catch … finally 문

      try {
          // 실행할 코드(에러가 발생할 가능성이 있는 코드)
      } catch (err) {
          // try 코드 블록에서 에러가 발생하면 이 코드 블록의 코드가 실행된다.
          // err에는 try 코드 블록에서 발생한 Error 객체가 전달된다.
      } finally {
          // 에러 발생과 상관없이 반드시 한 번 실행된다.
      }
    • finally 문은 불필요하다면 생략 가능. catch 문도 생략 가능하지만 catch 문이 없는 try 문은 의미가 없으므로 생략하지 않음

    • 먼저 try 코드 블록이 실행되어 try 코드 블록에 포함된 문 중에서 에러가 발생하면 발생한 에러는 catch문의 err 변수에 전달되고 catch 블록이 실행됨

    • catch 문의 err 변수(변수 이름은 무엇이든 상관 없음)는 try 코드 블록에 포함된 문 중에서 에러가 발생하면 생성되고 catch 코드 블록에서만 유효함

    • finally 코드 블록은 에러 발생과 상관없이 반드시 한 번 실행됨

    • try … catch … 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]');

      
      

Error 객체

  • Error 생성자 함수는 에러 객체를 생성함

    • 에러를 상세히 설명하는 에러 메시지를 인수로 전달할 수 있음

        const error = new Error('invalid');
  • Error 생성자 함수가 생성한 에러 객체가 가지는 프로퍼티

    • message 프로퍼티: Error 생성자 함수에 인수로 전달한 에러 메시지
    • stack 프로퍼티: 에러를 발생시킨 콜 스택의 호출 정보를 나타내는 문자열로, 디버깅 목적으로 사용
  • 자바스크립트는 Error 생성자 함수를 포함해 7가지의 에러 객체를 생성할 수 있는 Error 생성자 함수를 제공

    • SyntaxError, ReferenceError, TypeError, RangeError, URIError, EvalError 생성자 함수가 생성한 에러 객체의 프로토타입은 모두 Error.prototype을 상속받음

      생성자 함수 인스턴스
      Error 일반적 에러 객체
      SyntaxError 자바스크립트 문법에 맞지 않는 문을 해석할 때 발생하는 에러 객체
      ReferenceError 참조할 수 없는 식별자를 참조했을 때 발생하는 에러 객체
      TypeError 피연산자 또는 인수의 데이터 타입이 유효하지 않을 때 발생하는 에러 객체
      RangeError 숫자값의 허용 범위를 벗어났을 때 발생하는 에러 객체
      URIError encodeURI 또는 decodeURI 함수에 부적절한 인수를 전달했을 때 발생하는 에러 객체
      EvalError eval 함수에서 발생하는 에러 객체
      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

throw 문

  • Error 생성자 함수로 에러 객체를 생성한다고 해서 에러가 발생하는 것은 아님

    = 에러 객체 생성과 에러 발생은 의미가 다름

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

      throw 표현식;
  • throw 문의 표현식은 어떤 값이라도 상관없지만 일반적으로 에러 객체를 지정함

    • 에러를 던지면 catch문의 에러 변수가 생성되고 던져진 에러 객체가 할당됨. 그리고 catch 코드 블록이 실행되기 시작

      try {
      // 에러 객체를 던지면 catch 코드 블록이 실행되기 시작한다.
      **throw new Error('something wrong');**
      } **catch (error)** {
      console.log(error);
      }
    • eg. 외부에서 전달받은 콜백 함수를 n번만큼 반복 호출하는 repeat 함수

        // 외부에서 전달받은 콜백 함수를 n번만큼 반복 호출한다
        const repeat = (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
        }
      • repeat 함수는 에러를 발생시킬 가능성이 있으므로 try 코드 블록 내부에서 호출해야 함

에러의 전파

  • 에러는 호출자 방향으로 전파됨

    = 콜 스택의 아래 방향(실행 중인 컨텍스트가 푸시되기 직전에 푸시된 실행 컨텍스트 방향)으로 전파됨

      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한 에러는 다음과 같이 호출자에게 전파되어 전역에서 캐치됨

  • throw된 에러를 캐치하지 않으면 호출자 방향으로 전파됨
    • throw된 에러를 캐치하여 적절히 대응하면 프로그램을 강제 종료시키지 않고 코드의 실행 흐름을 복구할 수 있음
    • throw된 에러를 어디에서도 캐치하지 않으면 프로그램은 강제 종료됨
  • 주의할 것은 비동기 함수인 setTimeout이나 프로미스 후속 처리 메서드의 콜백 함수는 호출자가 없다는 것
    • 콜백 함수는 태스크 큐나 마이크로태스크 큐에 일시 저장되었다가 콜 스택이 비면 이벤트 루프에 의해 콜 스택으로 푸시되어 실행됨
    • 이때 콜 스택에 푸시된 콜백 함수의 실행 컨텍스트는 콜 스택 가장 하부에 존재하게 되므로 에러를 전파할 호출자가 존재하지 않음
저작자표시 비영리 변경금지 (새창열림)

'개발서적 > 모던 자바스크립트 Deep Dive' 카테고리의 다른 글

[모던 자바스크립트 Deep Dive] 48장: 모듈  (1) 2024.12.10
[모던 자바스크립트 Deep Dive] 46장: 제너레이터와 async/await  (1) 2024.12.10
[모던 자바스크립트 Deep Dive] 45장: 프로미스  (0) 2024.12.05
[모던 자바스크립트 Deep Dive] 44장: REST API  (1) 2024.12.05
[모던 자바스크립트 Deep Dive] 43장: Ajax  (0) 2024.12.05
'개발서적/모던 자바스크립트 Deep Dive' 카테고리의 다른 글
  • [모던 자바스크립트 Deep Dive] 48장: 모듈
  • [모던 자바스크립트 Deep Dive] 46장: 제너레이터와 async/await
  • [모던 자바스크립트 Deep Dive] 45장: 프로미스
  • [모던 자바스크립트 Deep Dive] 44장: REST API
jisunipark
jisunipark
일신우일신 | 日新又日新 압축성장 중인 개발자입니다.
  • jisunipark
    일신우일신
    jisunipark
    • 분류 전체보기 (89)
      • Frontend (12)
        • HTML ∙ CSS ∙ JavaScript (8)
        • React.js (4)
        • Next.js (0)
      • Backend (5)
        • Express.js (2)
        • Flask (3)
      • Language (27)
        • Python (24)
        • Java (3)
        • Kotlin (0)
      • Etc. (3)
        • Git ∙ GitHub (3)
      • 개발서적 (19)
        • 모던 자바스크립트 Deep Dive (16)
        • 리팩터링 2판 (3)
      • 스터디 (6)
        • JavaScript30 (5)
        • 독서 스터디 (1)
      • 배움과 성장 (4)
        • 컨퍼런스 ∙ 세미나 (1)
        • 생각 기록 (3)
      • 회고 (5)
      • English ver. (4)
  • 인기 글

  • 최근 글

  • hELLO· Designed By정상우.v4.10.1
jisunipark
[모던 자바스크립트 Deep Dive] 47장: 에러 처리
상단으로

티스토리툴바