본문 바로가기
Study/JavaScript

JavaScript) 예외처리란?

by JongIk 2022. 1. 7.
반응형

예외처리란?

  • 예외는 간단하게 말해 오류가 난 상황을 얘기합니다.
  • 일반적으로 프로그램에 오류가 발생하면 해당 프로그램은 강제적으로 종료됩니다. 하지만 자바스크립트 프로그램에서 발생한 오류는 굳이 프로그램을 종료하지 않더라도 오류만 적절하게 처리해준다면 프로그램을 계속 실행시킬 수 있습니다.

더 자세히

  • 프로그램을 실행하면서 예상치 못한 오류가 발생할 수도 있고, 오류는 아니지만 특별한 대처가 필요한 예외적인 상황이 생길 수 도 있습니다.
    예를 들어 파일이 존재하지 않거나 함수에 주어진 인수가 그 함수가 사용하려는 데이터 타입이 아닌 경우 등이 있습니다. 이 때 함수는 일반적으로 null 값을 반환해 오류를 알리는 방법을 사용하지만, 또 다른 방법으로 예외(Excepttion)던지는(Catch) 방법이 있습니다.
    예외를 던져서 오류 또는 예외 조건이 발생했다는 사실을 통지하고, 통지를 받은 쪽은 예외를 받아서 적절한 처리를 해주게 됩니다.
  • 예외를 받아서 처리하는 부분을 가리켜 예외 처리기라고 하고, 예외를 받는 작업을 가리켜 예외를 잡는다 (catch 한다) 라고 합니다.
  • JavaScript 에서는 throw 문으로 예외를 던지고 try/catch/finally 문으로 예외를 잡아서 처리하게 됩니다.

JavaScript 에서의 예외처리

throw 문

  • throw 문에서는 예외를 던집니다.
    throw 표현식;

  • 표현식으로는 어떠한 타입의 값도 지정할 수 있습니다.

  • 사용자에게 표시할 오류 메시지가 포함된 문자열이나 오류 코드를 의미하는 숫자도 허용됩니다. 하지만 일반적으로 Error 객체나 Error 객체를 상속받은 객체를 지정합니다.

  • 예시

    function permutation(a) {
    if( !(a instanceof Array) ){
      throw new Error(a+"is not an Array");
    }
    }
    permutation("ABC");  // Error: ABC is not an Array
  • 예외를 던지면 자바스크립트 인터프리터가 프로그램의 실행을 중단하고 바깥 블록에서 예외를 처리하는 예외 처리기를 찾습니다.
    예외 처리기는 바로 try/catch/finally 절 부분의 catch 문을 의미합니다. 예외처리기가 없으면 프로그램을 그냥 종료합니다.

Error 객체

자바스크립트에는 예외를 표현하기 위한 내장 객체 7가지가 있습니다.

예외를 표현하는 내장객체의 생성자

생성자 생성하는 인스턴스
Error 범용적인 예외 객체
EvalError eval 함수와 관련해서 발생한 예외 객체
RangeError 숫자 값이 허용 범위를 벗어났을 때 발생하는 예외 객체
ReferenceError 잘못된 참조를 만났을 때 발생하는 예외 객체
SyntaxError 자바스크립트 문법에 맞지 않는 구문을 만났을 때 발생하는 예외 객체
TypeError 변수 및 인수의 타입이 유효하지 않을 때 발생하는 예외 객체
URIError encodeURI와 decodeURI 메서드에 잘못된 인수가 전달되었을 때 발생하는 예외 객체
  • 예외를 표현하는 모든 내장 객체는 Error.prototype의 프로퍼티와 메서드를 상속받습니다.
  • Error.prototype의 프로퍼티
    • message : 오류 메시지를 의미하는 문자열
    • name : 오류의 이름
    • toString : 지정된 객체를 표현하는 문자열을 반환

try/catch/finally 문

  • 예외가 던져졌을 때 그것을 잡아서 처리하는 부분입니다.

    try{
      // 실행할 코드 ( 예외가 발생할 수 있는 코드 )
    } catch(exception){
      // try 블록에서 예외가 발생했을 때 실행되는 코드
        // exception에는 예외 값이 들어와 이 값을 바탕으로 예외를 처리
    } finally{
        // try 블록 코드와 catch 블록 코드가 실행된 이후에 반드시 실행됨
    }
  • finally 블록 안의 코드는 예외 처리에 마지막에 반드시 실행됩니다.
    try 블록이나 catch 블록에서 return, continue, break 문을 만나더라도 반드시 finally 블록 안의 코드가 실행이 됩니다.

예외가 여러 개 발생했을 때의 대처

  • instanceof 연산자 이용

    try{
    // try 블록에서 오류가 발생한다
    } catch(e){
       if( e instanceof TypeError ){
          // TypeError가 발생했을 때의 처리 코드 작성
      }else if( e instanceof ReferenceError ) {
            // ReferenceError 가 발생했을 때의 처리 코드 작성
      }else{
            // 나머지 경우의 에러가 발생했을 때의 처리 코드 작성
      }
    }

