We will find a way, we always have.

-interstellar

Programming Language/자바

[자바] 내가 커스텀 예외를 사용하게된 이유 (IllegalArgumentException, IllegalStateException)

Redddy 2024. 4. 9. 03:19

서론

지금까지 도메인 요구 사항에 맞지 않는 입력이 들어온다면 주로 IllegalArgumentException를 사용하였고 파라미터로 어떤 예외인지 메시지를 담아 의미를 전달하였다.
이녀석이 뭐 특별한 기능을 제공하느냐? 라고 물어본다면 네니오라고 답할 수 있다. 
 
IllegalArgumentException이 제공하는 기능은 의미를 전달하는 것이다. 다시말해 유효하지 않은 인자가 왔다는 의미를 전달하는데 요게 바로 특별한 기능이다.
 
나는 이 IllegalArgumentException 안에 메시지를 넣어 예외를 다르게 표현하는 방식을 사용해왔다. 
프리코스 때 뿐만 아니라 우테코 들어와 자동차 미션, 사다리 타기 미션 때까지만하여도.
이때까진 커스텀 예외를 사용하는 크루를 보고 아 너무 오버 엔지니어링 아닌가, 클래스 너무 많이 만들어지는 거 아닌가 라고 생각을 했었다.
 
페어 백호와 자동차 경주 미션 구현할 때에도 그냥 예외 메시지만 다르게 던지지 뭣하러 클래스를 만드냐, 귀찮다는 의견이었다. 이 부분에 대해서 고민했고 리뷰어 범블비에게도 질문을 했었다.

 
요약하자면, 걍 메시지만 달리 하면 안됨? 차피 커스텀 클래스 구현해도 클래스 이름만 다르지, 뭣도 없자나. 관리해야할 클래스만 늘어나는 걸.
 

질문에 대한 답변

 
자동차 미션에선 굳이 필요 없어보인다는 답변을 받았었다.
 
 

본론

이랬던 내가 커스텀 예외를 사용하기 시작하게 된 것은 블랙잭 미션때부터였다.
 
평소처럼 IllegalArgumentException 파라미터 안에 메시지를 넣어 예외를 표현하였다. 

예외 상황

 
메시지를 통해 예외를 다르게 표현했기에 테스트 코드에서도 이 예외가 발생하였는지 확인하기 위해 hasMessage() 메서드로 확인해줘야 했다.
 

테스트 코드

 
IllegalArgumentException을 여기저기에서 사용하기 때문에 hasMessage로 체크해주지 않으면 발생한 예외가 총 참여자 수가 2이상 8이하가 아니여서 발생했는지 아니면 어디 다른게 잘못되서 발생한 예외인지 파악할 수가 없다.
 
리뷰어 수달과 티키타카 중 이 예외 메시지를 protected 접근제어자를 사용하여 상수로 만들고 재사용해보는 건 어떻겠냐는 피드백을 받았다.
 

 
 
예외 메시지를 상수화해서 관리하게 된다면 특별한 이유가 없는 한 그 상수 메시지를 사용하는 클래스가 가지고 있는게 적절하다고 생각한다. 왜냐 메시지를 빠르게 확인할 수 있기 때문이다. 
하지만 이 메시지를 여러 클래스에서 사용하게 된다면 아예 예외 메시지를 관리하는 곳을 만들고 여기에 모아두는게 적절해보인다. enum의 목적이 상수를 관리하기 위해 만들어 졌기 때문에 상수만을 모아둔 클래스는 enum으로 만드는게 좋아보인다.
 
블랙잭 미션때는 한번 사용되는 메시지들도 enum으로 만들려고 했었다. 사용되는 클래스에 모으려니 상수 필드가 너무 커졌기 때문이다.
 
하지만 한번만 사용되는 메시지를 enum으로 만들긴 뭔가 찝찝한 느낌이 있었고 이제 슬슬 커스텀 예외가 필요한 상황이라고 생각되어 커스텀 예외가 등장하였다!
 

블랙잭의 커스텀 예외

 (아니 왜 OutOfRangeBetAmout만 Exception 접미사를 안붙인거지..)
 
이제 메시지를 확인하는 것이 아니라 예외를 확인할 수 있었다. 👍
 

 
 
 
 

결론

체스 미션에서도 커스텀 예외를 사용했었고 리뷰어 제이미에게 커스텀 예외에 대한 장단점이 무엇이 있는지 질문을 받았다. 
 

 
좀 더 심도 있게 답변해보자.
 

장점

예외 상황을 명확하게 전달하였기에 각 예외 상황에 따라 다르게 처리할 수 있었다.

체스 미션에서 나는 킹이 잡히는 경우를 예외로 보았다. (킹이 잡히는 경우를 예외로 보는게 맞을지에 대한 부분도 많은 크루들과 토론을 하였는데, 이 이야기는 나중에 다시 하기로 하고,,,) 
그래서 만약 킹을 잡는 움직임이라면 예외를 던지고, 위에서 KingDeadException을 캐치하여 킹이 잡힘으로써 체스 게임이 종료되었음을 확인할 수 있었다.
 

 
(다시 보니 KingDeadException을 캐치하고 Checkmate 상태가 되는게 이상하다. 킹이 잡히고 게임 종료 상태가 맞다.)
 

예외 상황을 명확하게 전달하였기에 테스트 코드에서도 예외만 확인할 수 있었다.

앞서 살펴보았던 것처럼 테스트 코드에서 hasMessage로 예외 메시지를 확인하는 것이 아니라 isInstanceOf로 예외만 확인해도 테스트가 가능하였다.
 
 

단점

예외가 추가될 때마다 클래스 단위로 늘어났기 때문에 클래스가 굉장히 많은데 이 예외가 어디에서 사용되는지 예외 패키지 안에선 한번에 파악하기 어렵다.
체스 미션에선 도메인 관련 예외와 데이터베이스 관련 예외만 패키지를 분리하였는데 도메인 관련 예외 안에서도 적절한 패키지 분리가 필요해보인다.
 

체스의 커스텀 예외

 
 추가로 이펙티브 자바 아이템 72 "표준 예외를 사용하라"에서 전하는 표준 예외를 사용하지 않았을 때의 단점을 살펴보자.
 
이미 잘 알려진 표준 예외를 사용하는 것이 커스텀 예외를 사용하는 것보다 다른 사람들이 익히고 사용하기가 더 쉽다는 것이다. 그리고 또 클래스 수가 적을수록 메모리 사용량도 줄고 클래스를 적재하는 시간도 적게 걸린다는 것이다.