728x90
요구사항
지난번에는 ConstraintViolationException
이 발생했을 때 응답을 커스터마이징 했다. 하지만 Exception
은 하나의 애플리케이션에서 여러 개가 발생할 수 있다. 이 때 마다 응답 형식이 다르면 일관성이 없어보일 수 있지 않을까? 라는 생각을 했다.
그래서 이번에는 Exception에 대한 응답 형식을 만들고, 해당 형식에 맞게 응답을 반환하는 것을 구현하고자 한다.
구현
먼저, global 패키지의 exception 패키지에 enum 타입인 ErrorCode
를 작성한다. ErrorCode에는 세 개의 속성이 있는데, code는 오류 코드를 식별하는 문자열로, message는 응답에 포함되는 오류 메시지, status는 HTTP 응답에서 사용될 상태 코드이다.
package kr.go.data.global.exception;
import lombok.Getter;
@Getter
public enum ErrorCode {
ID_DUPLICATION("ID_001", "이미 존재하는 아이디입니다.", 400),
ID_NOT_FOUND("ID_002", "존재하지 않는 아이디입니다.", 404),
EMAIL_DUPLICATION("EMAIL_001", "이미 존재하는 이메일입니다.", 400),
EMAIL_NOT_FOUND("EMAIL_002", "존재하지 않는 이메일입니다.", 404),
INPUT_VALUE_INVALID("INPUT_001", "입력값이 올바르지 않습니다.", 400);
private final String code;
private final String message;
private final int status;
ErrorCode(String code, String message, int status) {
this.code = code;
this.message = message;
this.status = status;
}
}
그리고 ErrorResponse
클래스를 작성한다. 이 클래스는 응답 형식을 정의하고 있다.
package kr.go.data.global.exception;
import java.util.List;
import lombok.AccessLevel;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
@Getter
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@AllArgsConstructor(access = AccessLevel.PROTECTED)
public class ErrorResponse {
private String message;
private String code;
private int status;
private List<FieldError> errors;
@Getter
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@AllArgsConstructor(access = AccessLevel.PROTECTED)
public static class FieldError {
private String field;
private String value;
private String reason;
}
}
위와 같이 작성하면, 아래와 같은 형식으로 반환하게 된다.
{
"message": "",
"code": "",
"status": "",
"errors": [
{
"field": "",
"value": "",
"reason" : ""
}
]
}
이제 Exception을 처리하는 부분인 GlobalExceptionHandler
를 수정하면 된다.
package kr.go.data.global.exception;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.validation.ConstraintViolationException;
import java.util.List;
import kr.go.data.global.exception.ErrorResponse.FieldError;
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 java.util.stream.Collectors;
@ControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(ConstraintViolationException.class)
public ResponseEntity<ErrorResponse> handleConstraintViolationException(ConstraintViolationException ex, HttpServletRequest request) {
List<ErrorResponse.FieldError> fieldErrors = ex.getConstraintViolations().stream()
.map(violation -> new ErrorResponse.FieldError(violation.getPropertyPath().toString(), violation.getInvalidValue().toString(), violation.getMessage()))
.collect(Collectors.toList());
ErrorResponse errorResponse = new ErrorResponse(
ErrorCode.INPUT_VALUE_INVALID.getMessage(),
ErrorCode.INPUT_VALUE_INVALID.getCode(),
ErrorCode.INPUT_VALUE_INVALID.getStatus(),
fieldErrors
);
return new ResponseEntity<>(errorResponse, HttpStatus.valueOf(errorResponse.getStatus()));
}
}
해결 완료
이후 응답을 보면 다음과 같이 나온다.
{
"message": "입력값이 올바르지 않습니다.",
"code": "INPUT_001",
"status": 400,
"errors": [
{
"field": "checkIsAvailableId.id",
"value": " ",
"reason": "아이디는 비어있을 수 없습니다."
}
]
}
728x90
'TIL' 카테고리의 다른 글
[BadSqlGrammarException] StatementCallback; bad SQL grammar [TRUNCATE TABLE members] (0) | 2024.05.21 |
---|---|
[Kotlin + SpringBoot] JaCoCo 추가하기 (0) | 2024.05.21 |
[Ubuntu] git에서 SpringBoot 프로젝트 받아오고 jar 파일 빌드 후 Script 로 jar 파일 배포하기 (0) | 2024.02.11 |
[JPA] @CreatedDate와 @LastModifiedDate (0) | 2024.01.13 |
[SpringBoot] RequestParam 검증하기 및 오류 메시지 커스터마이징 (2) | 2024.01.01 |