스프링

스프링 - 컨버터 / 포매터

이째형 2021. 12. 17. 22:26

컨버터란?

애플리케이션을 개발하다가 보면 타입을 변환해야 하는 경우가 많다. ( 특히 문자 <=> 다른 타입)

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

HttpServletRequest 객체를 이용해서 쿼리스트링을 받을 경우, 다음과 같이 개발자가 직접 Integer형으로 변환을 해주지 않으면 문자값을 읽게된다.

 

@GetMapping("/hello-v2")
public String helloV2(@RequestParam Integer data) {
 	System.out.println("data = " + data);
 	return "ok";
}

하지만 @RequestParam 어노테이션을 사용할 경우, 쿼리스트링을 따로 변환 작업 없이 Integer 타입의 숫자로 받을 수 있다. 이는 스프링이 타입변환을 수행해주기 때문이다.

(@ModelAttribute, @PathVariable 어노테이션을 이용해도 똑같이 스프링이 타입변환을 수행해준다. )

 

 

스프링은 다음과 같은 상황에 타입 변환이 자동으로 적용된다.

  • 스프링 MVC 요청 파라미터 
  • @Value를 이용해서 YML 정보를 읽을 때
  • XML에 넣은 스프링 빈 정보를 반환
  • 뷰를 렌더링 할 때

컨버터 인터페이스

새로운 타입을 만들어서 타입 변환을 하고 싶다면, 스프링에서 지원하는 Converter 인터페이스를 이용할 수 있다.

package org.springframework.core.convert.converter;

public interface Converter<S, T> {
	 T convert(S source);
}

 

다음과 같이 간단하게 source와 T위치에 원하는 타입의 값만 넣으면 컨버터를 생성할 수 있으며,

@Slf4j
public class IntegerToStringConverter implements Converter<Integer, String> {

 @Override
 public String convert(Integer source) {
 	log.info("convert source={}", source);
 	return String.valueOf(source);
 }
}

@Slf4j
public class StringToIntegerConverter implements Converter<String, Integer> {

 @Override
 public Integer convert(String source) {
 	log.info("convert source={}", source);
 	return Integer.valueOf(source);
 }
}

테스트 코드를 작성해보면 잘 동작한다.

class ConverterTest {

 @Test
 void stringToInteger() {
 	StringToIntegerConverter converter = new StringToIntegerConverter();
 	Integer result = converter.convert("10");
 	assertThat(result).isEqualTo(10);
 }
 @Test
 void integerToString() {
 	IntegerToStringConverter converter = new IntegerToStringConverter();
 	String result = converter.convert(10);
 	assertThat(result).isEqualTo("10");
 }
}

 

Stirng/Integer 타입과 같이 원래 존재하는 타입이 아니라, 개발자가 만든 객체 타입의 컨버터도 만들 수 있다.

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

IpPort라는 객체를 String으로 변환 시키는 컨버터와 그 역 컨버터를 생성했다.

@Slf4j
public class StringToIpPortConverter implements Converter<String, IpPort> {
 @Override
 public IpPort convert(String source) {
 	log.info("convert source={}", source);
 	String[] split = source.split(":");
 	String ip = split[0];
 	int port = Integer.parseInt(split[1]);
 	return new IpPort(ip, port);
 }
 
 @Slf4j
 public class IpPortToStringConverter implements Converter<IpPort, String> {
  @Override
  public String convert(IpPort source) {
 	 log.info("convert source={}", source);
 	 return source.getIp() + ":" + source.getPort();
 }
}

위 컨버터들을 이용해서 "127.0.0.1:8080" 이라는 문자를 IpPort객체로 만들어서 받을 수 있다.

@Test
void stringToIpPort() {
 	StringToIpPortConverter converter = new StringToIpPortConverter();
 	String source = "127.0.0.1:8080";
 	IpPort result = converter.convert(source);
	 assertThat(result).isEqualTo(new IpPort("127.0.0.1", 8080));
}

@Test
void ipPortToString() {
 	IpPortToStringConverter converter = new IpPortToStringConverter();
 	IpPort source = new IpPort("127.0.0.1", 8080);
 	String result = converter.convert(source);
	 assertThat(result).isEqualTo("127.0.0.1:8080");
}

 

컨버전 서비스

@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());
  }
}

컨버터를 직접 찾아서 타입 변환을 하지 않고, 묶어서 편리하게 관리할 수 있는 방법이 있다.

바로 ConversionService를 이용하는 것이다.

 

WebMvcConfigurer에서 제공하는 addFormatters()를 이용하면 직접 생성했던 컨버터를 등록할 수 있다. 이렇게 하면 스프링은 내부의 ConversionSerVice에 컨버터를 등록한다.

 

컨버전 서비스를 이용하면, 컨버터를 등록할 때는 타입 컨버터를 명확하게 알아야 하지만 컨버터를 사용하는 입장에서는 타입 컨버터를 전혀 몰라도 된다. 타입 컨버터들은 모두 컨버전 서비스 내부에 숨어서 제공된다.

 

따라서 타입 변환을 원하는 사용자는 컨버전 서비스 인터페이스에만 의존하면 된다.

 

※ 스프링에서는 다양한 컨버터들을 제공하기 때문에 String/Integer 컨버터등은 이미 구현되어 있다. 컨버터를 추가할 경우에는 추가된 컨버터가 기존 컨버터보다 우선순위를 가진다.


참고

김영한님 인프런 mvc 강의 2편