ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [김영한 스프링] 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

     

    스프링 MVC 2편 - 백엔드 웹 개발 활용 기술 - 인프런 | 강의

    웹 애플리케이션 개발에 필요한 모든 웹 기술을 기초부터 이해하고, 완성할 수 있습니다. MVC 2편에서는 MVC 1편의 핵심 원리와 구조 위에 실무 웹 개발에 필요한 모든 활용 기술들을 학습할 수 있

    www.inflearn.com

Designed by Tistory.