스크립트에 오류가 발생하더라도, 스크립트가 죽고 사이트가 멈추는 게 아니라 오류를 다루는 코드로 넘어간 뒤, 다음 코드를 실행하는 것을 말합니다.
이러면 오류가 나더라도 사이트가 멈추는 것을 방지할 수 있습니다.
try문 안에서 에러가 나지 않으면 catch문은 실행되지 않으며, try문 안에서 에러 발생시 catch문으로 바로 넘어갑니다.
try {
// 코드...
} catch (err) {
// 에러 핸들링
} finally {
// 항상 실행
}
에러 발생 시 에러 상세내용이 담긴 객체입니다. catch문에서 매개변수로 접근 가능합니다.
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" 형태의 문자열로 변환됩니다.
console.dir(err); // ReferenceError: lalala is not defined
}
let error = new Error("이상한 일이 발생했습니다. o_O");
alert(error.name); // Error
alert(error.message); // 이상한 일이 발생했습니다. o_O
try안에서 어떤 에러가 나든 다 catch문으로만 가므로 에러를 분기하려면 아래와 같이 합니다.
function readData() {
let json = '{ "age": 30 }';
try {
// ...
blabla(); // 에러!
} catch (e) {
// ...
if (e.name != 'SyntaxError') {
throw e; // 알 수 없는 에러 다시 던지기
}
}
}
try {
readData();
} catch (e) {
alert( "External catch got: " + e ); // 에러를 잡음
}
브라우저의 경우 window.onerror를 이용해 에러를 처리할 수 있습니다. 그러나 전역에서 죽어버린 스크립트 복구가 사실상 불가능하여 그 방식으로는 잘 사용하지 않습니다.
window.onerror = function(message, url, line, col, error) {
// ...
};
보다 명확한 에러를 주기 위해서 직접 만들 수 있습니다.
에러 객체를 상속받아 만듭니다.
에러 객체를 상속받으면 instanceof를 사용할 수 있고, 에러를 다시 던지는 것도 가능합니다.
class ValidationError extends Error {
constructor(message) {
super(message);
this.name = "ValidationError";
}
}
// 사용법
function readUser(json) {
let user = JSON.parse(json);
if (!user.age) {
throw new ValidationError("No field: age");
}
if (!user.name) {
throw new ValidationError("No field: name");
}
return user;
}
// try..catch와 readUser를 함께 사용한 예시
try {
let user = readUser('{ "age": 25 }');
} catch (err) {
if (err instanceof ValidationError) {
alert("Invalid data: " + err.message); // Invalid data: No field: name
} else if (err instanceof SyntaxError) { // (*)
alert("JSON Syntax Error: " + err.message);
} else {
throw err; // 알려지지 않은 에러는 재던지기 합니다. (**)
}
}
class ValidationError extends Error {
constructor(message) {
super(message);
this.name = "ValidationError";
}
}
class PropertyRequiredError extends ValidationError {
constructor(property) {
super("No property: " + property);
this.name = this.constructor.name; // class이름으로 지정됨.
this.property = property;
}
}
// 사용법
function readUser(json) {
let user = JSON.parse(json);
if (!user.age) {
throw new PropertyRequiredError("age");
}
if (!user.name) {
throw new PropertyRequiredError("name");
}
return user;
}
// try..catch와 readUser를 함께 사용하면 다음과 같습니다.
try {
let user = readUser('{ "age": 25 }');
} catch (err) {
if (err instanceof ValidationError) {
alert("Invalid data: " + err.message); // Invalid data: No property: name
alert(err.name); // PropertyRequiredError
alert(err.property); // name
} else if (err instanceof SyntaxError) {
alert("JSON Syntax Error: " + err.message);
} else {
throw err; // 알려지지 않은 에러는 재던지기 합니다.
}
}
에러가 나는 것을 감지하는 대표 에러클래스를 하나 만들고, 각각의 에러를 보여주는 형식을 써서 외부 코드에서는 여러 에러를 분기처리할 필요가 없어 깔끔하게 작성할 수 있습니다.
class ReadError extends Error {
constructor(message, cause) {
super(message);
this.cause = cause;
this.name = 'ReadError';
}
}
class ValidationError extends Error { /*...*/ }
class PropertyRequiredError extends ValidationError { /* ... */ }
function validateUser(user) {
if (!user.age) {
throw new PropertyRequiredError("age");
}
if (!user.name) {
throw new PropertyRequiredError("name");
}
}
function readUser(json) {
let user;
try {
user = JSON.parse(json);
} catch (err) {
if (err instanceof SyntaxError) {
throw new ReadError("Syntax Error", err);
} else {
throw err;
}
}
try {
validateUser(user);
} catch (err) {
if (err instanceof ValidationError) {
throw new ReadError("Validation Error", err);
} else {
throw err;
}
}
}
try {
readUser('{잘못된 형식의 json}');
} catch (e) {
if (e instanceof ReadError) {
alert(e);
// Original error: SyntaxError: Unexpected token b in JSON at position 1
alert("Original error: " + e.cause);
} else {
throw e;
}
}