에러가 있는 코드는 배제하고 싶지만, 아무리 완벽하게 짜여진 코드라고 하더라도 예외의 경우는 있는 법이다.
그렇다면 예외를 처리하려면 어떻게 해야할까? 예외의 경우에도 스크립트가 죽지 않도록 하려면 해결책을 만들어야한다.
그 방법으로 try...catch
문을 활용해 코드가 중단되는 것을 방지하고, 에러에 대한 예외 처리 등을 작성하면 안정적인 코드를 짤 수 있다.
말 그대로 try
시도해보고 에러가 생기면 catch
잡는 문법이다.
그렇기 때문에 try
와 catch
라는 두개의 주요 블록으로 구성된다.
try {
// 실행될 코드 (에러가 발생할 것으로 예상되는 코드)
} catch (error) {
// 에러 발생 시 실행할 코드
}
try
문 안에 코드가 실행된다.try
문 안의 마지막 줄까지 실행되고, catch
문을 건너뛰게 된다.try
문 안의 코드의 실행이 중단되고, catch
문으로 제어 흐름이 넘어가며 변수 (error)
는 무슨 일이 일어났는지에 대한 정보가 담긴 에러 객체를 포함한다. (변수명은 꼭 error가 아니여도 된다.)이렇게 try
문 안에서 에러가 발생하더라도 catch
문으로 흐름이 넘어가며 에러를 처리하기 때문에 실행중인 코드가 중단되지 않는다.
그렇다면 try...catch
문은 모든 에러를 처리할까?
그럼 만능의 코드이니 모든 코드에 쓰면 되겠지만... 아니다.
try...catch
는 몇가지 예외가 있다.
try...catch
는 실행 가능한 코드에서만 동작한다. 문법적으로 유효한 자바스크립트에서만 동작한다는 뜻이다.
try {
// parse-time error
{/=234..
} catch (error) {
alert("유효하지 않은 코드");
}
자바스크립트 엔진은 코드를 모두 한번에 읽고 난 뒤 코드를 실행시킨다. 코드를 읽는 도중 발생한 에러는 parse-time error
라고 부르는데, 자바스크립트는 이 코드를 이해할 수 없기 때문에 parse-time error
는 코드 안에서 복구가 불가능하다. 애초에 코드가 틀린거다.
try...catch
는 유효한 코드에서 발생하는 에러만 처리한다. 이런 에러는 런타임 에러(Runtime error) 혹은 예외(exception)라고 부른다.
setTimeout
, Promise
와 같은 비동기적인 코드에서 발생한 에러는 try...catch
에서 잡아낼 수 없다.
try {
setTimeout(() => {
noSuchVariable; //스크립트 죽음
}, 1000);
} catch (error) {
console.error('에러 발생');
}
setTimeout
에 넘겨진 함수는 엔진이 try...catch
문을 벗어난 뒤에 실행되기 때문에 비동기적으로 동작하는 코드 내부에 try...catch
문을 구현해야 한다.
// 에러 발생
setTimeout(() => {
try {
noSuchVariable;
} catch (error) {
console.error('에러 발생');
}
}, 1000);
try
문에서 에러가 발생하면 자바스크립트는 에러에 대한 데이터를 담은 객체를 생성한다. 그리고 catch
문에 이 객체를 인수로 전달하게 된다.
try {
// 에러가 발생할 수 있는 코드
} catch (err) {
// 에러 발생 시 실행할 코드
}
내장 에러 전체와 에러 객체는 두가지 주요 프로퍼티를 가진다.
err.name
: 에러의 이름, 정의되지 않은 변수 때문에 발생한 에러라면 ReferenceError
가 이름이 된다.
err.message
: 에러의 상세 내용을 담은 메세지
표준은 아니지만 대부분의 호스트 환경에서 지원하는 프로퍼티도 있다.
err.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
}
에러에 대한 자세한 정보가 필요하지 않다면
catch
에서 이를 생략 할 수 있다.try { // 에러가 발생할 수 있는 코드 } catch { // 에러 발생 시 실행할 코드 }
const json = '{ "age": "24" }';
try {
const user = JSON.parse(json); //전달받은 문자열을 자바스크립트 객체로 변환
console.log(user.name); //error 발생
} catch (error) {
console.error('error');
}
위와 같은 상황은 개발자가 의도한 상황이 아니다. user
이라는 객체에 name
이라는 프로퍼티가 없기 때문에 에러가 발생해야하지만 자바스크립트는 실제로 undefined
를 출력한다.
이럴 때에는 throw
연산자를 사용해 직접 에러를 생성할 수도 있다.
throw <error object>
문법은 다음과 같다. 이론적으로는 숫자, 문자열과 같은 원시형 자료를 포함한 어떤 것이든 에러 객체로 사용 할 수 있지만, 내장 에러와의 호환을 위해 되도록 에러 객체에 name
과 message
프로퍼티를 넣어주는 것을 권장한다.
const error = new Error('message');
const syntaxError = new SyntaxError('message');
const referenceError = new ReferenceError('message');
console.log(referenceError.name); // ReferenceError
console.log(referenceError.message); // 'message'
자바스크립트는 Error
, SyntaxError
, ReferenceError
TypeError
등의 표준 에러 객체 관련 생성자를 제공한다. 이 생성자들을 이용해 위와 같은 에러 객체를 만들 수 있다.
일반 객체가 아닌 내장 생성자를 사용해 만든 에러 객체의 name
프로퍼티는 생성자 이름과 동일한 값을 가지며 인자로 넣어준 문자열은 message
프로퍼티가 갖는다.
throw
연산자를 사용해 user.name
이 없으면 SyntaxError
가 호출되게끔 만들어줬다.
const json = '{ "age": "24" }';
try {
const user = JSON.parse(json);
if (!user.name) {
throw new SyntaxError('해당 프로퍼티가 존재하지 않습니다.');
}
console.log(AvenJS);
} catch (error) {
// 해당 프로퍼티가 존재하지 않습니다.
console.error(error.message);
}
에러 생성 방식은 자바스크립트가 자체적으로 에러를 생성하는 방식과 동일하다. 그래서 에러가 발생했으므로 try
의 실행은 즉시 중단되고 제어 흐름이 catch
로 넘어간 것을 확인 해볼 수 있다.
에러 핸들링은 try...catch
뿐만 아니라 finally
라는 코드 블록을 하나 더 가질 수 있다. finally
는 try
의 실행이 끝난 후나 catch
실행이 종료된 후에 실행된다.
try {
// 실행될 코드
} catch (error) {
// 에러 발생 시 실행할 코드
} finally {
// try블럭 또는 catch블럭 실행 후 무조건 실행
}
try...catch...finally
의 동작 원리는 다음과 같다.
1. try
문의 코드가 실행된다.
2. 에러가 없다면, try
문이 끝까지 실행되고, catch
문을 건너뛰고 finally
문이 실행된다.
3. 에러가 있다면, try
문이 중단되고, catch
문으로 제어 흐름이 넘어간 뒤 변수 error
는 무슨 일이 일어났는지에 대한 정보를 객체로 반환한 뒤 finally
문이 실행된다.
finally
문은 이와 같이 에러가 발생하든, 발생하지 않든 무조건 실행된다.
설령 try
나 catch
에서 return
으로 강제로 블록을 종료 시키려고 해도 finally
는 무조건 실행된다.
try...catch...finally
안의error
변수는 지역 변수이다.