ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • Section 11. 자바스크립트 에러 핸들링
    자바스크립트 2023. 4. 7. 14:49

    에러 핸들링 error handling 의 필요성

    에러 발생에 대비하지 않으면 프로그램이 종료된다. 

    console.log('에러 발생 전');
    
    // ⚠️ 오류를 발생시키는 코드
    (3).split('');
    
    // 출력되지 않음
    console.log('에러 발생 후');

     

    I. 자바스크립트의 에러 핸들링

    1. try ...catch 문

    console.log('에러 발생 전');
    
    try {
      (3).split('');
    
    } catch (e) {
      console.error('🛑 에러!!', e);
    }
    
    console.log('에러 발생 후');

    try ...catch문을 썼더니 맨 위에 있는 예시에서는 프로그램이 멈춰서 마지막 출력문이 출력 되지 않았지만 이번에는 출력이 되었다. 

     

    정리하자면

    try 블록

    • 에러 발생 여지가 있는 코드를 포함한다.
    • 이곳에서 발생한 에러는 프로그램을 멈추지 않는다.

    catch 블록

    • 에러 발생시 실행할 코드를 포함한다.
    • 발생한 오류 객체를 인자로 받는다. 
    const arr = ['ABC', '가나다', 123, '123'];
    
    function getLetterArray (str) {
      // 💡 인자로 어떤 타입의 값이 주어질지 모르는 상황
      try {
        return str.split('');
    
      } catch (e) {
        console.error('🛑 에러!!', e);
        return [];
      }
    }
    
    arr.forEach(i => {
      console.log(getLetterArray(i));
    });

    인자로 어떤 값을 받을지 모르는 상황에서 try블록 에서는 일단 작업을 한다. 그리고 아무 에러도 없으면 함수를 종료한다. 

    try블록에서 에러가 발생하게 되면 catch 블록으로 넘어가서 작업을 하고 반환값으로는 빈 배열을 반환하고 함수를 종료하게 된다. 

     

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

    finally 블록

    오류가 발생 여부와 관계없이 한 번 실행되는 코드를 포함한다.

    try나 catch문에 return이 있더라도 반드시 실행된다.

     

    function connect () { console.log('☀️', '통신 연결'); }
    function disconnect () { console.log('🌙', '통신 연결 해제'); }
    function sendArray (arr) { console.log('전송', arr); }
    
    function sendStringAsArray (str) {
      connect();
    
      try {
        sendArray(str.split(''));
        return true;
    
      } catch (e) {
        console.error('🛑 에러!!', e);
        return false;
    
      } finally {
        // 💡 전송 성공 여부와 관계없이 연결은 끊어야 함
        disconnect();
        console.log('- - - - - - - -');
      }
    
      // ❓ 이곳에 넣는 것과 무엇이 다른가?
      // 아래로 대체하여 실행해 볼 것
      // disconnect();
      // console.log('- - - - - - - -');
    }
    
    ['ABC', '가나다', 123, '123'].forEach(i => {
      console.log(
        sendStringAsArray(i) 
        ? '[성공]' : '[실패]', '\n\n'
      );
    });

    오류발생 여부와 관게없이 finally 블록에 있는 작업은 실행이 됐다.  

    그렇다면 finally 블록에서의 작업을 밖으로 빼서 실행하면 어떨까?

    함수는 return문을 만나면 종료된다. 그리고 disconnect가 실행이 되지 않아 출력이 안되는 것을 볼 수 있다. 실무에서 connect 등을 자원을 썼으면 반드시 종료를 해 주어야 하는데 이처럼 finally에서 disconnect를 하지 않으면 자원이 부족해져서 서버의 기능이 마비될 수 있으니 조심해야 한다.

     

    II. Error 객체

    • 에러 발생 시 던져지는 thrown 객체이다.
    • 에러에 대한 정보를 담고 있다.
    • 에러가 발생하지 않아도, 직접 생성하여 던지기가 가능하다.

    1. 기본 생성과 사용법

    const error = new Error('철권 8 발매일');
    
    console.error(error);

    두 번째 인자로 이유를 명시할 수도 있다.

    const error = new Error(
      '철권 8 발매일',
      { cause: '보이지가 않음' }
    );
    
    console.error(error);

    이것을 확인해보면 다음과 같다

    console.log(error.name); //Error
    console.log(error.message); //철권 8 발매일
    
    // cause를 입력했을 경우
    console.log(error.cause); //보이지가 않음

    이처럼 에러 객체의 인스턴스 프로퍼티와 메서드를 통해서 내용을 확인 할 수도 있다.

     

    의도적으로 에러 발생시키는 법

    throw new Error('이유를 묻지 마라');

     

    2. 에러의 여러 종류

    아래의 에러들은 모두 Error부터 상속받는다.

    어떤 문제에 의한 에러인지 쉽게 식별 가능하도록 하는 것들이다.

     

    오류에 종류에 따라 대처하는 예시

    const errorFuncs = [
      // 자료형에 맞지 않는 메서드 호출
      () => { (3).split(''); },
    
      // 선언되지 않은 함수 호출
      () => { hello(); },
    
      // 부적절한 숫자를 인자로 전달
      () => { (123.45).toFixed(-1); }
    ];
    errorFuncs.forEach(func => {
      try {
        func();
    
      } catch (e) {
        if (e instanceof TypeError) {
          console.error('자료형 확인하세요.');
          return;
        }
        if (e instanceof ReferenceError) {
          console.error('선언 안 된 거 쓴 거 없는지 확인하세요.');
          return;
        }
        console.error('아니, 뭘 한 거에요?');
      }
    });

    여러 종류의 에러를 발생시키는 함수들을 각각 만들고 catch 문을 활용하여 오류의 종류에 따라 대처하게 만들었다.

     

    직접 오류를 생성하여 던지기

    // 특정 월의 당번으로 지원하는 함수
    function applyForMonth (date) {
      try {
        if (typeof date !== 'number') {
          throw new TypeError('숫자로 입력해주세요.');
        }
        if (date < 1 || date > 12) {
          throw new RangeError('유효한 월을 입력해주세요.');
        }
    
        console.log(`${date}월 당번으로 등록되셨습니다.`);
    
      } catch (e) {
        console.error('🛑 에러 발생!', e);
        console.log('다시 등록해주세요.');
      }
    }
    applyForMonth('5');
    
    applyForMonth(13);

    각각 에러가 발생한다. 컴퓨터는 13이 들어갔을때 이것이 오류라는 것을 인지하지 못하기 때문에 수동적으로 오류를 발생시킨 것이다. 

     

    III. 에러 버블링 error bubbling

    다른 함수를 호출했을 때

    • 에러 발생시 해당 함수에서 잡지 않으면 호출한 곳으로 던져진다.
    • 다중 호출시 에러를 핸들링하는 코드가 있는 호출자까지 전달된다.
    function func1 () {
      throw new Error('에러');
    }
    
    function func2 () {
      func1();
    }
    
    function func3 () {
      func2();
    }
    
    function func4 () {
      try {
        func3();
    
      } catch (e) {
        console.error(e);
      }
      console.log('실행완료');
    }
    
    func4();

    func1 에서 에러가 났는데 func1에서 에러를 처리하는 로직이 없어서 func1을 호출한 func2로 애러를 핸들링하는 코드가 전달되고 거기에도 없으니 func2를 호출한 func3로 이동하고 결국 func4까지 가서 에러가 처리된 것을 볼 수 있다. 

    이런 현상은 좋지 않으니 에러는 가능한 발생한 곳 가까이서 처리하는 것이 좋다.

     

Designed by Tistory.