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