커스텀 예외

공병주(Chris)·2022년 5월 6일
2

Java를 탄탄히

목록 보기
9/9

커스텀예외

우아한테크코스 지하철 미션을 진행하면서 표준 예외가 아닌 예외를 직접 만들어서 진행을 해보았습니다.
실제로 느낀 장점들과 추가적으로 알아본 장점에 대해서 살펴보겠습니다.

명확한 의미

public class DuplicatedLineNameException extends InvalidSubwayResourceException {

    private static final String MESSAGE = "노선 이름 혹은 노선 색이 이미 존재합니다.";

    public DuplicatedLineNameException() {
        super(MESSAGE);
    }
}

위와 같은 예외에 대해서 예외를 컨트롤하는 부분에서 아래와 같이 사용할 수 있습니다.

public doSomething() {
	try {

	} catch (DuplicatedLineNameException exception) {

	}
}

사용자의 입력에 대해 중복이 발생한다면 저는 보통 IllegalArgumentException에 예외메시지를 생성자에 주입하여 사용하였는데요.

예외를 직접 만든다면 예외메시지 없이,
Exception 이름 만으로 명확하게 어떤 상황에서의 예외인지를 파악할 수 있습니다.

또한, 테스트에서도 IllegalArgumentException과 같은 표준 예외들은 범위가 비교적 넓기 때문에 메시지까지 체크해주어야 하는데요.

@DisplayName("추가하려는 역의 이름이 이미 존재하면 예외를 발생시킨다.")
@Test
void createStation_exception() {
    Station station = stationService.save(new Station("서울역"));

    assertThatThrownBy(() -> stationService.save(new Station("서울역")))
            .isInstanceOf(IllegalArgumentException.class)
			.hasMessage("이미 존재하는 역 이름입니다.");
}

커스텀 예외의 경우에는 구체적인 상황을 나타내기 때문에 예외의 타입만 체크해줘도 됩니다.

@DisplayName("추가하려는 역의 이름이 이미 존재하면 예외를 발생시킨다.")
@Test
void createStation_exception() {
    Station station = stationService.save(new Station("서울역"));

    assertThatThrownBy(() -> stationService.save(new Station("서울역")))
            ![](https://velog.velcdn.com/images/byeongju/post/91246c09-a7e1-436b-a7e6-dd8f9bc26b18/image.png)
.isInstanceOf(DuplicatedStationNameException.class);
}

예외에 대한 응집도 증가

  1. 커스텀 예외
public class DuplicatedStationNameException extends InvalidSubwayResourceException {

    private static final String MESSAGE = "이미 존재하는 역 이름입니다.";

    public DuplicatedStationNameException() {
        super(MESSAGE);
    }
}
@Service
public class StationService {

    private final StationDao stationDao;

    public StationService(StationDao stationDao) {
        this.stationDao = stationDao;
    }

    public Station save(Station station) {
        if (stationDao.existsByName(station)) {
            throw new DuplicatedStationNameException();
        }
        return stationDao.save(station);
    }
}
  1. 표준 예외
@Service
public class StationService {

		//private static final String DUPLICATED_STATION_NAME_EXCEPTION_MESSAGE =
            "이미 존재하는 역 이름입니다.";

    private final StationDao stationDao;

    public StationService(StationDao stationDao) {
        this.stationDao = stationDao;
    }

    public Station save(Station station) {
        if (stationDao.existsByName(station)) {
            throw new IllegalArgumentException("이미 존재하는 역 이름입니다.");
        }
        return stationDao.save(station);
    }
}

커스텀 예외와 표준 예외의 코드를 보면, 커스텀 예외가 응집도가 높다는 생각이 듭니다.

예외를 발생시키면서 추가적인 정보를 주어야한다면, DuplicatedStationNameException에서 관리하면 됩니다. StationService는 단순히 어떤 상황에서 어떤 예외를 발생시킬지만 신경쓰면 되는 것이죠.

지금은 사용자에게 알려줄 예외메시지만을 관리하기 때문에 굳이?라고 생각할 수 있지만,
예외메시지에 여러가지 정보가 들어가야 한다면 StationSerive가 복잡해질 것입니다.

예외에 대한 처리는 커스텀 예외 객체에 몰아넣고 StationService는 예외 밖의 로직들에 좀 더 집중할 수 있습니다. 책임이 조금 더 분리되었다고도 할 수 있겠네요.


예외 처리에 대한 명확함

만약 중복된 지하철 역이름노선 이름에 대해 둘다 IllegalArgumentException이 발생한다면, 이들에 대한 처리를 ControllerAdvice에서 아래와 같이 처리해야할 것입니다.

@ExceptionHandler(IllegalArgumentException.class)
public ResponseEntity<ExceptionResponse> handleDuplicatedName(IllegalArgumentException exception) {
    if (exception.getMessage().equals("이미 존재하는 역 이름입니다")) {
        //중복된 지하철 역 이름에 대한 처리
    }
    //중복된 노선 이름에 대한 처리
}

똑같은 타입의 예외가 어디서 발생하였는지 체크를 해주어야 합니다.
(물론 ControllerAdvice를 사용하지않고, 서로 다른 Controller로 나눌 수 있다면 Controller 내부 ExceptionHandler를 사용할 수 있습니다. 하지만, 예외 상황이 많아진다면 해당 controller가 복잡해지겠죠.)

커스텀 예외를 사용하면 아래와 같이 분리할 수 있습니다.

@ExceptionHandler(DuplicatedStationNameException.class)
public ResponseEntity<ExceptionResponse> handleDuplicatedStationName(DuplicatedStationNameException exception) {
    //중복된 지하철 역 이름에 대한 처리
}

@ExceptionHandler(DuplicatedLineNameException.class)
public ResponseEntity<ExceptionResponse> handleDuplicatedLineName(DuplicatedLineNameException exception) {
    // 중복된 노선 이름에 대한 처리
}

단점

지금 생각나는 단점은, 예외가 많아진다면 커스텀 예외가 상당히 많아져 패키지가 복잡해질 것이라고 생각합니다.

커스텀 예외를 처음 사용해보았는데요. 저는 가독성이 훨씬 좋음을 느꼈고, @ControllerAdvice에서 예외 처리도 보다 편안하다는 것을 느꼈습니다. 직접 만들기가 귀찮을 수도 있는데, 한번쯤 사용해보고 직접 느껴보는 것도 괜찮을 것 같습니다!

참고자료

2개의 댓글

comment-user-thumbnail
2022년 5월 6일

크리스 최고최고 👍👍👍👍

1개의 답글