We will find a way, we always have.

-interstellar

Spring

[Spring] 우리집을 못 찾겠군요 (feat: @RestControllerAdvice, @Order)

Redddy 2024. 8. 18. 23:40

배경지식

우리 코딩해듀오는 예외 상황을 좀 더 구체적으로 표현하고, 또 테스트도 정확하게 하고자 커스텀 예외를 사용하기로 컨벤션을 정했다. 그리고 각 도메인 별로 패키지를 나누어 각 패키지 마다 대빵 예외가 있고, 이 대빵 예외를 상속하는 세부 예외들이 있었다. @RestControllerAdvice도 각 패키지 안에 들어가 대빵 예외들을 잡아주고 common 패키지에선 최상위 예외인 Exception을 잡아주는 구조였다. 

 

각 패키지마다 있는 @RestControllerAdvice

 

 

초반에는 각 도메인의 대빵 예외가 CoduoException이라는 찐대장 예외를 상속하는 구조였다가 너무 깊은 상속구조를 갖는 것 같아 CoduoExcpetion은 제거하였다.

 

예외 구조

 

 

문제 상황

AccessCode로 페어룸을 찾는데 실패하면 PairRoomNotFoundException을 던지는 로직이 있었고, 해당 예외는 @RestControllerAdvice가 붙어있는 PairRoomErrorController의 @ExceptionHandler(PairRoomNotFoundException.class)에서 잡히기를 기대했다.

 

하지만 저어기 common의 @ExceptionHandler(Exception.class)에서 잡히는 것이였다. 

 

PairRoomNotFoundException이 Exception의 자식이긴하지만 더 상세하게 구분한 PairRoomNotFoundException에서 잡힐 줄 알았는데 그게 아니었다. 

 

PairRoomNotFoundException과 Exception이 하나의 클래스에 안에 있을 때에는 의도한대로 PairRoomNotFoundExceptionHandler가 잡아주는데 서로 다른 클래스에 있을때에는 저어기 딴집가서 잡히는 것이었다.

 

문제원인

문제 원인은 예상했다시피 PairRoomNotFoundException이 PairRoomNotFoundExceptionHandler을 만나기전에 ExceptionHandler를 만나기 때문에 발생하는 것이었다. 먼저 만나는 것은 스프링 빈 등록 순서때문이었다. 

 

액추에이터로 등록된 빈들을 확인해보니 CommonErrorController가 PairRoomErrorController 보다 앞에 있었다. (배운거 써먹는중

 

그래서 @Order 애노테이션을 활용해 PairRoomErrorController 의 빈 등록 순서를 앞당겼더니 테스트는 통과하였다. 그리고 액추에이터로 등록된 빈들을 다시 확인해보았는데, 둘의 순서는 똑같았다. 이때 하나 더 깨달은 것은 액추에이터의 beans 들은 순서는 등록 순서와 무관한다는 점이다. 

 

해결 방법

@RestControllerAdvice가 제공하는 basePackages를 사용하여 PairRoomErrorController의 범위를 pairroom 패키지로 지정해도 테스트는 통과하지 않았었다. 

 

지금까지 찾은 방법은 두가지이다.

 

  1. @Order 애노테이션 사용으로 순서 지정
  2. 하나의 ErrorController에 때려밖기

 

어떤 방법을 사용하여 해결할지는 이제 팀원들과 같이 논의해보려고 한다 😀

 

배운점

스프링은 항상 구체적인 조건들에 먼저 반응하여 처리하는줄만 알았는데 상황에 따라 아닐 수도 있다는 것을 배웠다.

하나의 @RestControllerAdvice 안에서는 구체적인 예외에게 먼저 반응하지만 여러개의 @RestControllerAdvice가 있다면 빈 등록 순서에 영향을 받고, 먼저 등록된 @RestControllerAdvice에 찾아가서 처리할 예외가 있나 살펴본다.