프로젝트를 진행하다보면 정말 다양한 예외 처리를 해줘야한다.
여기서 예외는 입력값에 대한 검증일 수도 있고, 누락 여부의 확인일 수도 있고,
개발자의 문법 에러일 수도 있다.
보통new Error()
를 사용하여 에러를throw
하는 경우가 많다.
그런데...마냥new Error()
만 쓰자니 어떤 에러인지 구분이 잘 안된다.
내가 직접 이 에러는 이런 에러다~ 라고 명시할 수는 없을까?
이번에는 에러를 더 자세하게 분류할 수 있는 커스텀 에러에 대해서 알아보자.
└── src
├── App.js
├── BridgeGame.js
├── BridgeMaker.js
├── BridgeRandomNumberGenerator.js
├── Game.js
├── Player.js
├── constant
│ └── Constants.js
├── error
│ ├── CommandError.js
│ ├── MovingError.js
│ ├── SizeError.js
│ └── ValidationError.js
├── validation
│ ├── CommandValidation.js
│ ├── MovingValidation.js
│ └── SizeValidation.js
└── view
├── InputView.js
└── OutputView.js
여기서 다룰 것은 error
, validation
폴더이다.
// error/ValidationError.js
class ValidationError {
constructor(message) {
this.message = message;
}
}
module.exports = ValidationError;
어떤 에러인지를 알려줄 최상위 클래스이다.
이 클래스를 상속하는 것으로 상속받은 클래스에서 발생하는 에러가 validation
에 관한 에러라는 것을 알 수 있다.
이 부분에 대해서는 더 자세하게 명시할 수 있는데, 이는 게시글 하단 참고 자료를 보면 더 자세하게 알 수 있다.
필자는 에러 표시를 사용자에게 직접 보여줘야하기 때문에 이 부분은 생략했다.
가령 예를들어, validation
에 관한 에러가 아니라 문법에 관한 에러를 나타내고 싶다면,
SyntaxError
등으로 생성해볼 수 있겠다.
// error/SizeError.js
const ValidationError = require('./ValidationError');
class SizeError extends ValidationError {
constructor(message) {
super(message);
this.name = 'SizeError';
}
}
module.exports = SizeError;
ValidationError
를 상속하는 것으로 이 에러 클래스가 의미하는게 입력값 검증이라는 것을 알 수 있다.
클래스명만 봐도 size
라는 입력값에 대한 validation
에러라는걸 알 수 있다.
super
를 사용하여 ValidationError
에 에러 메세지를 전달했다.
// validation/SizeValidation.js
const SizeError = require('../error/SizeError');
const { ERROR, GAME } = require('../constant/Constants');
class SizeValidation {
#size;
constructor(size) {
this.#size = size;
}
checkError() {
this.checkRange();
this.checkStartWithZero();
this.checkOnlyNumber();
}
checkRange() {
const sizeNumber = Number(this.#size);
if (sizeNumber < GAME.MINIMUM_RANGE || sizeNumber > GAME.MAXIMUM_RANGE) {
throw new SizeError(ERROR.OUT_OF_RANGE);
}
}
checkStartWithZero() {
if (this.#size[0] === GAME.STRING_ZERO) {
throw new SizeError(ERROR.START_WITH_ZERO);
}
}
checkOnlyNumber() {
const regex = /^\d+$/g;
if (!regex.test(this.#size)) {
throw new SizeError(ERROR.NOT_ONLY_NUMBER);
}
}
}
module.exports = SizeValidation;
size
입력값에 대한 예외 처리를 담당하는 클래스이다.
특정 조건에서 예외가 발생하면 new Error()
대신 new SizeError()
를 던진다.
코드 상을 어떤 에러인지 명확하게 알 수 있게되었다.
new SizeError()
에 들어가는 에러 메세지는 SizeError
에서 super
를 통해 ValidationError
까지 전달된다.
일부러 예외 상황을 발생시켜보자.
message
에는 상수화하여 new SizeError()
에 넣어줬던 문자열이 출력된다.
name
은 SizeError
의 constructor
에서 생성했던 this.name
이 출력된다.
사실, SizeError
를 던졌기 때문에 당연하게도 생성자에 들어있는 값들이 출력되는 것이다.
아무튼, 중요한 것은 어떤 에러가 발생했는지 이름을 통해 더 세세하게 구분할 수 있다는 것이다.
Game.js
에서 입력값에 대한 SizeValidation
을 실행한다.
SizeValidation
에서는 SizeError
에 어떤 예외 상황인지를 전달한다.
SizeError
는 ValidationError
를 상속받아, 유효값 검증에 대한 에러라는 것을 알 수 있다.
에러 상황은 다양할 수 있다. 따라서 ValidationError
외에도 다른 에러 클래스를 생성할 수 있다.
마찬가지로 size
라는 입력값에 대한 예외 처리는 다양한 상황이 있을 수 있다.
여기서는 SizeError
로 묶여있지만, PropertyError
등의 이름으로 더 다양한 상황에 적용 가능한 클래스를 만들 수도 있겠다.
그렇게되면 이런 식의 참조 구조가 될 것으로 예상된다.
SizeValidation -> sizeError -> PropertyError -> ValidationError
검증에 대한 에러이며, 프로퍼티 검증 중 발생한 에러이며, 그 프로퍼티는 size
이다.
이런 식으로 이해할 수 있겠다.