자바 스프링에서 에러 메시지를 효과적으로 관리하는 방법은 여러 가지가 있다. 에러 메세지를 사용자에게 제공할 때 일관성을 유지하고, 명확한 피드백을 주며, 보안 및 디버깅을 고려해야 한다. 다음은 자바 스프링에서 에러 메세지를 관리하는 대표적인 방법이다.
1. Custom Exception 클래스 작성
일단, 특정 에러 상황을 처리하기 위한 사용자 정의 예외 클래스를 작성할 수 있다. 예외마다 구체적인 메세지를 포함하여 상황에 맞는 메세지를 제공이 가능하다.
사용자 정의 예외 클래스 ex)
public class InvalidRequestException extends RuntimeException {
private String message;
public InvalidRequestException(String message) {
super(message);
this.message = message;
}
@Override
public String getMessage() {
return message;
}
}
2. GlobalExceptionHandler 사용
전역적으로 예외를 처리할 수 있는 @ControllerAdvice와 특정 예외 처리할때 사용하는 @ExceptionHandler를 사용해 예외 처리를 전역적으로 관리하고, 다양한 예외 상황에 맞는 에러 메세지를 반환이 가능하다.
글로벌 예외 처리 클래스
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.context.request.WebRequest;
@ControllerAdvice
public class GlobalExceptionHandler {
// 특정 예외 처리
@ExceptionHandler(InvalidRequestException.class)
public ResponseEntity<ErrorResponse> handleInvalidRequestException(InvalidRequestException ex, WebRequest request) {
ErrorResponse errorResponse = new ErrorResponse(ex.getMessage(), HttpStatus.BAD_REQUEST.value());
return new ResponseEntity<>(errorResponse, HttpStatus.BAD_REQUEST);
}
// 일반적인 예외 처리
@ExceptionHandler(Exception.class)
public ResponseEntity<ErrorResponse> handleGlobalException(Exception ex, WebRequest request) {
ErrorResponse errorResponse = new ErrorResponse("Internal Server Error", HttpStatus.INTERNAL_SERVER_ERROR.value());
return new ResponseEntity<>(errorResponse, HttpStatus.INTERNAL_SERVER_ERROR);
}
}
에러 메시지 구성을 보면
FieldError: MethodArgumentNotValidException에서 제공하는 BindingResult는 FieldError 객체의 목록을 포함한다. 각 FieldError는 검증 실패가 발생한 필드와 관련된 정보 (필드명, 기본 메시지 등)를 제공한다.
StringBuilder: FieldError를 통해 수집한 정보를 StringBuilder를 사용하여 단일 문자열로 구성한다. 각 필드의 오류 메시지를 명확하게 구분할 수 있도록 한다.
3. 에러 응답 객체 커스텀마이징
에러 메시지를 단순한 텍스트가 아닌 JSON 구조로 관리하면, 클라이언트는 더 구체적인 정보를 얻을 수 있다.
예를 들어 에러 메세지와 상태 코드, 시간 등의 정보를 포함한 응답을 구성이 가능하다.
에러 응답 객체
import java.time.LocalDateTime;
public class ErrorResponse {
private String message;
private LocalDateTime timestamp;
private int status;
public ErrorResponse(String message, int status) {
this.message = message;
this.timestamp = LocalDateTime.now();
this.status = status;
}
// Getter and Setter methods
}
4. Validation(검증) 에러 메시지 관리
스프링에서 @Valid 또는 @Validated를 사용해 입력값을 검증할 때 발생하는 검증 오류에 대한 메세지도 글로벌 예외 처리에서 처리가 가능하다.
검증이 왜 필요한가?
- 데이터 무결성 유지: 잘못된 데이터가 저장되지 않도록 합니다.
- 보안: 잘못된 데이터가 시스템에 악영향을 미치거나 보안 취약점을 유발하지 않도록 합니다.
- 비즈니스 로직: 애플리케이션의 비즈니스 규칙에 맞는 데이터만 처리하도록 합니다.
스프링의 검증 메커니즘을 보면 스프링은 javax.validation API를 활용하여 검증을 지원한다. @Valid 또는 @Validated 어노테이션을 사용하여 요청 데이터가 특정 조건을 만족하는지 확인한다. 검증이 실패하면 MethdoArgumentNotValidException이 발생한다.
ex) MethodArgumentNotValidException 처리
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.validation.FieldError;
@ExceptionHandler(MethodArgumentNotValidException.class)
public ResponseEntity<ErrorResponse> handleValidationException(MethodArgumentNotValidException ex) {
StringBuilder errorMessage = new StringBuilder();
for (FieldError fieldError : ex.getBindingResult().getFieldErrors()) {
errorMessage.append(fieldError.getField()).append(": ").append(fieldError.getDefaultMessage()).append("; ");
}
ErrorResponse errorResponse = new ErrorResponse(errorMessage.toString(), HttpStatus.BAD_REQUEST.value());
return new ResponseEntity<>(errorResponse, HttpStatus.BAD_REQUEST);
}
해당 방식으로, 입력 값 검증 실패에 대해 일관된 에러 메세지를 반환이 가능하다.
MethodArgumentNotValidException 설명을 좀 더 하자면...
- 발생 시점: @Valid 또는 @Validated 어노테이션을 사용하여 요청 바디의 검증이 실패할 때 발생한다. 예를 들어, 요청이 올바르지 않은 형식이나 범위를 벗어난 값이 포함되어 있을 때 발생한다.
- 예외 처리: 이 예외를 처리하여 클라이언트에게 적절한 에러 메시지를 반환할 수 있다. 일반적으로 검증 오류에 대한 상세한 정보를 포함하여 클라이언트가 어떤 잘못된 입력을 했는지 알 수 있도록 한다.
5. 로깅(Logback 등)과 연동
에러 메세지를 관리할 때 로깅도 매우 중요하다. GlobalExceptionHandler에서 예외가 발생할 때 로그를 남겨 디버깅을 쉽게 할 수 있다.
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@ControllerAdvice
public class GlobalExceptionHandler {
private static final Logger logger = LoggerFactory.getLogger(GlobalExceptionHandler.class);
@ExceptionHandler(Exception.class)
public ResponseEntity<ErrorResponse> handleGlobalException(Exception ex, WebRequest request) {
logger.error("Exception occurred: {}", ex.getMessage(), ex);
ErrorResponse errorResponse = new ErrorResponse("Internal Server Error", HttpStatus.INTERNAL_SERVER_ERROR.value());
return new ResponseEntity<>(errorResponse, HttpStatus.INTERNAL_SERVER_ERROR);
}
}
요약하자면...
- Custom Exception 클래스: 각 예외 상황에 맞는 사용자 정의 예외를 만들어 구체적인 메시지를 관리.
- Global Exception Handling: @ControllerAdvice와 @ExceptionHandler를 사용해 중앙에서 에러 메시지를 처리.
- ErrorResponse 클래스: 에러 메시지를 구조화된 JSON 응답으로 관리.
- 검증 에러 처리: 입력값 검증 실패에 대해 일관된 메시지를 반환하도록 처리.
- 로깅: 에러 발생 시 로그를 남겨 디버깅과 모니터링을 용이하게 관리.공.
Enum을 이용해서 예외 메세지를 전달할 경우
enum을 사용하여 에러 메시지를 관리하면 코드의 가독성 및 유지보수성이 높다. 에러 메시지와 관련된 상수들을 enum으로 정의하고, 각 에러에 대한 코드 및 메시지를 관리할 수 있다. 이는 특정 상황에서 발생하는 에러를 효율적으로 처리할 수 있으며, 특히 스프링 프로젝트에서 전역적으로 사용되는 예외 처리에도 유용하다.
1. 기본적인 enum으로 에러 메시지 관리
먼저, 에러 코드를 상수처럼 enum으로 관리하면서 각 에러에 메시지를 할당이 가능하다.
에러 메시지용 enum 작성
public enum ErrorCode {
INVALID_REQUEST("E001", "Invalid request parameter."),
UNAUTHORIZED("E002", "You are not authorized."),
NOT_FOUND("E003", "Resource not found."),
INTERNAL_SERVER_ERROR("E500", "Internal server error occurred.");
private final String code;
private final String message;
// Enum 생성자
ErrorCode(String code, String message) {
this.code = code;
this.message = message;
}
// Getter 메서드
public String getCode() {
return code;
}
public String getMessage() {
return message;
}
}
ErrorCode를 사용하는 예외 처리
이제 글로벌 예외 처리 클래스에서 ErrorCode를 사용해 일관된 에러 메시지를 제공할 수 있다.
2. GlobalExceptionHandler 작성
이제 글로벌 예외 처리 클래스에서 ErrorCode를 사용해 일관된 에러 메시지를 제공할 수 있습니다
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
@ControllerAdvice
public class GlobalExceptionHandler {
// InvalidRequestException 발생 시 처리
@ExceptionHandler(InvalidRequestException.class)
public ResponseEntity<ErrorResponse> handleInvalidRequestException(InvalidRequestException ex) {
ErrorResponse errorResponse = new ErrorResponse(ErrorCode.INVALID_REQUEST);
return new ResponseEntity<>(errorResponse, HttpStatus.BAD_REQUEST);
}
// UnauthorizedException 발생 시 처리
@ExceptionHandler(UnauthorizedException.class)
public ResponseEntity<ErrorResponse> handleUnauthorizedException(UnauthorizedException ex) {
ErrorResponse errorResponse = new ErrorResponse(ErrorCode.UNAUTHORIZED);
return new ResponseEntity<>(errorResponse, HttpStatus.UNAUTHORIZED);
}
// 그 외의 모든 예외에 대한 처리
@ExceptionHandler(Exception.class)
public ResponseEntity<ErrorResponse> handleGlobalException(Exception ex) {
ErrorResponse errorResponse = new ErrorResponse(ErrorCode.INTERNAL_SERVER_ERROR);
return new ResponseEntity<>(errorResponse, HttpStatus.INTERNAL_SERVER_ERROR);
}
}
3. 에러 응답 객체에 enum
사용응답 객체(ErrorResponse)에 ErrorCode를 이용하여 에러 메시지를 JSON 형태로 클라이언트에 제공할 수 있다.
ErrorResponse 클래스 작성
public class ErrorResponse {
private String code;
private String message;
// 에러 코드에 기반한 생성자
public ErrorResponse(ErrorCode errorCode) {
this.code = errorCode.getCode();
this.message = errorCode.getMessage();
}
// Getter 메서드
public String getCode() {
return code;
}
public String getMessage() {
return message;
}
}
4. 사용자 정의 예외 클래스에 ErrorCode 적용
각 예외 상황에 맞는 ErrorCode를 함께 전달하여 더 구체적인 에러 메시지를 사용할 수 있습니다.
사용자 정의 예외 클래스
public class InvalidRequestException extends RuntimeException {
private final ErrorCode errorCode;
public InvalidRequestException(ErrorCode errorCode) {
super(errorCode.getMessage());
this.errorCode = errorCode;
}
public ErrorCode getErrorCode() {
return errorCode;
}
}
예외 발생 시 ErrorCode와 함께 처리
public class UserService {
public void validateUserInput(String input) {
if (input == null || input.isEmpty()) {
throw new InvalidRequestException(ErrorCode.INVALID_REQUEST);
}
}
}
Enum을 활용한 에러 처리를 요약하자면
- ErrorCode Enum 정의: 각 에러에 대한 코드 및 메시지를 enum으로 정의.
- Custom Exception 클래스: ErrorCode를 활용하여 예외 상황에 맞는 에러 코드 및 메시지를 제공.
- GlobalExceptionHandler: @ControllerAdvice를 사용해 에러를 글로벌하게 처리하고, ErrorCode를 사용해 일관된 에러 응답을 반환.
- ErrorResponse 클래스: 클라이언트에게 에러 코드를 포함한 구조화된 JSON 형태로 에러 정보를 제공.
해당 방법을 통해 코드의 가독성과 유지보수성을 높일수 있고, 스프링 에플리케이션에서 에러 메세지를 일관되게 관리가 가능하다.
'Spring' 카테고리의 다른 글
단위테스트, 통합테스트 (1) | 2024.10.01 |
---|---|
Fetch Type:LazyLoading vs EagerLoading (0) | 2024.09.30 |
Service 테스트 코드, controller 테스트 코드 (0) | 2024.09.13 |
AOP(Aspect-Oriented Programming) (0) | 2024.09.12 |
단위 테스트(Unit Testing), JUnit5 (0) | 2024.09.10 |