JavaScript(10) 에러 핸들링 try...catch

김재홍·2022년 6월 29일
0

아무리 프로그래밍에 능한 사람이더라도 에러가 있는 스크립트를 작성할 수 있다.
원인은 실수, 예상치 못한 사용자 입력, 잘못된 서버 응답 등 다양할 수 있다.

에러가 발생하면 스크립트는 중지되고, 에러가 출력되는데

try...catch 문법을 사용하면 스크립트가 중지되는 것을 방지하고, 에러를 catch하여
다른 동작을 수행할 수 있다.

try {
  try_statements // 실행될 선언들
}
[catch (exception_var ) { 
  catch_statements //try 블록에서 예외가 발생했을 때 실행될 선언들
}]
[finally {
  finally_statements //try 선언이 완료된 이후에 실행된 선언들. 이 선언들은 예외 발생 여부와 상관없이 실행된다.

}]

여기서 exception_var는
catch 블록과 관련된 예외 객체를 담기 위한 식별자

설명

try 선언의 구성은 하나 혹은 그 이상의 선언을 포함한 try 블록 및 catch 항목이나 finally 항목 중 최소한 하나 혹은 둘 다 포함하여 이루어진다. 즉, try 선언에는 세 가지 형식이 존재한다.

try...catch
try...finally
try...catch...finally

catch 블록은 try 블록 안에서 예외가 발생(throw)하는 경우 무엇을 할지 명시하는 코드를 포함합니다. try 블록 (또는 try 블록 내에서 호출된 함수) 내의 명령문이 예외를 throw 하면 제어가 catch 블록으로 이동합니다. try 블록에 예외가 발생하지 않으면 catch 블록을 건너뜁니다.

finally 블록은 try 블록과 catch 블록(들)이 실행을 마친 후 항상 실행됩니다. 예외가 발생했는지에 관계없이 항상 실행됩니다.

하나 이상의 try 문을 중첩 할 수 있습니다. 내부의 try 문에 catch 블록이 없으면, 둘러싼 try 문의 catch 블록이 입력됩니다.

무조건적 catch문

try-block 내에서 예외가 발생하면 catch-block이 실행된다.

try {
   throw "myException"; // generates an exception
}
catch (e) {
   // statements to handle any exceptions
   logMyErrors(e); // pass exception object to error handler
}

조건적 catch문

다음과 같이 try...catch 블록을 if...else if...else 구조와 결합하여 '조건부 catch-blocks'를 만들 수 있다.

try {
  myroutine(); // may throw three types of exceptions
} catch (e) {
  if (e instanceof TypeError) {
    // statements to handle TypeError exceptions
  } else if (e instanceof RangeError) {
    // statements to handle RangeError exceptions
  } else if (e instanceof EvalError) {
    // statements to handle EvalError exceptions
  } else {
    // statements to handle any unspecified exceptions
    logMyErrors(e); // pass exception object to error handler
  }
}

그러나 일반적인 사용 사례는 예상 오류의 작은 하위 집합 만 포착 (및 침묵) 한 다음 다른 경우에 오류를 다시 발생시키는 것입니다.

try {
  myRoutine();
} catch (e) {
  if (e instanceof RangeError) {
    // statements to handle this very common expected error
  } else {
    throw e;  // re-throw the error unchanged
  }
}

try...catch 특징

- 런타임에러(runtime error)에만 동작한다.

실행가능한 코드에만 동작한다.
실행 가능한 코드는 유효한 자바스크립트 코드를 의미한다.
중괄호 짝이 안 맞는 것처럼 코드가 문법적으로 잘못된 경우엔 try..catch가 동작하지 않습니다.

