[Spring] 스프링 부트 - 요청 타입변환(Converter), 포맷터(Formatter)

스프링 타입 컨버터(Converter)

웹 개발을 하다보면 문자를 숫자로 변환하거나, 숫자를 문자로 변환해야 하는 일이 상당히 많습니다.

특히, HTTP 요청 파라미터는 모두 문자로 처리되기 때문에 요청 파라미터를 자바에서 다른 타입으로 변환해서 사용하고 싶으면 다음과 같이 숫자 타입으로 변환하는 과정을 거쳐야 합니다.

 

그런데! 스프링의 요청 파라미터인 @RequestParam , @ModelAttribute , @PathVariable 어노테이션을 사용하면

스프링이 중간에 해당 요청 파라미터의 반환 타입을을 보고, 타입 변환기를 사용해서 타입을 변환해줍니다.

public String hello(@RequestParam Integer data) {
...
}

(@RequestParam 요청 파라미터의 반환 타입이 Integer인 것을보고 String을 -> Integer로 변환 )

 


직접 만든 컨버터 등록하기

직접 만든 타입 변환기를 등록할 수 도 있습니다.

개발하면서 스프링에 추가적인 타입 변환이 필요하면 이 컨버터 인터페이스를 구현해서 등록하면 됩니다.

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

 

예시로 127.0.0.1:8080인 String을 사용자가 만든 클래스인 IpPort로 변환하는 컨버터를 만들어 보겠습니다.

 

IpPort 클래스
@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 타입으로 변환할 컨버터 

먼저 타입을 변환할 클래스를 하나 생성합니다.

이때 Coverter 인터페이스를 상속받고 제네릭에는 각각 <변환 될 타입, 변환 할 타입>을 적습니다.

그리고 convert 메서드를 오버라이딩하여 작성합니다.

public class StringToIpPortConverter implements Converter<String, IpPort> {

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

 

컨버전 서비스 (ConversionService)

스프링은 개별 컨버터를 모아두고 그것들을 묶어서 편리하게 사용할 수 있는 기능을 제공하는데

이것이 바로 컨버전 서비스입니다.

즉, 우리가 구현한 컨버터를 컨버전 서비스에 등록하여 사용할 수 있습니다.

 

@Configuration 어노테이션을 붙여주고, WebMvcConfigurer 인터페이스를 상속받아 다음과 같이 오버라이딩하여 resitrt에 직접 만든 컨버터를 등록해줍니다.

@Configuration
public class WebConfig implements WebMvcConfigurer {

    @Override
    public void addFormatters(FormatterRegistry registry) {
        registry.addConverter(new StringToIpPortConverter());
    }
}

 

이렇게 등록 해주면 요청 파라미터 어노테이션@RequestParam , @ModelAttribute , @PathVariable 가 붙으면 이 어노테이션들이 알아서 컨버트 해줍니다.

 

컨트롤러를 만들어 한번 요청해보겠습니다.

컨트롤러
@RestController
public class Controller {
    @GetMapping("/ip-port")
    public String ipPort(@RequestParam IpPort ipPort) {
        System.out.println("IP = " + ipPort.getIp());
        System.out.println("PORT = " + ipPort.getPort());
        return "OK";
    }
}

쿼리 파라미터를 ipPort=127.0.0.1:8080로 주고 요청한 결과,

우리가 등록한 컨버터에 맞게 변환된 것을 볼 수 있습니다.

 

* 참고 *
스프링이 내부에서 수 많은 기본 컨버터들을 제공하는데,
컨버터를 추가하면 추가한 컨버터가 기본 컨버터 보다 높은 우선순위를 갖게 됩니다.

포맷터(Formatter)

개발을 하다면 객체를 특정한 포맷형식에 맞춰 문자로 출력하는 기능이 필요할 때가 많습니다.

(예를들어 1000원을 출력할 때, 천 자리 구분 기호(,)를 추가하여 1,000으로 변환한다던지, 날짜를 출력할 때 yy-MM-dd 형식으로 출력한다던지 등 )

 

이렇게 객체를 특정한 포멧에 맞추어 문자로 출력하는 기능을 바로 Formatter 라고 합니다. (컨버터의 특별한 버전)

 

스프링은 애노테이션 기반으로 원하는 형식을 지정해서 사용할 수 있는 매우 유용한 포맷터 두 가지를 기본으로 제공하여

우리는 어노테이션을 붙이는 것만으로 포맷터를 적용할 수 있습니다.

 

@NumberFormat 숫자 관련 형식 지정 포맷터 사용
@DateTimeFormat 날짜 관련 형식 지정 포맷터 사용

 

@Data
public class Form {
 	@NumberFormat(pattern = "###,###")
	private Integer number;
    
 	@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
 	private LocalDateTime localDateTime;
}

 

Controller에서 View템플릿으로 Model을 넘겨 확인한 결과를 보면 다음과 같이 작성한 포맷 패턴이 적용된 것을 볼 수 있습니다.


타임리프에서 변환된 결과보기

타임리프는 ${{...}} 를 사용하면 자동으로 컨버전 서비스를 사용해서 변환된 결과를 출력해 줍니다.

 

위 결과를 보면 변수 표현식 ${...}을 사용한 결과는 변환되기 전 값을 보여주고

${{...} 사용한 결과는 변환된 값을 출력해주는 것을 볼 수 있습니다.