스프링에 Converter 적용하기
웹 애플리케이션에 Converter 를 적용해보자.
WebConfig - 컨버터를 등록한다.
package hello.typeconverter;
import hello.typeconverter.converter.IntegerToStringConverter;
import hello.typeconverter.converter.IpPortToStringConverter;
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) {
// 없어도 잘 동작하는데, 그 이유는 기본적으로 스프링이 제공하기 떄문이다.
// 사용자정의 컨버터는 직접 구현해줘야함. IP 같은 것
registry.addConverter(new StringToIntegerConverter());
registry.addConverter(new IntegerToStringConverter());
registry.addConverter(new StringToIpPortConverter());
registry.addConverter(new IpPortToStringConverter());
}
}
스프링은 내부에서 ConversionService를 제공한다.
WebMvcConfigurer가 제공하는 addFormatters()를 사용해서 추가하고 싶은 컨버터를 등록하면 된다.
이렇게 하면 스프링은 내부에서 사용하는 ConversionService에 컨버터를 추가해준다.
등록한 컨버터가 잘 동작하는지 확인하기 위하여 HelloController의 /hello-v2 를 이용해본다.
HelloController - 기존 코드
@GetMapping("/hello-v2")
public String helloV2(@RequestParam Integer data) {
System.out.println("data = " + data);
return "ok";
}
실행 - http://localhost:8080/hello-v2?data=10
실행 로그
StringToIntegerConverter : convert source=10
data = 10
?data=10 의 쿼리 파라미터는 문자이고 이것을 Integer data로 변환하는 과정이 필요하다.
실행해보면 직접 등록한 StringToIntegerConverter가 작동하는 로그를 확인할 수 있다.
그런데 생각해보면 StringIntegerConverter를 등록하기 전에도 이 코드는 잘 수행됐다.
그것은 스프링 내부에서 수 많은 기본 컨버터들을 제공하기 때문이다.
컨버터를 추가하면 추가한 컨버터가 기본 컨버터보다 높은 우선순위를 가진다.
이번에는 직접 정의한 타입인 IpPort를 사용해보자.
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";
}
실행 : http://localhost:8080/ip-port?ipPort=127.0.0.1:8080
실행 로그
StringToIpPortConverter : convert source=127.0.0.1:8080
ipPort IP = 127.0.0.1
ipPort PORT = 8080
?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 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 ConvertorController {
@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";
}
}
Model에 숫자 10000 와 ipPort 객체를 담아서 뷰 템플릿에 전달한다.
resources/templates/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>
타임리프는 ${{...}}를 사용하면 자동으로 컨버전 서비스를 사용해서 변환된 결과를 출력해준다.
물론 스프링과 통합되어서 스프링이 제공하는 컨버전 서비스를 사용하므로, 우리가 등록한 컨버터들을 사용할 수 있다.
변수 표현식 : ${...}
컨버전 서비스 적용 : ${{...}}
실행 : http://localhost:8080/converter-view
실행 결과 로그
IntegerToStringConverter : convert source=10000
IpPortToStringConverter : convert source=hello.typeconverter.type.IpPort@59cb0946
- ${{number}} : 뷰 템플릿은 데이터를 문자로 출력한다.
따라서 컨버터를 적용하게 되면 Integer타입인 10000을 String 타입으로 변환하는 컨버터인
IntegerToStringConverter를 실행하게 된다.
이 부분은 컨버터를 실행하지 않아도 타임리프가 숫자를 문자로 자동으로 변환하기 때문에
컨버터를 적용할 때와 하지 않을 때가 같다.
- ${{ipPort}} : 뷰 템플릿은 데이터를 문자로 출력한다.
따라서 컨버터를 적용하게 되면 IpPort 타입을 String 타입으로 변환해야 하므로 IpPortToStringConverter가 적용된다.
그 결과, 127.0.0.1:8080 가 출력된다.
폼에 적용하기
ConverterController - converterForm 추가
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 ConvertorController {
@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, 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;
}
}
}
Form 객체를 데이터 전달 폼 객체로 사용한다.
- GET /converter-edit : IpPort를 뷰 템플릿 폼에 출력한다
- POST /converter-edit : 뷰 템플릿 폼의 IpPort 정보를 받아서 출력한다.
resources/templates/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 는 컨버터 적용이 된다. -->
th:field <input type="text" th:field="*{ipPort}"><br/>
th:value <input type="text" th:value="*{ipPort}">(보여주기 용도)<br/>
<input type="submit"/>
</form>
</body>
</html>
타임리프의 th:field 는 id, name를 출력하는 등의 다양한 기능이 있는데, 여기에 컨버전 서비스도 함께 적용된다.
실행 : http://localhost:8080/converter-edit
- GET /converter-edit
: th:field가 자동으로 컨버전 서비스를 적용해주어서 ${{ipPort}} 처럼 적용되었다. 따라서 IpPort → String으로 변환된다.
- POST /converter-edit
: @ModelAttribute를 사용해서 String → IpPort 로 변환된다.
'스프링 > 스프링 웹 개발 활용' 카테고리의 다른 글
[Spring] 파일 업로드 개념 및 서블릿으로 파일 업로드하기 (1) | 2024.01.13 |
---|---|
[Spring] 포맷터 - Formatter (1) | 2024.01.08 |
[Spring] 스프링 컨버전 서비스 - ConversionService, 그리고 인터페이스 분리 원칙(ISP) (1) | 2023.12.28 |
[Spring] 스프링 타입 컨버터 - 타입 컨버터 (0) | 2023.12.06 |
[Spring] API 예외 처리 - @ExceptionHandler 와 @ControllerAdvice (0) | 2023.12.04 |
댓글