-
[김영한 스프링] 52. 스프링 타입 컨버터 - 스프링에 Converter 적용하기 & 뷰 템플릿에 컨버터 적용하기Spring/스프링 MVC 2편 - 백엔드 웹 개발 활용 기술 2023. 10. 11. 01:34
스프링에 Converter 적용하기
WebConfig - 컨버터 등록
package hello.typeconverter; import hello.typeconverter.converter.IntegerToStringConverter; import hello.typeconverter.converter.IpPortStringConverter; import hello.typeconverter.converter.StringToIntegerConverter; import hello.typeconverter.converter.StringToIpPortConverter; import org.springframework.context.annotation.Configuration; import org.springframework.format.FormatterRegistry; import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; @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 IpPortStringConverter()); } }
main/java/hello/typeconverter/WebConfig 생성
스프링은 내부에서 ConversionService를 제공한다. 우리는 WebMvcConfigurer가 제공하는 addFormatters()를 사용해서 추가하고 싶은 컨버터를 등록하면 된다. 이렇게 하면 스프링은 내부에서 사용하는 ConversionService에 컨버터를 추가해 준다.
실행
로그
?data=10의 쿼리 파라미터는 문자이고 이것을 Integer data로 변환하는 과정이 필요하다.
실행해 보면 직접 등록한 StringToIntegerConverter가 작동하는 로그를 확인할 수 있다.
그런데 생각해 보면 StringToIntegerConverter를 등록하기 전에도 이 코드는 잘 수행되었다. 그것은 스프링이 내부에서 수많은 기본 컨버터들을 제공하기 때문이다. 컨버터를 추가하면 추가한 컨버터가 기본 컨버터 보다 높은 우선순위를 가진다.
HelloController - 추가
package hello.typeconverter.controller; import hello.typeconverter.type.IpPort; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; import javax.servlet.http.HttpServletRequest; @RestController public class HelloController { @GetMapping("/ip-port") public String ipPort(@RequestParam IpPort ipPort) { System.out.println("ipPort IP = " + ipPort.getIp()); System.out.println("ipPort PORT = " + ipPort.getPort()); return "OK"; } @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 Integer data) { System.out.println("data = " + data); return "OK"; } }
실행
로그
?ipPort=127.0.0.1:8080 쿼리 스트링이 @RequestParam IpPort ipPort에서 객체 타입으로 잘 변환된 것을 확인할 수 있다.
처리 과정
@RequestParam은 @RequestParam을 처리하는 ArgumentResolver인 RequestParamMethodArgumentResolver에서 ConversionService를 사용해서 타입을 변환한다. 부모 클래스와 다양한 외부 클래스를 호출하는 등 복잡한 내부 과정을 거치기 때문에 대략 이렇게 처리되는 것으로 이해해도 충분하다. 만약 더 깊이 있게 확인하고 싶으면 IpPortConverter에 디버그 브레이크 포인트를 걸어서 확인해 보자.뷰 템플릿에 컨버터 적용하기
타임리프는 렌더링 시에 컨버터를 적용해서 렌더링 하는 방법을 편리하게 지원한다.
이전까지는 문자를 객체로 변환했다면, 이번에는 그 반대로 객체를 문자로 변환하는 작업을 확인할 수 있다.
ConverterController
package hello.typeconverter.controller; import hello.typeconverter.type.IpPort; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.web.bind.annotation.GetMapping; @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"; } }
main/java/hello/typeconverter/controller/ConverterController 생성
Model에 숫자 10000와 ipPort 객체를 담아서 뷰 템플릿에 전달한다
converter-view.html
<!DOCTYPE html> <html xmlns:th="http://www.thymeleaf.org"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <ul> <li>${number}: <span th:text="${number}" ></span></li> <li>${{number}}: <span th:text="${{number}}" ></span></li> <li>${ipPort}: <span th:text="${ipPort}" ></span></li> <li>${{ipPort}}: <span th:text="${{ipPort}}" ></span></li> </ul> </body> </html>
main/resources/templates/converter-view.html 생성
타임리프는 ${{...}}를 사용하면 자동으로 컨버전 서비스를 사용해서 변환된 결과를 출력해 준다. 물론 스프링과 통합되어서 스프링이 제공하는 컨버전 서비스를 사용하므로, 우리가 등록한 컨버터들을 사용할 수 있다.
변수 표현식 : ${...}
컨버전 서비스 적용 : ${{...}}
실행
로그
- ${{number}} : 뷰 템플릿은 데이터를 문자로 출력한다. 따라서 컨버터를 적용하게 되면 Integer 타입인 10000을 String 타입으로 변환하는 컨버터인 IntegerToStringConverter를 실행하게 된다. 이 부분은 컨버터를 실행하지 않아도 타임리프가 숫자를 문자로 자동으로 변환하기 때문에 컨버터를 적용할 때와 하지 않을 때가 같다.
- ${{ipPort}} : 뷰 템플릿은 데이터를 문자로 출력한다. 따라서 컨버터를 적용하게 되면 IpPort 타입을 String 타입으로 변환해야 하므로 IpPortToStringConverter가 적용된다. 그 결과 127.0.0.1:8080가 출력된다.
폼에 적용하기
ConverterController - 코드 추가
package hello.typeconverter.controller; import hello.typeconverter.type.IpPort; import lombok.Data; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.ModelAttribute; import org.springframework.web.bind.annotation.PostMapping; @Controller public class ConverterController { @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, Model model) { IpPort ipPort = form.getIpPort(); model.addAttribute("ipPort", ipPort); return "converter-view"; } @Data static class Form { private IpPort ipPort; public Form(IpPort ipPort) { this.ipPort = ipPort; } } @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"; } }
Form 객체를 데이터를 전달하는 폼 객체로 사용한다.
- GET /converter/edit : IpPort를 뷰 템플릿 폼에 출력한다.
- POST /converter/edit : 뷰 템플릿 폼의 IpPort 정보를 받아서 출력한다.
converter-form.html
<!DOCTYPE html> <html xmlns:th="http://www.thymeleaf.org"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <form th:object="${form}" th:method="post"> th:field <input type="text" th:field="*{ipPort}"><br/> th:value <input type="text" th:value="*{ipPort}">(보여주기 용도)<br/> <input type="submit"/> </form> </body> </html>
main/resources/templates/converter-form.html 생성
타임리프의 th:field는 앞서 설명했듯이 id, name을 출력하는 등 다양한 기능이 있는데, 여기에 컨버전 서비스도 함께 적용된다.
실행
로그
실행 - 제출 버튼
로그
- GET /converter/edit
- th:field가 자동으로 컨버전 서비스를 적용해 주어서 ${{ipPort}}처럼 적용이 되었다. 따라서 IpPort -> String으로 변환된다.
- POST /converter/edit
- @ModelAttribute를 사용해서 String -> IpPort로 변환된다.
출처 : https://www.inflearn.com/course/%EC%8A%A4%ED%94%84%EB%A7%81-mvc-2
'Spring > 스프링 MVC 2편 - 백엔드 웹 개발 활용 기술' 카테고리의 다른 글
[김영한 스프링] 54. 스프링 타입 컨버터 - 포맷터 적용하기 & 스프링이 제공하는 기본 포맷터 (1) 2023.10.11 [김영한 스프링] 53. 스프링 타입 컨버터 - 포맷터(Formatter) & 포맷터를 지원하는 컨버전 서비스 (0) 2023.10.11 [김영한 스프링] 51. 스프링 타입 컨버터 - 타입 컨버터(Converter) & 컨버전 서비스(ConversionService) (1) 2023.10.10 [김영한 스프링] 50. 스프링 타입 컨버터 - 소개 (1) 2023.10.05 [김영한 스프링] 49. 스프링 타입 컨버터 - 프로젝트 생성 & 세팅 (0) 2023.10.05