개발을 해 나가면서 점점 더 중요하게 느껴지는 부분 중 하나가 바로 예외 처리입니다.
초반에는 애플리케이션이 정상적으로 동작하는지에 집중하게 되지만, 시간이 지날수록 예외 상황에 대한 철저한 대응이 안정성에 필수적이라는 것을 실감하게 됩니다.
특히 API 개발에서 예외 처리는 단순히 오류 메시지를 출력하는 것을 넘어서, 클라이언트가 요청의 실패 원인을 명확하게 알 수 있도록 도와주는 역할을 합니다.
이 글에서는 스프링 부트 기반 애플리케이션에서 예외 처리를 체계적으로 구현하고, 이를 통해 API가 보다 안정적으로 동작하도록 설계하는 방법을 살펴보겠습니다.
⭐ 예외 처리란 무엇인가?
예외 처리(Exception Handling)는 프로그램 실행 중 예기치 않은 오류가 발생했을 때 이에 대한 적절한 대응을 하는 것을 의미합니다.
단순히 오류 메시지를 출력하는 것이 아니라, 발생한 예외에 따라 적절한 응답 코드와 메시지를 클라이언트에 전달함으로써 시스템의 안정성을 높일 수 있습니다.
스프링 부트에서는 ExceptionHandler와 ControllerAdvice와 같은 기능을 활용해 예외를 일관된 방식으로 처리할 수 있습니다.
이를 통해 특정 오류 상황에 맞는 맞춤형 메시지와 상태 코드를 정의하고, 불필요한 에러 전파를 방지할 수 있습니다.
⭐ 예외 처리가 중요한 이유
예외 처리가 중요한 이유는 크게 두 가지입니다.
첫째, 클라이언트는 응답 메시지를 통해 요청의 성공 여부 및 실패 원인을 파악할 수 있습니다.
둘째, 서버는 이를 통해 에러가 발생했을 때 시스템이 예상치 못한 상태로 전이되는 것을 방지할 수 있습니다.
예를 들어, 클라이언트에서 유효하지 않은 토큰으로 요청을 보냈을 경우 단순히 오류가 발생했다고만 알려주는 것이 아니라, 해당 토큰이 유효하지 않음을 명확히 전달하여 클라이언트가 문제를 수정할 수 있도록 하는 것이 바람직합니다.
⭐ 스프링 부트에서 예외 처리 구조
스프링 부트는 기본적으로 HandlerExceptionResolver라는 메커니즘을 통해 예외가 발생했을 때 요청이 다시 WAS까지 거슬러 올라가 불필요하게 응답되는 것을 방지합니다. 예외 처리를 적절히 설계하면 불필요한 에러 전파를 줄이고, API 응답을 일관되게 관리할 수 있습니다.
1️⃣ ExceptionBase 클래스 정의하기
우선, 프로젝트에서 공통으로 사용할 ExceptionBase라는 커스텀 예외 클래스를 정의합니다.
이 클래스는 RuntimeException을 상속받아 프로젝트 내의 모든 예외 처리를 이 클래스를 통해 관리할 수 있게 합니다.
public abstract class ExceptionBase extends RuntimeException {
public abstract int getStatusCode();
protected String errorMessage;
protected ResponseCode errorCode;
}
2️⃣ 커스텀 예외 클래스 생성
ExceptionBase를 상속받아 실제로 발생할 수 있는 예외를 커스텀 예외 클래스로 구현합니다. 예를 들어, 인증 관련 예외를 AuthException으로 정의할 수 있습니다.
@ResponseStatus(value = HttpStatus.UNAUTHORIZED)
public class AuthException extends ExceptionBase {
public AuthException(ResponseCode _responseCode, String message) {
this.errorCode = _responseCode;
this.errorMessage = message;
}
@Override
public int getStatusCode() {
return HttpStatus.UNAUTHORIZED.value();
}
}
3️⃣ 예외 응답 객체 정의
예외가 발생했을 때 클라이언트에 전달할 응답 객체인 ErrorResponse를 정의합니다. 이 객체는 예외의 세부 정보와 HTTP 상태 코드를 포함해 클라이언트가 문제의 원인을 파악할 수 있게 도와줍니다.
public class ErrorResponse extends HashMap<String, Object> {
public ErrorResponse(ExceptionBase exception) {
this.put("error", true);
this.put("http_status_code", exception.getStatusCode());
this.put("error_code", exception.getErrorCode());
this.put("error_message", exception.getErrorMessage());
}
}
4️⃣ ControllerAdvice를 통한 전역 예외 처리
이제 예외 발생 시 전역에서 이를 핸들링할 수 있도록 ControllerAdvice와 ExceptionHandler를 활용합니다. @ControllerAdvice는 모든 컨트롤러에서 발생하는 예외를 처리할 수 있도록 하고, @ExceptionHandler는 특정 예외를 처리하는 핸들러 메서드를 정의할 수 있게 합니다.
@ControllerAdvice
public class ExceptionResolver {
@ExceptionHandler(AuthException.class)
@ResponseStatus(HttpStatus.UNAUTHORIZED)
@ResponseBody
public ErrorResponse handleAuthException(AuthException exception) {
return new ErrorResponse(exception);
}
}
이렇게 하면 AuthException이 발생할 경우 handleAuthException 메서드가 호출되어, ErrorResponse 객체를 JSON 형태로 클라이언트에게 반환합니다.
이처럼 스프링 부트에서의 예외 처리는 단순히 에러 메시지를 출력하는 것을 넘어서, 애플리케이션의 안정성과 사용자 경험을 높이는 중요한 역할을 합니다. 올바른 예외 처리를 통해 클라이언트가 요청의 실패 원인을 명확히 이해하고, API 응답을 일관되게 관리하여 예외 발생 시에도 신뢰성 높은 서비스를 제공할 수 있습니다.
'기술 지식 쌓아가기 📚 > Backend 🍔' 카테고리의 다른 글
[MySQL Workbench] 아주 쉽게 ERD 추출하여 이미지로 저장하는 방법 (1) | 2024.11.15 |
---|---|
[IntelliJ] 자동완성 기능 켜기/끄기 (0) | 2024.11.14 |
[Backend] 스프링부트로 설명하는 백엔드 개발 개념 완전 정복 🚀 (IT 개발 동아리 면접 질문 완벽 대비 가능⭐) (0) | 2024.11.11 |
[Optimization] Spring의 Ahead-of-Time (AOT) Compilation: 성능과 효율을 높이는 컴파일 방식 알아보기 (0) | 2024.11.10 |
[Optimization] 무중단 배포: 서비스 중단 없이 안전하게 배포하는 방법 (0) | 2024.11.09 |