리뷰 작성 테스트를 진행 하던 중에 원하는 오류 내용과 다른 오류가 발생하는 문제를 확인했습니다.
이 문제를 해결하며 배운 오류 처리 흐름에 대해 기록합니다.
리뷰 작성 API를 수행할 때 아래의 비즈니스 로직을 수행합니다.
public void verifyUserCanReview(
String accessToken,
UUID authUserId,
UUID orderId,
UUID productId
) {
String encodedJwt = "Bearer " + accessToken;
OrderDetailResponse order =
orderClient.getOrderDetail(encodedJwt, orderId);
if(order == null) {
throw new BizException(ReviewErrorCode.ORDER_INFO_NOT_FOUND);
}
// 주문자 == 현재 로그인 유저인지 확인
if (!order.userId().equals(authUserId)) {
throw new BizException(ReviewErrorCode.USER_NOT_ORDER_OWNER);
}
// 주문 안에 해당 상품이 포함되어 있는지 확인
OrderItemResponse item = order.items().stream()
.filter(i -> i.productId().equals(productId))
.findFirst()
.orElseThrow(() -> new BizException(ReviewErrorCode.PRODUCT_NOT_IN_ORDER));
// 주문 상태가 리뷰 가능한 상태인지 확인 (배송 완료)
if (!item.status().isReviewable()) {
throw new BizException(ReviewErrorCode.ORDER_NOT_REVIEWABLE);
}
}

Client
→ ReviewController
→ ReviewService
→ OrderClientService
→ OrderClient (feign, order-service 호출)
→ OrderClientService (검증 로직)
→ BizException(ORDER_NOT_REVIEWABLE) 발생
호출 흐름만 생각했을 때는 revier-service 내부 비즈니스 예외인데,
응답은 인증 오류처럼 보입니다.

✅ 중요 포인트 :
POST /error가 추가로 호출된 것을 확인할 수 있습니다.❌ 직접 작성한 BizException이 처리되지 않고 있었습니다.
공통 모듈에 아래와 같은 전역 예외 처리기가 등록되어 있습니다.
@RestControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(BizException.class)
public ResponseEntity<ApiResponse<?>> handleBizError(BizException e) {
return ResponseEntity
.status(e.getResponseCode().getStatus())
.body(ApiResponse.error(e.getResponseCode()));
}
}
하지만 멀티 모듈 구조에서 GlobalExceptionHandler가 review-service의 컴포넌트 스캔 범위 밖에 위치하기 때문에 실제로는 review-service에서 등록되지 않고 있었던 것입니다.
컨트롤러/서비스에서 발생한 예외가
@ExceptionHandler에 의해 처리되지 않으면
기본 에러 처리 경로/error로 내부 디스패치(forward)한다.
즉,
1. BizException(ORDER_NOT_REVIEWABLE) 발생
2. review-service에서 이 예외를 잡아줄 @RestControllerAdvice가 실제로 적용되지 않음
3. 예외 미처리 → Spring이 /error로 포워딩
4. /error 요청이 다시 Spring Security 필터 체인을 탐
5. 이 과정에서 인증 정보가 없으므로 → 인증 오류(SERV-006) 응답이 생성됨
그래서 비즈니스 예외가 인증 오류로 덮여 보이는 현상이 발생한 것이었습니다.
✅ review-service에 명시적으로 ExceptionHandler 등록
@RestControllerAdvice
public class CustomExceptionHandler extends GlobalExceptionHandler {
}
review-service 모듈 내부에 ExceptionHandler를 배치하면 컴포넌트 스캔 대상이 되면서 BizException이 정상적으로 처리됩니다.
이번 이슈를 통해서 오류가 어디서 발생하는지보다 어디서 처리하는지가 더 중요하다는 것을 배울 수 있었습니다.
특히 Spring Security가 결합된 환경에서 예외 처리 흐름을 이해하지 못하고 개발하는 경우 실제 원인과 전혀 다른 오류가 전달될 수 있습니다. 이 혼선을 예방하기 위해 /error 요청을 permitAll() 로 분리하여 의도와 다른 오류로 고통받지 않도록 예외의 책임을 명확히 설계하는 습관을 들여야겠습니다.