티스토리 뷰

10.2 Custom errors, extending Error

필요에 따라 에러 객체를 커스텀해서 만들 수도 있다. (나도 Java에서는 만들어서 많이 사용을 했다.) 다만, Error 타입 객체를 무조건 상속할 필요는 없다는 점이 차이인데, 그래도 Error 타입 객체를 상속하는 것을 권장한다.

Extending Error

어떤 에러가 존재할 때, 해당 에러에 대한 validation 역할을 해줄 객체가 필요할 것이다. 그렇기 때문에 에러 객체를 커스텀하는 것이 필요하고 그에 맞게 에러를 던질 수 있겠다.

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; // 알려지지 않은 에러는 재던지기 합니다. (**)
  }
}

사실 이러한 방식은 에러 핸들링이라기보다, 객체지향의 영역인 것 같다. 각각의 에러에 맞는 객체를 생성하고 역할을 부여하는 것.

Further inheritance

아래 예시처럼 상속을 좀 더 잘 활용하여서 수동적인 코드는 제거하고 간결한 코드로 바꿀 수도 있겠다.

class ValidationError extends Error {
  constructor(message) {
    super(message);
    this.name = "ValidationError";
  }
}

class PropertyRequiredError extends ValidationError {
  constructor(property) {
    super("No property: " + property);
    this.name = "PropertyRequiredError";
    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 MyError extends Error {
  constructor(message) {
    super(message);
    this.name = this.constructor.name;
  }
}

class ValidationError extends MyError { }

class PropertyRequiredError extends ValidationError {
  constructor(property) {
    super("No property: " + property);
    this.property = property;
  }
}

// name is correct
alert( new PropertyRequiredError("field").name ); // PropertyRequiredError

Wrapping exceptions

에러 감싸기는 아래 예시처럼, 에러의 종류가 계속해서 늘어나는 것에 대비해 에러를 한번 감싸서 간결하게 관리하고자 하는 방식이다. 기존의 VallidationError, SyntaxError와 같은 것들은 readUser 내부에서 처리하게 하고 ReadError로 간결하게 처리하는 것이다.

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;
  }
}

 

댓글
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday