[Spring Boot] MessageSource - 오류 메시지 처리 방식
개요
며칠 전 검증 결과로 에러 메시지를 확인하는 과정에서
API를 사용했을 때 errors.properties를 통해 설정한 메시지가 아닌 디폴트 메시지로 나오는 것을 발견했다.
오류 메시지를 처리하는 과정이 각각의 응답 방식(타임리프와 Json 등)에 차이가 있기 때문이었다.
이번 포스트에서는 이전 글에 이어서 스프링이 오류 메시지를 처리하는 방식에 대해 간단히 알아보고자 한다.
이전글: https://hyunrian.tistory.com/84
Bean Validation - 검증 기능 사용 방법
Bean Validation이란? 회원가입 등 특정 필드에 대한 검증 로직을 모든 프로젝트에 적용할 수 있도록 표준화 한 것으로, 애너테이션을 사용하여 쉽게 검증 기능을 구현할 수 있다. 여기에서 다양한
hyunrian.tistory.com
BindingResult로 에러 메시지 확인하기
스프링은 Bean Validation을 통한 검증이 가능하고, 유효하지 못한 값이 존재할 때 그와 관련한 에러를 Controller에서 `BindingResult`를 통해 확인할 수 있다.
Validation 클래스의 필드에 Bean Validation을 적용하고 `errors.properties`로 메시지를 설정한 후 컨트롤러를 통해 필드 값에 대한 에러를 확인해 보자.
@Data
public class Validation {
@NotNull
@Min(1)
private Integer minVal;
@Max(100)
private Integer maxVal;
@Range(min = 10, max = 100)
private Integer rangeVal;
}
# errors.properties 설정
NotNull.validation.minVal = 값 입력!!
Range={0}, {2} ~ {1} 허용!!
Max={0}, 최대 {1}!!
NotNull= Null 안됨!!!!
@Slf4j
@Controller
public class ValidationController {
@PostMapping("/test")
public String setValues(@Validated @ModelAttribute Vaildation validation, BindingResult bindingResult) {
if (bindingResult.hasErrors()) {
log.info("errors={}", bindingResult);
}
return "test/validationtest";
}
}
`minVal`의 값으로 null을 보내면 로그에 찍히는 `bindingResult`는 아래와 같으며
발생한 에러의 개수와 필드명, rejected value, 디폴트 메시지 등의 내용을 포함하고 있다.
org.springframework.validation.BeanPropertyBindingResult: 1 errors
Field error in object 'validation' on field 'minVal': rejected value [null];
codes [NotNull.validation.minVal,NotNull.minVal,NotNull.java.lang.Integer,NotNull];
arguments [org.springframework.context.support.DefaultMessageSourceResolvable:
codes [validation.minVal,minVal]; arguments []; default message [minVal]];
default message [널이어서는 안됩니다]
디폴트 메시지 [널이어서는 안됩니다]는 스프링이 제공하는 기본 메시지로
`errors.properties`에서 설정했던 [값 입력!!]이 출력될 것이란 기대와 다르다.
타임리프를 사용하여 에러 메시지 확인하기
하지만 view에서 오류 메시지를 확인해보면 기대했던 대로 [값 입력!!]이 출력되는 것을 볼 수 있다.
view의 html 코드는 다음과 같으며 타임리프를 사용했다.
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<!-- 생략 -->
<body>
<div class="container">
<div class="py-5">
<h1>테스트</h1>
<hr>
<form th:action method="post" th:object="${validation}">
minVal <input type="text" th:field="*{minVal}" class="form-control" th:errorclass="field-error"><br>
<p class="field-error" th:errors="*{minVal}"></p>
maxVal <input type="text" th:field="*{maxVal}" class="form-control" th:errorclass="field-error"><br>
<p class="field-error" th:errors="*{maxVal}"></p>
rangeVal <input type="text" th:field="*{rangeVal}" class="form-control" th:errorclass="field-error"><br>
<p class="field-error" th:errors="*{rangeVal}"></p>
<div th:if="${#fields.hasGlobalErrors()}">
<p class="field-error" th:each="err : ${#fields.globalErrors()}" th:text="${err}"></p>
</div>
<button class="btn btn-primary" type="submit">로그인</button>
<button class="btn btn-primary" type="button" onclick="location.href='/'">메인으로</button>
</form>
</div>
</div>
<!-- 생략 -->
물론 `NotNull`뿐만 아니라 다른 필드의 메시지도 설정한대로 잘 출력된다.
MessageSource
위에서 확인했던 BindingResult 로그를 다시 보자.
org.springframework.validation.BeanPropertyBindingResult: 1 errors
Field error in object 'validation' on field 'minVal': rejected value [null];
codes [NotNull.validation.minVal,NotNull.minVal,NotNull.java.lang.Integer,NotNull];
arguments [org.springframework.context.support.DefaultMessageSourceResolvable:
codes [validation.minVal,minVal]; arguments []; default message [minVal]];
default message [널이어서는 안됩니다]
`BindingResult`에는 다음 값이 보관되어 있다.
1. `NotNull.validation.minVal`
2. `NotNull.minVal`
3. `NotNull.java.lang.Integer`
4. `NotNull`
타임리프는 이 값들을 `MessageSource`와 비교하여 1 -> 4의 순서대로 찾고, 찾은 메시지를 view에서 확인할 수 있다.
하지만 타임리프를 사용하지 않는다면 이런 내부적인 로직이 없기 때문에
단순히 컨트롤러에서 `bindingResult`를 통해서는 설정해 놓은 메시지를 확인할 수 없다.
컨트롤러에서 `errors.properties`에 등록한 메시지를 가져 오려면 MessageSource를 활용해야 한다.
messageSource.getMessage(String code, @Nullable Object[] args, Locale locale)
기존 컨트롤러에 적용해보자.
@Slf4j
@Controller
public class ValidationController {
@Autowired private MessageSource messageSource;
@PostMapping("/test")
public String setValues(@Validated @ModelAttribute Vaildation validation, BindingResult bindingResult) {
log.info("MessageSource NotNull message={}", messageSource.getMessage("NotNull.validation.minVal", null, null));
if (bindingResult.hasErrors()) {
log.info("errors={}", bindingResult);
}
return "test/validationtest";
}
}
minVal 필드값에 null을 주고 로그를 확인하면 아래와 같다.
MessageSource NotNull message=값 입력!!
참고로 `MessageSource`란 스프링에서 지원하는 인터페이스로, 메시지의 매개 변수화 및 국제화를 지원한다.
결론적으로 컨트롤러에서 errors.properties에 설정된 메시지를 얻으려면 `MessageSource`를 통해서 값을 꺼내야 하고,
API 응답으로 메시지를 보낼 때도 마찬가지 방식으로 값을 얻어와야 한다.
스프링에서 에러 메시지와 관련하여 Json으로 응답하는 방법에 대해서는 아래 링크에 상세히 나와 있으니 참고하면 좋을 것이다.
https://meetup.nhncloud.com/posts/147
Spring BindingResult를 json으로 받기 : NHN Cloud Meetup
Spring BindingResult를 json으로 받기
meetup.nhncloud.com
참고
MessageSource (Spring Framework 6.1.3 API)
getMessage Try to resolve the message using all the attributes contained within the MessageSourceResolvable argument that was passed in. NOTE: We must throw a NoSuchMessageException on this method since at the time of calling this method we aren't able to
docs.spring.io
'Programming > Spring' 카테고리의 다른 글
[JPA] 상속관계 매핑하기 (0) | 2024.02.09 |
---|---|
Spring Annotation 정리 (0) | 2024.01.23 |
[Spring Boot] Embeded Mode로 테스트 하기 (feat. H2 Database) (0) | 2024.01.16 |
[Spring Boot] Bean Validation - 검증 기능 사용 방법 (0) | 2024.01.05 |
Session에 대해서 - 컨트롤러에서 HttpSession 사용하기 (0) | 2023.12.21 |
Liked this Posting!