스프링/MVC

[MVC 기초] Custom 객체 Converter 생성, ConversionService에 등록해 자유롭게 변환하기 (+thymeleaf에서 변환법)

nomoreFt 2022. 7. 25. 22:45

특정 객체 Converter 만들기

Converter 인터페이스를 구현하여 변환을 원하는 객체, 결과 순으로 입력한다.

  • ex) IP, port를 변수로 가지고 있는 IpPort 객체를 String으로 변환해보자.
  1. 원하는 객체 생성 (IpPort)
  2. @Getter @EqualsAndHashCode public class IpPort { private String ip; private int port; public IpPort(String ip, int port) { this.ip = ip; this.port = port; } }
  • @EqualsAndHashCode : @Data를 등록하면 자동으로 생성되는 롬복 제공 기능이다 객체가 같은지 equals를 제공해준다.
  1. IpPort 객체를 원하는 객체로 변환 Converter 생성
@Slf4j  
public class IpPortToStringConverter implements Converter<IpPort,String> {  
@Override  
public String convert(IpPort source) {  
log.info("converter source={}", source);  
return source.getIp() + ":" + source.getPort();  
}  
}
  • converter : 변환 메서드
public interface Converter<S, T> {

    @Nullable
    T convert(S source);
}

Converter 테스팅

단순히 객체를 new 해서 convert 기능을 사용하면 된다.

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

제작한 ConverterConversionService에 등록하여 사용하기

매 번 모든 컨버터를 개별적으로 사용하는 것 보다,
일괄적으로 등록하여 사용하는게 더 작업에 효율적이다. 일괄로 묶어주는 일을 해주는 것이

큰 장점으로는 사용자가 등록된 컨버터들에 대한 구체적인 정보를 몰라도,
conversionService만으로 사용할 수 있다.

ConversionService 이다.

ConversionService 인터페이스

public interface ConversionService {
    boolean canConvert(@Nullable Class<?> sourceType, Class<?> targetType);
    <T> T convert(@Nullable Object source, Class<T> targetType);
  • canConvert : 컨버팅이 가능한가
  • convert : 변환

예시

Bean 등록 방법

conversionService를 생성하며, 여기에 내가 원하는 객체 변환기를
등록하기 위해서, @Bean 등록을 해줄 필요가 있다.(이미 스프링에서 내부에서 사용중이기 때문에,)

@Configuration
@RequiredArgsConstructor
public class WebMvcConfiguration {

    @Bean(name="conversionService") //빈 등록
    public ConversionService getConversionService() {
        ConversionServiceFactoryBean bean = new ConversionServiceFactoryBean(); //팩토리 생성

        Set<Converter> converters = new HashSet<>(); //컨버터 담은 Set 생성
        converters.add(new IpPortToStringConverter());
        converters.add(new StringToIpPortConverter());

        bean.setConverters(converters); //add converters
        bean.afterPropertiesSet();
        return bean.getObject();
    }

}
  • bean.afterPropertiesSet() : 프로퍼티를 세팅하고 기본적인 생성 작업을 담당한다.

나와 같은 xml 세대를 위해 이해가 쉽게 xml버젼도

<bean id="conversionService" class="org.springframework.context.support.ConversionServiceFactoryBean">
    <property name="converters">
        <set>
            <bean class="example.MyCustomConverter"/>
        </set>
    </property>
</bean>
  • converters 프로퍼티에 set으로 커스텀 컨버터들을 넣어준다.

WebMvcConfiguration으로 추가하기


@Configuration
@RequiredArgsConstructor
public class WebMvcConfiguration  implements WebMvcConfigurer {
    @Override
    public void addFormatters(FormatterRegistry registry) {
        registry.addConverter(new IpPortToStringConverter());
        registry.addConverter(new StringToIpPortConverter());
    }
}
  • implements WebMvcConfigurer : WebMvcConfigurer에는 이미 Converter 등록 기능이 있다.
  • addFormatters : Converter를 등록

conversionService 테스트

등록된 것 확인

@SpringBootTest
public class ConversionServiceTest {
    @Autowired
    ConversionService conversionService;

    @Test
    void conversionService() {
        //등록

        IpPort result = conversionService.convert("127.0.0.1:8080", IpPort.class);
        Assertions.assertThat(result).isEqualTo(new IpPort("127.0.0.1", 8080));

    }

}
  • @Autowired ConversionService conversionService : 내가 빈으로 등록한 (빈 네임 : conversionService) 가 주입된다.

변환에 성공했다. 등록이 성공적으로 완료됐다는 의미.

스크린샷 2022-07-25 오후 10 46 53

컨버터를 등록했기 때문에, 내 커스텀 객체가 자동 변환이 가능하게 됐다.
Api 객체 받기에 사용 가능하다. (약간 )

    @GetMapping("/test")
    public String testing(@RequestParam IpPort ipPort) {
        System.out.println("ipPort.getPort() = " + ipPort.getPort());
        System.out.println("ipPort.getIp() = " + ipPort.getIp());
        return "ok";
    }

http://localhost:8080/test?ipPort=127.0.0.1:8080 로 테스트시 결과

잘 변환된 모습

스크린샷 2022-07-25 오후 11 18 51


View 에서 thymeleaf Converting 이용 출력하기

기본적으로 타임리프는 ConversionService가 지원된다.

  • ${...} : 우리가 아는 일반적인 변수 표현식
  • ${{...}} : 컨버젼 서비스 적용하여 변환된 값이 나온다.

우리가 만든 IpPort 객체를 Thymeleaf로 변환

${ipPort} vs ${{ipPort}}

Controller에서 Model에

addAttribute("ipPort",new IpPort("127.0.0.1",8080));

하여 Rest가 아닌 View에 담아 넘기는 경우,

<span th:text="${ipPort}">
<span th:text="${{ipPort}}">
  • ${ipPort}는 객체 프록시 그대로 보여준다.
  • ${{ipPort}}는 컨버젼 서비스를 사용하란 명령이므로 127.0.0.1:8080 으로 보여준다.

Form에서 컨버팅 사용하기

<form th:obejct="${form}" th:method="post">
    <input type="text" th:field="*{ipPort}"><br/>
    <input type="text" th:value="*{ipPort}">
</form>
  • *{} : th:object가 있으면, 그 변수를 포함하는 값을 나타낸다.
  • th:field : 컨버젼 서비스를 이미 포함하고 있어서 ipPort 실 데이터를 보여준다.

Form에서 컨버팅 사용법

  1. @GetMapping으로 form View로 넘겨주기
  2. form에서 위처럼 작성해서 Controller에서 ipPort 변환 컨버터를 사용하여
    자동 변환 시켜주기
  3. 그 컨트롤러에서 원하는 곳으로 보내기 (View라면 ${{ipPort}}로 표현)