- 예외처리를 하는 이유
- 예외처리를 하지 않으면 에러가 나는 순간 프로그램 실행이 중단이 되지만,
예외처리를 만들어주면 중간에 어느 구간에서 어떤 에러가 발생했는지 표시가 가능하고 나머지 코드를 실행시킬 수 있기 때문.
고전적 예외처리
개발자가 원하지 않는 상황을 예외시키기 위해 if문으로 대체 -> 예외처리
/** 조건문을 사용한 고전적 예외처리 */
// 프로그램이 실행되기에 적합하지 않은 파라미터가 전달된 경우 미리 약속된 값을 리턴
// -> 프로그램의 성공 실패 여부를 리터값으로 판단하는 경우
function foo(x, y) {
if (x < 0 && y < 0) {
return 0;
}
return x + y;
}
// 정상호출 상황
console.log(foo(10, 20));
// 비정상 호출 상황
console.log(foo(-1, -2));
// 비정상 상황에 대한 고전적 처리 방법
const k = foo(-1, -2);
// 에러 상황에 대한 대응(메시지 처리)을 함수를 호출하는 곳에서 해야 한다.
if (k == 0) {
console.log("x와 y가 0보다 작습니다.");
} else {
console.log(k);
}
모듈을 이용했을때 추가적으로 에러 대응을 입력해줘야 하는 단점이 생김.
try,catch
[에러의 종류]
const data = [1, 2, 3];
console.log("배열 탐색 시작");
// 먼저, try {...} 안의 코드가 실행된다.
// 에러가 없다면, try 안의 마지막 줄까지 실행되고, catch 블록은 건너뛴다.
// 에러가 있다면, try 안 코드의 실행이 중단되고, catch(err) 블록으로 제어 흐름이 넘어간다.
// 변수 err(아무 이름이나 사용 가능)는 무슨 일이 일어났는지에 대한 설명이 담긴 에러 객체를 포함한다.
try {
for (let i = 0; i < 10; i++) {
console.log(data[i].toFixed(2));
}
console.log("try 블록 실행 완료~!!!");
} catch (err) {
console.group("%s 에러발생", err.name);
console.error(err.message);
console.groupEnd();
// 에러정보 전체
// console.error(err);
} finally {
// 에러의 발생 여부에 상관 없이 무조건 맨 마지막에 실행되는 블록.
// 필요하지 않은 경우 생략할 수 있다.
console.log("배열 탐색이 종료되었습니다.");
}
console.log("프로그램 종료");
에러객체
개발자가 직접 정의하는 에러
// 에러 객체를 생성
// 생성자 파라미터로 에러의 내용 전달
let err = new Error("이상한 일이 벌어졌습니다.");
console.log("에러이름: %s", err.name);
console.log("에러이름: %s", err.message);
// 개발자가 직접 에러를 발생시킬 수 있다.
// --> 이 구문을 실제 에러로 인식하기 때문에 프로그램이 이 위치에서 중단된다.
throw err;
console.log("프로그램 종료");
개발자가 발생시키는 에러에 대한 예외처리
let err = new Error("이상한 일이 벌어졌습니다.");
try {
// throw구문은 그 자체를 에러로 인식하기 때문에 try-catch 처리가 가능하다.
throw err;
} catch (err) {
console.log("에러이름: %s", err.name);
console.log("에러이름: %s", err.message);
}
// 에러 상황을 try-catch로 처리했으므로 프로그램이 중단되지 않고 무사히 종료할 수 있다.
console.log("프로그램 종료");
예외 객체를 통한 예외처리
try블록 안의 코드는 최소화 하는 것이 프로그램 효율에 좋다.
그래서 k값을 정상적으로 리턴 받았다면 그 결과값을 활용하는 처리는 try블록 밖에서 하는것이 좋다.
에러 점검이 끝난 후 try-catch 블록 밖에서 k값을 활용하려면
변수의 선언 위치가 try블록보다 상위에 위치해야 한다. --> 변수의 스코프 규칙
function foo(x, y) {
if (x < 0) {
// 함수 안에서 에러를 강제로 발생시킬 경우, 이 함수를 호출하는 위치를 에러로 인식한다.
throw new Error("x가 0보다 작습니다.");
}
if (y < 0) {
// 함수 안에서 에러를 강제로 발생시킬 경우, 이 함수를 호출하는 위치를 에러로 인식한다.
throw new Error("y가 0보다 작습니다.");
}
return x + y;
}
// try블록 안의 코드는 최소화 하는 것이 프로그램 효율에 좋다.
// 그래서 k값을 정상적으로 리턴 받았다면 그 결과값을 활용하는 처리는 try블록 밖에서 하는것이 좋다.
// 에러 점검이 끝난 후 try-catch 블록 밖에서 k값을 활용하려면
// 변수의 선언 위치가 try블록보다 상위에 위치해야 한다. --> 변수의 스코프 규칙
const a = null;
const b = null;
try {
a = foo(-1, 10);
} catch (err) {
// 이 블록으로 전달되는 err객체는 5라인에서 생성한 Error 클래스의 객체이다.
console.log("에러이름: %s", err.name);
console.log("에러이름: %s", err.message);
}
try {
b = foo(10, -1);
} catch (err) {
// 이 블록으로 전달되는 err객체는 10라인에서 생성한 Error 클래스의 객체이다.
console.log("에러이름: %s", err.name);
console.log("에러이름: %s", err.message);
}
// 코드는 좌우중에서 우변을 먼저 실행하는데 우변에서 에러가 났으므로 변수에 값이 할당되지 않음.
console.log(a); // null
console.log(b); // null
사용자 정의 에러
에러의 종류를 세분화 하기 위해 기본 Error 클래스의 기능을 확장하여 개발자가 직접 에러에 대한 경우의 수를 정의할 수 있다.
class XlessError extends Error {
// 자식 클래스가 생성자를 갖을 경우 부모의 생성자를 반드시 강제호출해야 한다. --> super(...)
constructor(msg) {
super(msg);
super.name = "XlessError";
}
}
class YlessError extends Error {
// 자식 클래스가 생성자를 갖을 경우 부모의 생성자를 반드시 강제호출해야 한다. --> super(...)
constructor(msg) {
super(msg);
super.name = "YlessError";
}
}
function foo(x, y) {
if (x < 0) {
// 함수 안에서 에러를 강제로 발생시킬 경우, 이 함수를 호출하는 위치를 에러로 인식한다.
throw new XlessError("x가 0보다 작습니다.");
}
if (y < 0) {
// 함수 안에서 에러를 강제로 발생시킬 경우, 이 함수를 호출하는 위치를 에러로 인식한다.
throw new YlessError("y가 0보다 작습니다.");
}
return x + y;
}
const a = null;
const b = null;
try {
a = foo(-1, 10);
} catch (err) {
console.log("에러이름: %s", err.name);
console.log("에러내용: %s", err.message);
console.log(err);
}
try {
b = foo(10, -1);
} catch (err) {
console.log("에러이름: %s", err.name);
console.log("에러내용: %s", err.message);
console.log(err);
}
console.log(a);
console.log(b);
helper