본문 바로가기
Spring|Spring-boot

Spring Converter

by oncerun 2022. 1. 10.
반응형

 

HTTP 요청 파라미터는 모두 문자로 처리된다. 따라서 요청 파라미터를 다른 타입으로 변환해서 사용하고 싶으면 다른 타입으로 변환하는 과정이 필요하다.

 

그런데 스프링에서는 (@RequestParam Interger data)와 같이 요청 파라미터를 Interger로 받을 수 있다.

그래서 별도의 타입 변환 과정 없이 사용할 수 있다. 

 

이 맥락에서 꼭 알아야하는 지식은 HTTP 메시지의 파라미터는 모두 문자라는 점이다.  

우리가 사용하는 @ModelAttribute, @PathVariable에서도 마찬가지이다.

 

/users/{userId}
@PathVariable("data") Integer data



----------------


@ModelAttribute UserData data
class UserData {
 Integer data;
}

 

즉 변환하는 모든 번거로운 과정을 Spring이 TypeConverter를 사용해 손쉽게 원하는 타입으로 값을 받을 수 있다.

 

Converter 인터페이스

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

이 컨버터의 인터페이스는 S -> T 타입으로 변환하도록 규약 되어 있다.

 

참고 

과거에는 PropertyEditor 라는 것으로 타입을 변환했다. PropertyEditor는 동시성 문제가 있어서 타입을 변환할 때마다 객체를 계속 생성해야 하는 단점이 있다. 지금은 Converter의 등장으로 해당 문제들이 해결되었고, 기능 확장이 필요하면 Converter를 사용하면 된다.

 

 

숫자 <-> 문자 Converter

public class StringToIntegerConverter implements Converter<String, Integer> {

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



public class IntegerToStringConverter implements Converter<Integer,String> {
    @Override
    public String convert(Integer source) {

        return String.valueOf(source);
    }
}

 

 

 스프링은 용도에 따라 다양한 방식의 타입 컨버터를 제공한다. 

 

Converter 기본 타입 컨버터 

ConverterFactory 전체 클래스 계층 구조가 필요할 때 

GenericConverter 정교한 구현, 대상 필드의 애노테이션 정보 사용 가능 

ConditionalGenericConverter 특정 조건이 참인 경우에만 실행 

 

자세한 내용은 공식 문서를 참고하자.

Convert

 

Core Technologies

In the preceding scenario, using @Autowired works well and provides the desired modularity, but determining exactly where the autowired bean definitions are declared is still somewhat ambiguous. For example, as a developer looking at ServiceConfig, how do

docs.spring.io

 스프링은 문자, 숫자, 불린, Enum등 일반적인 타입에 대한 대부분의 컨버터를 기본으로 제공한다. IDE에서 Converter , ConverterFactory , GenericConverter의 구현체를 찾아보면 수많은 컨버터를 확인할 수 있다.

 

 

ConversionService

 

 

개별적으로 Convert인터페이스를 구현한 구현체를 만들었다면 스프링이 제공하는 기능을 이용할 수 있다. 

스프링은 ConversionService interface를 통해 컨버터를 묶어서 편리하게 사용할 수 있는 기능을 제공한다.

 

package org.springframework.core.convert;
import org.springframework.lang.Nullable;
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);
}

 

Test코드

@Test
void conversionService() {

    //등록하자
    DefaultConversionService defaultConversionService = new DefaultConversionService();

    defaultConversionService.addConverter(new StringToIntegerConverter());
    defaultConversionService.addConverter(new IntegerToStringConverter());
    defaultConversionService.addConverter(new IpPortToStringConverter());
    defaultConversionService.addConverter(new StringToIpPortConverter());

    //사용
    Integer result = defaultConversionService.convert("10", Integer.class);
    Assertions.assertThat(result).isEqualTo(10);

    String host = "127.0.0.1:8080";
    IpPort result2 = defaultConversionService.convert(host, IpPort.class);
    Assertions.assertThat(result2).isEqualTo(new IpPort("127.0.0.1", 8080));
}

DefaultConversionService 는 다음 두 인터페이스를 구현했다.

 

 

  • ConversionService : 컨버터 사용에 초점
  • ConverterRegistry : 컨버터 등록에 초점

 

이렇게 인터페이스를 분리하면 컨버터를 사용하는 클라이언트와 컨버터를 등록하고 관리하는 클라이언트의 관심사를 명확하게 분리할 수 있다. 특히 컨버터를 사용하는 클라이언트는 ConversionService 만 의존하면 되므로, 컨버터를 어떻게 등록하고 관리하는지는 전혀 몰라도 된다. 결과적으로 컨버터를 사용하는 클라이언트는 꼭 필요한 메서드만 알게 된다. 이렇게 인터페이스를 분리하는 것을 ISP 라 한다.

 

스프링은 내부에서 ConversionService 를 사용해서 타입을 변환한다. 예를 들어서 @RequestParam 같은 곳에서 이 기능을 사용해서 타입을 변환한다. 

 

다음과 같이 스프링에 생성한 컨버터를 등록한다.

@Configuration
public class Config implements WebMvcConfigurer {

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

    }
}

 

* 추가한 컨버터는 스프링이 제공하는 컨버터보다 우선순위를 갖는다.

 

 

Spring MVC 구조에서 Handler의 Argument는 ArgumentResolver의 구현체들이 호출 전에 객체를 생성해서 넣어주는데  이때 타입 변환에 대해서는 ConversionService를 통해 타입을 변환한다.

 

단순히 HTTP의 Parameter에만 쓰이지 않고, properties파일을 읽고 변환할 때, yml을 읽을 때 xml에서 빈정보를 읽을 때, view를 렌더링 할 때 등등 많은 곳에서 활용될 수 있다.

 

Thymeleaf에서 view를 렌더링할 때 다음과 같이 사용할 수도 있다. 

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

 

${{}} 중괄호가 2개씩 사용된 것은 타입 컨버터가 동작한다는 의미를 가진다.

반응형

'Spring|Spring-boot' 카테고리의 다른 글

Spring-boot Error Page  (0) 2022.01.13
Spring Formatter  (0) 2022.01.10
Spring MessageCodesResolver  (0) 2022.01.08
Spring Web Validation (2)  (0) 2022.01.08
Spring Web Validation (1)  (0) 2022.01.08

댓글