잡동사니
Http Request의 Object 바인딩시 enum 으로 받는 방법 본문
안녕하세요. yeTi입니다.
오늘은 Spring Framework으로 http request를 처리할때 request string을 enum으로 바인딩하는 방법을 보면서 Spring에서 제공하는 바인딩 서비스들을 확인해보겠습니다.
이전에 작성한 스프링 추전 강좌, 스프링 프레임워크 핵심 기술 내용 정리 및 후기 에도 관련 내용이 있습니다.
Converter 활용
Spring 에서는 모델을 바인딩하기 위해 Converter interface를 제공합니다.
그리고 http request가 들어오면 각 컨트롤러들은 BindingInitializer에 등록된 converter 들을 가지고 string 을 모델로 변환해줍니다.
이를 활용하여 http get 요청시 Path variable와 Request param 에 정의한 EnumType 클래스로 변환할 수 있습니다.
스프링에서는 기본적으로 StringToEnum 클래스를 컨버터로 제공해주는데, 해당 클래스는 대문자에 대해서만 Enum으로 변경해주도록 되어있습니다.
따라서 다른 형식을 사용하고 싶다면 컨버터에 원하는 로직을 넣어 사용하면 됩니다.
Converter 빈 등록
@GetMapping("/enum/{type}")
public ResponseEntity<Response> getData(@PathVariable(name = "enums") final EnumType enumType) {
...
}
@GetMapping("/enum")
public ResponseEntity<Response> getData(@RequestParam(name = "enums") final EnumType enumType) {
...
}
---
import org.springframework.core.convert.converter.Converter;
...
@Component
public class EnumTypeConverter implements Converter<String, EnumType> {
@Override
public EnumType convert(String source) {
return source.isEmpty() ? null:EnumType.valueOf(source.trim().toUpperCase());
}
}WebMvcConfigurer 에 converter 클래스 추가
@GetMapping("/enum/{type}")
public ResponseEntity<Response> getData(@PathVariable(name = "enums") final EnumType enumType) {
...
}
@GetMapping("/enum")
public ResponseEntity<Response> getData(@RequestParam(name = "enums") final EnumType enumType) {
...
}
---
import org.springframework.core.convert.converter.Converter;
...
public class EnumTypeConverter implements Converter<String, EnumType> {
@Override
public EnumType convert(String source) {
return source.isEmpty() ? null:EnumType.valueOf(source.trim().toUpperCase());
}
}
---
@Configuration
public class WebConfiguration implements WebMvcConfigurer {
@Override
public void addFormatters(FormatterRegistry registry) {
WebMvcConfigurer.super.addFormatters(registry);
registry.addConverter(new EnumTypeConverter());
}
}Formatter 활용
Converter 와 마찬가지로 모델을 바인딩하기 위한 interface입니다.
다만 converter보다 특화되어 사용하고 싶거나(Annotation 활용) 양방향 바인딩 활용, Locale 을 활용하고 싶을 때 사용할 수 있습니다.
대표적인 annotation formatter로는 @NubmerFormat 과 @DateTimeFormat 이 있습니다.
Formatter 빈 등록
@GetMapping("/enum/{type}")
public ResponseEntity<Response> getData(@PathVariable(name = "enums") final EnumType enumType) {
...
}
@GetMapping("/enum")
public ResponseEntity<Response> getData(@RequestParam(name = "enums") final EnumType enumType) {
...
}
---
import org.springframework.format.Formatter;
...
@Component
public class EnumTypeFormatter implements Formatter<EnumType> {
@Override
public EnumType parse(String text, Locale locale) throws ParseException {
return text.isEmpty() ? null: EnumType.valueOf(text.trim().toUpperCase());
}
@Override
public String print(EnumType enumType, Locale locale) {
return enumType.toString().toLowerCase();
}
}WebMvcConfigurer 에 formatter 클래스 추가
@GetMapping("/enum/{type}")
public ResponseEntity<Response> getData(@PathVariable(name = "enums") final EnumType enumType) {
...
}
@GetMapping("/enum")
public ResponseEntity<Response> getData(@RequestParam(name = "enums") final EnumType enumType) {
...
}
---
import org.springframework.format.Formatter;
...
public class EnumTypeFormatter implements Formatter<EnumType> {
@Override
public EnumType parse(String text, Locale locale) throws ParseException {
return text.isEmpty() ? null: EnumType.valueOf(text.trim().toUpperCase());
}
@Override
public String print(EnumType enumType, Locale locale) {
return enumType.toString().toLowerCase();
}
}
---
@Configuration
public class WebConfigutation implements WebMvcConfigurer {
@Override
public void addFormatters(FormatterRegistry registry) {
WebMvcConfigurer.super.addFormatters(registry);
registry.addFormatter(new EnumTypeFormatter());
}
}Message Converter 활용
위에서 언급한 Converter와 Formatter는 http request에 대하여 URL이나 query string 과 같은 http request parameter를 model object에 바인딩하는 역할을 합니다.
따라서 POST 와 같이 body로 전달된 XML이나 JSON 같은 데이터 포맷을 object로 변환하기 위해서는 message converter를 사용해야 합니다.
앞서 언급한것처럼 미디어 타입이 application/json 으로 들어오면 스프링 프레임워크는 등록된 messageConverter를 사용하는데 기본적으로 MappingJackson2HttpMessageConverter 를 사용하고 있습니다.
이 때, DeserializationContext 는 각 타입에 맞는 Deserializer 를 찾아서 object 변환을 수행하는데요.Enum의 경우에는 기본적으로 BasicDeserializerFactory에서 EnumDeserializer 를 받아 사용하게 됩니다.
Deserializer 활용
Enum 을 사용하면서 커스텀한 로직을 사용하고 싶다면 @JsonCreator 사용하면 되는데요.
내부 함수에 @JsonCreator 어노테이션을 달면 FactoryBasedEnumDeserializer 가 반환되고 메소드가 등록되어 deserialize 할 때 동작합니다.
public enum EnumType{
@JsonCreator
public static EnumTypefrom(String s) {
return EnumType.valueOf(s.toUpperCase());
}
}Setter 활용
Enum 을 사용하면서 커스텀한 로직을 사용하고 싶을때 deserialize 를 사용하지 않고 setter 를 사용하는 방법이 있는데요.
BeanDeserializer가 object를 바인딩할때 object의 디폴트 생성자로 instance를 생성한 후에 setter를 사용해서 값을 맵핑 시킵니다.
이 때 정의된 setter가 있으면 해당 함수를 사용하여 값을 맵핑하기 때문에 setter에 커스텀한 로직을 넣어 사용할 수 있습니다.
public void setEnums(String enumsString) {
this.enums = EnumType.valueOf(enumsString.toUpperCase());
}참고 자료
- 토비의 스프링 3 - 13.3.2 Converter와 Formatter
- 토비의 스프링 3 - 13.5 메시지 컨버터와 AJAX
- Enum 파라미터 바인딩 - haerong22
'IT > Spring' 카테고리의 다른 글
| 스프링 @Transactional 에서 readOnly 설정을 하는 이유 (0) | 2024.06.18 |
|---|---|
| 서비스 레이어의 테스트는 어떻게 작성하나요? (feat. 테스트 코드 작성하는 법) (0) | 2023.05.04 |
| MyBatis에서 resultType으로 Object 맵핑시 필드에 상이한 값이 맵핑되는 이슈 (0) | 2022.05.10 |
| 예제로 배우는 스프링 입문 완주 후기 (0) | 2022.04.12 |
| Spring Framework 개념 이해하기 (0) | 2021.01.14 |