Web/Spring

Spring MVC(2) - 스프링 타입 컨버터

now0204 2023. 11. 21. 11:04

 

1. 타입 컨버터란?

 

- 문자를 숫자로 변환하거나, 숫자를 문자로 변환하는 일이 굉장히 많음

- 기존 스프링을 사용하면서 @RequestParam, @ModelAttribute, @Pathvariable에서 이게 가능했음 (자동)

   추가로 @Value를 통해 정보읽기, XML에 넣은 스프링 빈 정보 변환 + 뷰 렌더링 

   (요청파라미터, @Value, 뷰렌더링)

- 개발자가 직접 새로운 타입을 정의하고, 이에 맞는 타입 컨버터를 등록하고 싶다면 어떻게 해야할까?

 

package org.springframework.core.convert.converter;
public interface Converter<S, T> {
 T convert(S source);
}

 

- 스프링은 확장 가능한 컨버터 인터페이스를 제공함! 

- 추가적인 타입 변환이 필요하면 위 인터페이스를 구현하고, 등록하면 된다.

 

* (org.springframework.core.convert.converter.Converter를 import해서 사용하도록 주의하자)

 

2. 타입 컨버터 만들어보기

 

2.1 String,Integer

 


public class StringToIntegerConverter implements Converter<String, Integer> {

	public Integer convert(String source){
    	return Integer.valueOf(source);
    }

}

public class IntegerToStringConverter implements Converter<Integer,String>{

	public String convert(Integer source){
    	return String.valueOf(source);
    }
}

 

- 위와 같이 converter를 만들면, converter를 생성해서 자유롭게 활용 가능하다.

 

@Test

void stringToInteger(){
		
        StringToIntegerConverter converter = new StringToIntegerConverter();
        Integer result = converter.convert("10");
        assertThat(result).isEqualTo(10);

}

 

 

2.2 사용자 정의 타입 컨버터 

 

- 사용자 정의 타입

@Getter
@EqualsAndHashCode
public class IpPort {
 private String ip;
 private int port;
 public IpPort(String ip, int port) {
 this.ip = ip;
 this.port = port;
 }
}

 

- String, IpPort Converter

 

public class StringToIpPortConverter implements Converter<String, IpPort> {

	public IpPort convert(String source){
    
		String[] split = source.split(":");
        String ip = split[0];
    	int port = Integer.parseInt(split[1]);
    	return new IpPort(ip,port);
    }

}

public class IpPortToStringConverter implements Converter<IpPort, String> {
 @Override
 public String convert(IpPort source) {
 
 return source.getIp() + ":" + source.getPort();
 }
}

 

> 타입 컨버터 인터페이스는 아주 단순해서 어렵진 않다! 

> 이제 타입 컨버터를 등록하고 관리하면서 편리하게 변환 기능을 제공하는 걸 배워보자 

 

출처: 스프링MVC(2) 김영한 - 인프런

 

 

3. 컨버전 서비스 - 컨버터 묶어서 편리하게 사용하기

 

public interface ConversionService {
boolean canConvert(@Nullable Class<?> sourceType, Class<?> targetType);
boolean canConvert(@Nullable TypeDescriptor sourceType, TypeDescriptor 
targetType);
<T> T convert(@Nullable Object source, Class<T> targetType);
Object convert(@Nullable Object source, @Nullable TypeDescriptor sourceType,
TypeDescriptor targetType);
}

 

 

3.1 컨버전 서비스에 등록과 사용

 

@Test
 void conversionService() {
 
 	DefaultConversionService conversionService = new DefaultConversionService();
    	
        //등록
    	conversionService.addConverter(new StringToIntegerConverter());
        conversionService.addConverter(new IntegerToStringConverter());
 		conversionService.addConverter(new StringToIpPortConverter());
 		conversionService.addConverter(new IpPortToStringConverter());
 		
        //사용
        conversionService.convert("10",Integer.class);
 		
        	
 }

 

- 등록하는 경우에는 StringToIntegerConverter와 같은 타입 컨버터를 명확하게 알아야함

- 사용하는 경우 정확하게 몰라도 됨 (컨버전 서비스 내부에 숨어서 제공됨)

- 따라서 등록하는 경우와 사용하는 경우 다른 인터페이스에 의존해서 등록과 사용하면됨

  (구현체만 DefaultConversionService)

 

* DefaultConversionService : ConversionService (사용), ConverterRegistry(등록) 두개의 인터페이스 구현 (ISP)

 

 

4. 스프링에 Converter 적용하기

 

@Configuration
public class WebConfig implements WebMvcConfigurer{


	public void addFormatters(FormatterRegistry regist){
    	 registry.addConverter(new StringToIntegerConverter());
         registry.addConverter(new IntegerToStringConverter());
 		 registry.addConverter(new StringToIpPortConverter());
 		 registry.addConverter(new IpPortToStringConverter());
    }

}

 

- 스프링 내부에서 ConversionService를 제공함 따라서 WebMvcConfigurer에 addFormatters()를 사용해서 등록하면 됨

- 스프링 내부에서 사용하는 ConversionService에 컨버터 추가해줌 

 

* 기본 컨버터보다, 직접 등록한 컨버터의 우선순위가 더 높다.

 

4.1 처리과정

 

- @RequestParam -> ArgumentResolver만남 (RequestParamMethodArgumentResolver) 

- 얘가 Argument를 집어넣는 과정에서 ConversionService를 사용해서 타입 변환을 시도 

 

 

5. 뷰 템플릿에 컨버터 적용하기

 

- 뷰에 컨버터를 적용하는 방법을 알아보자

- 타임리프는 이러한 렌더링을 편리하게 제공함

- 이제 객체 -> 문자 컨버팅

 

 @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";
 }

-> 이렇게 view로 객체를 넘겨보자

 

<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>

 

-> ${}는 그냥 객체의 값을 사용하고 ${{}}을 사용하면, 컨버전 서비스의 컨버터를 사용해서 출력한다.

 

* String -> 객체 : 항상 컨버터를 거쳐서 특정 객체로 변환된다.

  객체 -> String : 컨버터를 사용할 수도 있고 아닐 수도 있다.

 

 

5.1 폼에 적용하기

 

//폼에 기본값으로 넘겨주기 위해 
@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";
 }
 
 //폼에서 넘어온 값을 Converting
 @PostMapping("/converter/edit")
 public String converterEdit(@ModelAttribute Form form, Model model) {
 IpPort ipPort = form.getIpPort();
 model.addAttribute("ipPort", ipPort);
 return "converter-view";
 }

 

<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>

 

- 폼에 (input태그 등)에 값을 출력할때는 field를 사용하면 컨버팅 된 값을

  보여주고 value를 사용하면 객체 값을 보여준다.

 

*th field는 id,name을 출력하는 등 다양한 기능 제공한다. 컨버전 서비스도 함께 적용됨