김영한 강사님의 스프링 MVC 2편 - 백엔드 웹 개발 활용 기술을 듣고 정리한 내용입니다. 자세한 내용은 강의를 참고해주세요
@PathVariable을 이용해, url을 받을 때 -> 모두 자료형이 String이다
스프링 타입 변환 적용의 예
@RequestParam, @ModelAttribute,@Pathvariable@Value등으로 넣은 YML정보를 읽을 때@Configuration의 클래스에 등록하면 된다(WebMvcConfigurer 구현)
Converter<S,T>로, 메서드 convert를 사용하면 S타입을 T타입으로 변경한다.
@EqualsAndHashCode를 사용해, 참조 자료형이, 참조값이 달라도, 값이 같으면 equals결과가 침이 되게 한다
parseInt(): 원시데이터인 int 타입을 반환
Integer.valueOf(): Integer 래퍼(wrapper)객체를 반환
@RestController
public class HelloController {
@GetMapping("/hello-v1")
public String helloV1(HttpServletRequest request){
String data = request.getParameter("data"); // 뮨자 타입 조회
Integer intValue = Integer.valueOf(data); // 숫자타입으로 변경
System.out.println("intValue = " + intValue);
return "ok";
}
@GetMapping("/hello-v2")
public String helloV2(@RequestParam("data") Integer data){
System.out.println("data = " + data);
return "ok";
}
@GetMapping("/ip-port")
public String ipPort(@RequestParam("ipPort")IpPort ipPort){
System.out.println("ipPort IP = " + ipPort.getIp());
System.out.println("ipPort Port = " + ipPort.getPort());
return "ok";
}
}
ConversionService이다참고
스프링은 용도에 따라 다양한 방식의 타입 컨버터를 제공한다.
Converter: 기본 타입 컨버터
ConverterFactory: 전체 클래스 계층 구조가 필요할 때
GenericConverter: 정교한 구현, 대상 필드의 애노테이션 정보 사용 가능
ConditionalGenericConverter: 특정 조건이 참인 경우에만 실행

canConvert : 컨버팅이 가능한가?convert : 컨버팅이 가능하다면, 컨버팅 기능을 제공DefaultConversionService를 이용하는데
ConversionService : 컨버터 사용에 초점ConverterRegistry : 컨버터 등록에 초점등록과 사용의 분리! -> 즉 역할에 따라 분리를 했기 때문이다(ISP)
StringToIntegerConverter같은 타입 컨버터를 명확히 알아야 한다SOLID의 ISP(Interface Segregation Principle)을 잘 지켰고, 좋은 설계라고 할 수 있다.@Configuration
public class WebConfig implements WebMvcConfigurer {
@Override
public void addFormatters(FormatterRegistry registry) {
registry.addConverter(new StringToIntegerConverter());
registry.addConverter(new IntegerToStringConverter());
registry.addConverter(new StringToIpPortConverter());
registry.addConverter(new IpPortToStringConverter());
}
}
@Configuration이 있는 WebConfig에 WebConfigurer을 구현해,addFormatters라는 메서드를 오버라이드해서, 추가하고 싶은 컨버터를 등록한다ConversionService에 컨버터를 추가할 수 있다!!!@RequestParam을 이용한다고 하면, ArgumentResolver의 구현체인 RequestParamMethodArgumentResolver에서 ConversionService를 사용해 원하는 타입으로 바꿀 수 있다!${{...}}를 사용하면 자동으로 컨버전 서비스를 사용해서 변환된 결과를 출력해준다
{} 1개: converter적용 X
{{}} 2개: converter적용O
th:field는 converter까지 자동으로 적용해버린다, {{}}를 2개 쓸 필요가 없다
만약 컨버터 적용하기 싫다?? -> th:value를 사용하면 된다