비동기 처리의 콜백 함수가 던진 예외의 처리

  • 비동기 처리의 콜백함수가 던진 예외는 콜백함수를 넘긴 함수로 전파되지 않습니다.
try {
  setTimeout(function throwError(){
    throw new Error("오류가 발생했습니다");    
  },1000);
} catch(e){
  console.log("예외 캐치 ->" + e);
}

위 코드의 실행 결과는
Uncaught Error: 오류가 발생했습니다
라는 내용이 표시되고 예외가 캐치되지 않은 상태로 프로그램이 종료됩니다.

예외를 던지는 콜백함수 throwError 는 함수 정의가 try 블록 안에 있을 뿐 try 블록 안에서 호출된 것은 아닙니다. 이 콜백 함수를 호출한 주체는 타이머 이벤트이며 try 블록 안에서 발생한 예외가 아닙니다. 그래서 예외를 잡을 수 없습니다.

이렇게 비동기 처리의 콜백 함수에서 발생한 예외는 바깥의 try/catch 문으로는 잡을 수 없습니다.

ES6 부터 추가된 제너레이터를 활용하면 이러한 비동기 처리 중 발생한 예외를 잡을 수 있습니다.

  • 아래 함수 run 은 인수로 받은 함수 callback을 실행시킵니다. 이때 인수로 받은 함수가 비동기 처리를 하는 중에 발생시킨 예외도 잡아서 처리합니다.
  • 두 번째 이후 인수로 callback 함수가 원래 받아야 하는 모든 인수를 지정할 수 있습니다. callback 함수는 첫 번째 인수로 제너레이터의 참조인 g를 받아야 합니다. 그리고 callback 함수 안에서 예외를 던질 때 g.throw 메서드를 실행합니다.
//  function test
function sleepAndError(g,n){
  setTimeout(function(){
    for(var i=0; i<n; i++) console.log(i); // do something
    g.throw(new Error("오류가 발생했습니다"));
  }, 1000);
}

// callback 함수를 실행하는 함수 : callback 함수가 비동기 처리 중에 발생시킨 예외도 잡아서 처리함
function run(callback, ...argsList){
  var g = (function* (cb,args) {
    try{
      yield cb(g, ...args);
    } catch(e){
      console.log("예외를 잡음 -> " + e);
    }
  })(callback, argsList);
  g.next();
}

// 실행
run(sleepAndError, 10);
  • 실행결과
  • 0 1 2 ... 예외를 잡음 -> Error : 오류가 발생했습니다.
  • sleepAndError 함수의 실행을 run 함수에 위임합니다. sleepAndError 함수는 setTimeout 함수를 사용해서 약 1초 후에 예외를 던지는 비동기 처리를 하는 함수입니다. sleepAndError 함수가 예외를 던질 때 그 예외를 throw문이 아니라 첫 번째 인수로 받은 제너레이터 객체 g의 throw 메서드에 던지는 부분이 포인트입니다. 제너레이터 객체 g 의 throw 메서드는 호출된 그 자리에서 예외를 던지는 것이 아니라 제너레이터를 한 번 동작 시킨 다음에 yield문이 위치한 자리에서 예외를 던집니다. 그러면 이 예외는 제너레이터가 던진 예외가 되므로 catch 블록 안에서 잡아낼 수 있습니다.

반복문에서 빠져나오기

  • 중첩 반복문처럼 깊은 반복문 안에서 탈출할 때에도 예외 매커니즘을 사용할 수 있습니다.
  • 예시로 forEach 문은 실행하는 도중에 취소할 수 없지만 예외 매커니즘을 이용하면 실행 중인 반복문에서 빠져나올 수 있습니다.
var a = [0,1,2,3,4,5,6,7,8,9];
try{
  a.forEach(function(v,i,a){
    if(i>5){
      throw false;
    }
    return a[i] = v*v;
  });
}catch(e){
  if(e) throw e;
}
console.log(a); // -> [0,1,2,3,4,5,6,7,8,9]
  • 의도하지 않은 예외가 try 블록 안에서 발생하는 경우를 대비하여 예외 값이 true로 평가되었을 때는 그 예외를 밖으로 던집니다. 예외 값이 false면 그 다음 코드인 console.log를 실행하게 됩니다.

참고도서 : 모던 자바스크립트 입문

반응형

'Study > JavaScript' 카테고리의 다른 글

JavaScript ) 다차원 배열 생성  (0) 2022.01.27
JavaScript ) 배열 기초  (0) 2022.01.14
JavaScript ) 버그 잡아내기  (0) 2022.01.07
JavaScript ) 자주 사용되는 정규식  (0) 2022.01.07
JavaScript) 단축평가 (1)  (0) 2022.01.06

댓글