리팩토링 이유
기존 GlobalExceptionHandler는
ServiceException과 MethodArgumentNotValidException만 처리하고 있었다.
즉, 예상한 예외에 대해서는 응답 형식을 통일할 수 있었지만,
그 외의 예상하지 못한 예외가 발생할 경우에는
Spring의 기본 500 에러 응답이 내려갈 가능성이 있었다.
이 경우 문제는 다음과 같았다.
- API 응답 형식이 일관되지 않음
- 프론트엔드에서 에러 응답을 동일한 방식으로 처리하기 어려움
- 서버 내부 예외 원인을 로그로 추적하기 어려움
이러한 문제를 보완하기 위해 GlobalExceptionHandler를 리팩토링하였다.
기존 코드의 한계
기존 코드에서는 다음 두 가지 예외만 처리했다.
- ServiceException
- MethodArgumentNotValidException
즉, 예를 들어 NullPointerException, IllegalStateException 같은
예상하지 못한 런타임 예외가 발생하면,
프로젝트에서 정의한 ApiResponse 형식이 아닌
기본 에러 응답이 반환될 수 있었다.
또한, 예외 발생 시 별도의 로그를 남기지 않기 때문에
문제가 발생했을 때 원인 파악이 어려울 수 있었다.
리팩토링 내용
1. @Slf4j 추가
예상하지 못한 예외가 발생했을 때
로그를 남겨 원인 추적이 가능하도록 하였다.
@Slf4j
2. Exception.class 처리 추가
모든 예외의 마지막 안전망 역할을 하는
@ExceptionHandler(Exception.class)를 추가하였다.
이를 통해 처리되지 않은 예외가 발생하더라도
항상 동일한 형식의 응답을 반환할 수 있도록 했다.
@ExceptionHandler(Exception.class)
public ResponseEntity<ApiResponse<ExceptionResponse>> handleException(
Exception exception,
HttpServletRequest request
) {
log.error("Unhandled exception occurred", exception);
ExceptionResponse response = ExceptionResponse.from(
HttpStatus.INTERNAL_SERVER_ERROR.value(),
ErrorCode.INTERNAL_SERVER_ERROR.getMessage(),
request.getRequestURI()
);
return ResponseEntity
.status(HttpStatus.INTERNAL_SERVER_ERROR)
.body(ApiResponse.fail(HttpStatus.INTERNAL_SERVER_ERROR, response));
}
3. ServiceException 처리 방식 개선
기존에는 exception.getStatus()와 exception.getMessage()를 직접 사용했지만,
리팩토링 후에는 ErrorCode를 꺼내 명시적으로 사용하도록 변경했다.
ErrorCode errorCode = exception.getErrorCode();
이렇게 하면
- 예외의 근본 원인이 되는 ErrorCode를 기준으로 처리할 수 있고
- 상태 코드와 메시지의 출처가 더 명확해진다.
즉, 단순 문자열/상태값 사용보다
에러 코드 중심의 일관된 예외 처리 구조로 정리한 것이다.
리팩토링 후 기대 효과
이번 리팩토링으로 다음과 같은 개선 효과를 얻을 수 있었다.
- 예상한 예외뿐 아니라 예상하지 못한 예외까지 처리 가능
- 모든 실패 응답을 ApiResponse<ExceptionResponse> 형식으로 통일
- 예외 발생 시 로그를 남겨 디버깅 가능성 향상
- ErrorCode 기반 처리로 예외 응답의 일관성 강화
느낀 점
예외 처리는 단순히 에러 메시지를 반환하는 것을 넘어,
어떤 예외까지 시스템이 책임지고 처리할 것인지를 결정하는 과정이라는 것을 느꼈다.
기존에는 “내가 만든 예외만 처리하면 충분하지 않을까?”라고 생각했지만,
실제로는 예상하지 못한 예외도 언제든 발생할 수 있기 때문에
마지막 안전망 역할을 하는 글로벌 예외 처리기가 꼭 필요했다.
또한, 응답 형식을 통일하는 것만큼이나
에러 로그를 남겨 문제를 추적할 수 있게 만드는 것도 중요하다는 점을 배웠다.
⭐ 예상한 예외만 처리하던 구조에서, 예상하지 못한 예외까지 포괄하도록 개선하여
응답 일관성과 디버깅 가능성을 높였다.
'트러블슈팅' 카테고리의 다른 글
| 외부 API 호출 비동기 처리 적용 (@Async) (0) | 2026.03.31 |
|---|---|
| 외부 플랫폼 주문 전송 기능 - Mock API 구현 (0) | 2026.03.31 |
| DTO 생성 방식 혼용 문제를 기준 정립으로 해결하기 (1) | 2026.03.30 |
| 기능 중심 API 설계의 한계와 도메인 중심으로의 개선 과정 (0) | 2026.03.30 |
| Response DTO에서 Builder와 생성자를 구분해서 사용한 이유 (1) | 2026.03.30 |