커스텀 에러 클래스 (Custom Error Classes)
자바스크립트에서 에러 처리는 기본 Error
객체로 충분할 때도 있지만, 상황에 따라 더 구체적인 에러를 정의하고 싶을 때가 있다. 이때 커스텀 에러 클래스를 만들면 흐름을 더 명확하게 제어할 수 있다. 이번에는 커스텀 에러 클래스를 어떻게 만들고 활용하는지, 기본부터 심화까지 코드와 함께 자세히 풀어보려고 한다.
커스텀 에러를 잘 다루면 코드가 더 읽기 쉽고 안정적으로 변한다. 하나씩 단계별로 살펴보자.
기본 에러 클래스와의 차이
자바스크립트는 기본적으로 Error
객체를 제공한다. 근데 이건 단순히 메시지만 담고 있어서 구체적인 상황을 표현하기엔 좀 부족하다. 기본 사용법부터 보자:
try {
throw new Error("뭔가 잘못됐다");
} catch (error) {
console.log(error.name + ": " + error.message);
} // "Error: 뭔가 잘못됐다"
기본 Error
는 이름이 "Error"로 고정돼 있고, 메시지만 커스터마이징할 수 있다. 이걸 확장해서 더 유용하게 만들어보자.
1. 간단한 커스텀 에러 클래스 만들기
Error
를 상속받아서 커스텀 에러 클래스를 만들면 된다. 간단한 예시를 보자:
class ValidationError extends Error {
constructor(message) {
super(message);
this.name = "ValidationError";
}
}
try {
throw new ValidationError("입력값이 잘못됐다");
} catch (error) {
console.log(error.name + ": " + error.message);
} // "ValidationError: 입력값이 잘못됐다"
extends Error
로 상속받고, super
로 메시지를 전달한다. name
을 바꿔서 에러의 종류를 구체적으로 나타냈다.
2. 추가 속성 넣기
커스텀 에러에 속성을 추가하면 더 많은 정보를 담을 수 있다:
class AuthError extends Error {
constructor(message, code) {
super(message);
this.name = "AuthError";
this.code = code;
}
}
try {
throw new AuthError("로그인 실패", 401);
} catch (error) {
console.log(error.name + ": " + error.message + ", 코드: " + error.code);
} // "AuthError: 로그인 실패, 코드: 401"
code
속성을 추가해서 에러의 세부 정보를 담았다. 이런 식으로 에러에 맥락을 더할 수 있다.
3. 에러 타입에 따라 분기하기
커스텀 에러를 쓰면 catch
에서 에러 타입을 구분해서 다룰 수 있다:
class InputError extends Error {
constructor(message) {
super(message);
this.name = "InputError";
}
}
class NetworkError extends Error {
constructor(message) {
super(message);
this.name = "NetworkError";
}
}
function process(value) {
if (!value) {
throw new InputError("값이 없다");
}
if (value === "network") {
throw new NetworkError("네트워크 문제");
}
console.log("처리 완료: " + value);
}
try {
process("");
} catch (error) {
if (error instanceof InputError) {
console.log("입력 문제: " + error.message);
} else if (error instanceof NetworkError) {
console.log("네트워크 문제: " + error.message);
}
} // "입력 문제: 값이 없다"
instanceof
로 에러 타입을 체크해서 상황에 맞게 처리했다. 흐름을 세밀하게 조정할 때 유용하다.
4. 비동기에서 커스텀 에러 활용
비동기 코드에서도 커스텀 에러를 잘 쓸 수 있다:
class FetchError extends Error {
constructor(message, status) {
super(message);
this.name = "FetchError";
this.status = status;
}
}
async function getData() {
try {
const response = await fetch("https://invalid.url");
if (!response.ok) {
throw new FetchError("데이터 가져오기 실패", response.status);
}
console.log("데이터 가져오기 성공");
} catch (error) {
if (error instanceof FetchError) {
console.log(error.name + ": " + error.message + ", 상태: " + error.status);
} else {
console.log("알 수 없는 에러: " + error.message);
}
}
}
getData(); // "FetchError: 데이터 가져오기 실패, 상태: undefined" (URL이 잘못된 경우)
비동기 작업에서 에러를 구체적으로 정의해서 처리했다. status
같은 속성을 추가해서 더 많은 정보를 전달할 수 있다.
5. 계층적인 에러 구조 만들기
에러를 계층적으로 설계하면 복잡한 상황도 체계적으로 다룰 수 있다:
class AppError extends Error {
constructor(message) {
super(message);
this.name = "AppError";
}
}
class DatabaseError extends AppError {
constructor(message, query) {
super(message);
this.name = "DatabaseError";
this.query = query;
}
}
try {
throw new DatabaseError("쿼리 실패", "SELECT * FROM users");
} catch (error) {
if (error instanceof DatabaseError) {
console.log(error.name + ": " + error.message + ", 쿼리: " + error.query);
} else if (error instanceof AppError) {
console.log("앱 에러: " + error.message);
}
} // "DatabaseError: 쿼리 실패, 쿼리: SELECT * FROM users"
AppError
를 부모로 두고 DatabaseError
를 자식으로 만들어 계층을 나눴다. 이렇게 하면 에러를 체계적으로 분류할 수 있다.
6. 커스텀 에러로 흐름 제어하기
커스텀 에러를 활용하면 복잡한 로직의 흐름도 깔끔하게 정리된다:
class UserError extends Error {
constructor(message, field) {
super(message);
this.name = "UserError";
this.field = field;
}
}
function validateUser(user) {
if (!user.name) {
throw new UserError("이름이 없다", "name");
}
if (user.age < 18) {
throw new UserError("미성년자다", "age");
}
console.log(`유저 확인: ${user.name}, ${user.age}세`);
}
try {
validateUser({ name: "철수", age: 15 });
} catch (error) {
console.log(error.name + ": " + error.message + ", 문제 필드: " + error.field);
} // "UserError: 미성년자다, 문제 필드: age"
field
속성을 추가해서 어떤 부분에서 문제가 생겼는지 명확히 했다. 복잡한 검증 로직을 간결하게 처리할 수 있다.
7. 커스텀 에러와 함께 디버깅 정보 제공
에러에 디버깅 정보를 추가하면 문제를 추적하기 쉬워진다:
class ProcessingError extends Error {
constructor(message, data) {
super(message);
this.name = "ProcessingError";
this.data = data;
this.timestamp = new Date();
}
}
try {
const input = { value: null };
if (!input.value) {
throw new ProcessingError("값이 유효하지 않다", input);
}
} catch (error) {
console.log(error.name + ": " + error.message);
console.log("데이터: ", error.data);
console.log("시간: ", error.timestamp);
}
// "ProcessingError: 값이 유효하지 않다"
// "데이터: { value: null }"
// "시간: (현재 날짜와 시간)"
data
와 timestamp
를 추가해서 에러 발생 시점과 원인을 추적할 수 있게 했다.
8. 성능과 가독성에 미치는 영향
커스텀 에러 클래스가 성능과 가독성에 어떤 영향을 주는지 보자:
- 성능: 기본 Error
와 큰 차이는 없지만, 객체 생성 오버헤드가 조금 더 생길 수 있다. 그래도 안정성과 디버깅 이점이 크다.
- 가독성: 에러를 명확히 구분하고 추가 정보를 담으면 코드가 훨씬 읽기 쉬워진다.
커스텀 속성으로 맥락을 추가하고 계층적 설계로 체계성을 높이는 점이 커스텀 에러의 강점이다.
마무리
커스텀 에러 클래스는 기본 에러를 넘어 더 구체적이고 유연한 에러 처리를 가능하게 한다. 속성 추가, 계층 설계, 비동기 활용 등 다양한 방법으로 흐름을 제어하면 코드가 더 명확해진다.
'코딩 공부 > 자바스크립트' 카테고리의 다른 글
59. 자바스크립트 비동기 에러 처리 (Asynchronous Error Handling) (1) | 2025.03.19 |
---|---|
58. 자바스크립트 에러 핸들링 전략 (Error Handling Strategies) (1) | 2025.03.19 |
56. 자바스크립트 제어 흐름 예외 처리 (Control Flow with Exceptions) (0) | 2025.03.18 |
55. 자바스크립트 break와 continue 심화 (Break and Continue Deep Dive) (1) | 2025.03.18 |
54. 자바스크립트 스위치 문 활용 (Advanced Switch Statement Usage) (3) | 2025.03.18 |