${{number}} : 뷰 템플릿은 데이터를 문자로 출력한다. 따라서 컨버터를 적용하게 되면 Integer 타입인 10000 을 String 타입으로 변환하는 컨버터인 IntegerToStringConverter 를 실행하게 된다.
${{ipPort}} : 뷰 템플릿은 데이터를 문자로 출력한다. 따라서 컨버터를 적용하게 되면 IpPort 타입을 String 타입으로 변환해야 하므로 IpPortToStringConverter 가 적용된다. 그 결과 127.0.0.1:8080 가 출력된다.
@Slf4j
@Controller
public class ConverterController {
@GetMapping("/converter-view")
public String converterView(Model model){
model.addAttribute("number",10000);
model.addAttribute("ipPort",new IpPort("127.0.0.1",8080));
return "converter-view";
}
@GetMapping("/converter/edit")
public String converterForm(Model model){
IpPort ipPort = new IpPort("127.0.0.1", 8080);
Form form = new Form(ipPort);
model.addAttribute("form",form);
return "converter-form";
}
@PostMapping("/converter/edit")
public String converterEdit(@ModelAttribute("form") Form form, Model model){
IpPort ipPort = form.getIpPort();
model.addAttribute("ipPort",ipPort);
return "converter-view";
}
@Getter
@Setter
@RequiredArgsConstructor
static class Form{
private IpPort ipPort;
public Form(IpPort ipPort) {
this.ipPort = ipPort;
}
}
}
th:object와 th:field="*{}"문법을 이용할 수 있다.Model이니까 addAttribute로 잘 담아서 view에서 이용하자!th:field가 자동으로 컨버전 서비스를 적용해주어서 %{{isPort}}처럼 적용되었다 @ModelAttribute를 사용해서 String자료를 IpPort로 변환하고Formatter가 있다Locale 정보가 있는데, 이를 통해 현지화 정보를 사용한다


숫자 1000을 1,000으로 바꾸는 포맷, 반대도 해주는 포맷터를 만들어 보자
@Slf4j
public class MyNumberFormatter implements Formatter<Number> {
@Override
public Number parse(String text, Locale locale) throws ParseException {
log.info("text={}, locale={}", text,locale);
// "1,000" -> 1000
NumberFormat format = NumberFormat.getInstance(locale);
return format.parse(text);
}
@Override
public String print(Number object, Locale locale) {
log.info("object={}, locale={}", object, locale);
return NumberFormat.getInstance(locale).format(object);
}
}
NumberFormat기능을 이용해준다parse() : 문자를 -> 객체print() : 객체를 -> 문자파라미터 : 바꿀 객체, Locale.KOREA(로케일 정보)FormattingConversionService 는 포맷터를 지원하는 컨버전 서비스이다DefaultFormattingConversionService 는 FormattingConversionService 에 기본적인 통화, 숫자 관FormattingConversionService는 ConversionService 관련 기능을 상속받기 때문에 결과적으로 컨버터도 포맷터도 모두 등록할 수 있다. ConversionService 가 제공하는 convert 를 사용하면 된다.DefaultFormattingConversionService 를 상속 받은 WebConversionService 를 내부에서 사용한다

@Configuration 파일에 등록해주고
@NumberFormat: 숫자 관련 형식 지정 포맷터 사용, NumberFormatAnnotationFormatterFactory
@DateTimeFormat: 날짜 관련 형식 지정 포맷터 사용,
Jsr310DateTimeFormatAnnotationFormatterFactory
@Controller
public class FormatterController {
@GetMapping("/formatter/edit")
public String formatterForm(Model model){
Form form = new Form();
form.setNumber(10000);
form.setLocalDateTime(LocalDateTime.now());
model.addAttribute("form",form);
return "formatter-form";
}
@PostMapping("/formatter/edit")
public String formatterEdit(@ModelAttribute("form") Form form){
return "formatter-view";
}
@Data
static class Form{
@NumberFormat(pattern = "###,###")
private Integer number;
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private LocalDateTime localDateTime;
}
}
@NumberFormat @LocalDateTime 각각 에노테이션을 붙여서 적용을 한다!!!pattern 파라티머를 이용해, 원하는 모양으로 포맷팅을 하는 것이다
주의!
메시지 컨버터(HttpMessageConverter)에는 컨버전 서비스가 적용되지 않는다.
특히 객체를 JSON으로 변환할 때 메시지 컨버터를 사용하면서 이 부분을 많이 오해하는데,
HttpMessageConverter의 역할은 HTTP 메시지 바디의 내용을 객체로 변환하거나 객체를 HTTP 메시지 바디에 입력하는 것이다. 예를 들어서 JSON을 객체로 변환하는 메시지 컨버터는 내부에서 Jackson 같은 라이브러리를 사용한다.
객체를 JSON으로 변환한다면 그 결과는 이 라이브러리에 달린 것이다.
따라서 JSON 결과로 만들어지는 숫자나 날짜 포맷을 변경하고 싶으면 해당 라이브러리가 제공하는 설정을 통해서 포맷을 지정해야 한다.
결과적으로 이것은 컨버전 서비스와 전혀 관계가 없다.
jackson 데이터 포멧터
ObectMapper 데이터 포멧터
이렇게 검색해야 함!!!(해당 라이브러리의)
컨버전 서비스는
@RequestParam,@ModelAttribute,@PathVariable,뷰 템플릿등에서 사용할 수 있다.