try {
  {{{{{{{{{{{{
} catch(e) {
  alert("유효하지 않은 코드이기 때문에, 자바스크립트 엔진은 이 코드를 이해할 수 없습니다.");
}

자바스크립트 엔진은 코드를 읽고 난 후 코드를 실행합니다.
코드를 읽는 중에 발생하는 에러는 'parse-time 에러’라고 부르는데,
엔진은 이 코드를 이해할 수 없기 때문에 parse-time 에러는 코드 안에서 복구가 불가능합니다.
try..catch는 유효한 코드에서 발생하는 에러만 처리할 수 있습니다. 이런 에러를 ‘런타임 에러(runtime error)’ 혹은 '예외(exception)'라고 부릅니다.

- 동기적으로 동작한다.

setTimeout처럼 ‘스케줄 된(scheduled)’ 코드에서 발생한 예외는 try..catch에서 잡아낼 수 없습니다.

try {
  setTimeout(function() {
    noSuchVariable; // 스크립트는 여기서 죽습니다.
  }, 1000);
} catch (e) {
  alert( "작동 멈춤" );
}

setTimeout에 넘겨진 익명 함수는 엔진이 try..catch를 떠난 다음에서야 실행되기 때문입니다.
스케줄 된 함수 내부의 예외를 잡으려면, try..catch를 반드시 함수 내부에 구현해야 합니다.

setTimeout(function() {
  try {
    noSuchVariable; // 이제 try..catch에서 에러를 핸들링 할 수 있습니다!
  } catch {
    alert( "에러를 잡았습니다!" );
  }
}, 1000);

- 에러 객체

에러가 발생하면 자바스크립트는 에러 상세내용이 담긴 객체를 생성하고, 그 후 catch 블록에 이 객체를 인수로 전달한다.

try {
  // ...
} catch(err) { // <-- '에러 객체', err 대신 다른 이름으로도 쓸 수 있음
  // ...
}

내장 에러 전체와 에러 객체는 두 가지 주요 프로퍼티를 가집니다.

name
에러 이름. 정의되지 않은 변수 때문에 발생한 에러라면 "ReferenceError"가 이름이 됩니다.
message
에러 상세 내용을 담고 있는 문자 메시지
표준은 아니지만, name과 message 이외에 대부분의 호스트 환경에서 지원하는 프로퍼티도 있습니다. stack은 가장 널리 사용되는 비표준 프로퍼티 중 하나입니다.

stack
현재 호출 스택. 에러를 유발한 중첩 호출들의 순서 정보를 가진 문자열로 디버깅 목적으로 사용됩니다.

try {
  lalala; // 에러, 변수가 정의되지 않음!
} catch(err) {
  alert(err.name); // ReferenceError
  alert(err.message); // lalala is not defined
  alert(err.stack); // ReferenceError: lalala is not defined at ... (호출 스택)

  // 에러 전체를 보여줄 수도 있습니다.
  // 이때, 에러 객체는 "name: message" 형태의 문자열로 변환됩니다.
  alert(err); // ReferenceError: lalala is not defined
}

‘try…catch’ 사용하기

try..catch가 실무에서 어떻게 사용되는지 알아봅시다.
앞서 JSON으로 인코딩된 값을 읽을 수 있도록 해주는 JSON.parse(str) 메서드에 대해 배운 바 있습니다.
이 메서드는 주로 서버 등에서 네트워크를 통해 전달받은 데이터를 디코딩하는 데 사용합니다.
전달받은 데이터에 JSON.parse를 호출하는 식으로 사용되죠.

잘못된 형식의 json이 들어온 경우, JSON.parse는 에러를 만들기 때문에 스크립트가 ‘죽습니다’.
스크립트가 죽는 것에 만족해야 할까요? 당연히 아니죠!
서버에서 전달받은 데이터가 잘못되어 스크립트가 죽는 경우, 사용자는 개발자 콘솔을 열지 않는 이상 절대 원인을 알 수 없습니다. 그런데 사람들은 메시지 등을 통해 에러의 원인을 알지 못한 채 무언가가 '그냥 죽는 것’을 정말 싫어합니다.
try..catch를 사용해 이를 처리해 봅시다.

let json = "{ bad json }";

try {

  let user = JSON.parse(json); // <-- 여기서 에러가 발생하므로
  alert( user.name ); // 이 코드는 동작하지 않습니다.

} catch (e) {
  // 에러가 발생하면 제어 흐름이 catch 문으로 넘어옵니다.
  alert( "데이터에 에러가 있어 재요청을 시도합니다." );
  alert( e.name );
  alert( e.message );
}

위 예시에선 에러가 발생했다는 걸 보여주기 위해 간단히 예외처리했지만, catch 블록 안에서 새로운 네트워크 요청 보내기, 사용자에게 대안 제안하기, 로깅 장치에 에러 정보 보내기 등과 같은 구체적인 일을 할 수 있습니다. 스크립트가 죽도록 놔두는 것보다 훨씬 나은 대응이죠.

직접 에러를 만들어서 던지기

‘throw’ 연산자
throw 연산자는 에러를 생성합니다.

문법은 다음과 같습니다.

throw <error object>

이론적으로는 숫자, 문자열 같은 원시형 자료를 포함한 어떤 것이든 에러 객체(error object)로 사용할 수 있습니다. 하지만 내장 에러와의 호환을 위해 되도록 에러 객체에 name과 message 프로퍼티를 넣어주는 것을 권장합니다.

자바스크립트는 Error, SyntaxError, ReferenceError, TypeError등의 표준 에러 객체 관련 생성자를 지원합니다. 이 생성자들을 이용해 에러 객체를 만들 수도 있습니다.

아래와 같이 말이죠.

let error = new Error(message);
// or
let error = new SyntaxError(message);
let error = new ReferenceError(message);
// ...

참고
https://ko.javascript.info/try-catch
https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Statements/try...catch

0개의 댓글

관련 채용 정보