스프링 타입 컨버터

slee2·2022년 2월 24일
0

프로젝트 생성

생략

소개

문자를 숫자로 변환하거나, 반대로 숫자를 문자로 변환해야 하는 것 처럼 어플리케이션을 개발하다 보면 타입을 변환해야 하는 경우가 상당히 많다.

위의 경우 파라미터를 받을때 문자로 받는 것을 확인할 수 있다.
따라서 요청 파라미터를 자바에서 다른타입으로 변환해서 사용하고 싶다면, 위와 같이 숫자 타입으로 변환하는 과정을 거쳐야 한다.

이렇게 @RequestParam을 사용하면 Integer 타입으로 받을 수 있는데,
그 이유는 @RequestParam이 스프링 내부에서 문자타입을 숫자타입으로 바꿔주었기 때문이다.
즉, 처음에 받는
localhost:8080/hello-v2?data=123에서
123은 문자이다.

이러한 예시는 @ModelAttribute, @PathVariable에서도 확인할 수 있다.

스프링의 타입 변환 적용 예

  • 스프링 MVC 요청 파라미터 : @RequestParam, @ModelAttribute, @PathVariable
  • @Value 등으로 YML 정보 읽기
  • XML에 넣은 스프링 빈 정보를 변환
  • 뷰를 렌더링 할 때

이렇듯, 타입을 변환해야 하는 경우는 상당히 많다. 개발자가 직접 하나하나 타입 변환을 해야 한다면, 정말 많은 작업을 해야한다.
스프링이 중간에 타입 변환기를 사용해서 String -> Integer로 변환해주었기 때문에 개발자는 편리하게 해당 타입을 바로 받을 수 있다. 앞에서는 문자를 숫자로 변경하는 예시를 들었지만, 반대로 숫자를 문자로 변경하는 것도 가능하고, Boolean 타입을 숫자로 변경하는 것도 가능하다.

만약 개발자가 새로운 타입을 만들어서 변환하고 싶으면 어떻게 하면 될까?

컨버터 인터페이스

package org.springframework.core.convert.converter;

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

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

Converter

문자를 숫자로

숫자를 문자로

테스트

새로운 자바 클래스 IpPort

String을 IpPort로

IpPort를 String으로

테스트

그런데 이를 위와 같이 사용하기 위해서는 해당 컨버터 클래스를 Autowired로 등록하고 넣는 것은 직접 컨버팅하는 것과 큰 차이가 없다.
이를 좀 더 편리하게 사용할 수 있는 뭔가가 필요하다.

참고

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

Converter -> 기본 타입 컨버터
ConverterFactory -> 전체 클래스 계층 구조가 필요할 때
GenericConverter -> 정교한 구현, 대상 필드의 어노테이션 정보 사용 기능
ConditionalGenericConverter -> 특정 조건이 참인 경우에만 실행

컨버전 서비스 - ConversionService

DefaultConversionServiceConversionService 인터페이스를 구현한 것이고, 추가로 컨버터를 등록하는 기능도 제공한다.

등록과 사용을 분리

컨버터를 등록할 때는 StringToIntegerConverter같은 타입 컨버터를 명확하게 알아야 한다. 반면에 컨버터를 사용하는 입장에서는 타입 컨버터를 몰라도 된다.
그러니, 사용할때는 그냥 conversionService를 통해서 그냥 사용하면 된다.
물론 autowired를 통한 의존관계 주입을 하여 사용해야한다.

인터페이스 분리 원칙 - ISP(Interface Segregation Principal)
인터페이스 분리 원칙은 클라이언트가 자신이 이용하지 않는 메서드에 의존하지 않아야 한다.

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

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

이렇게 인터페이스를 분리하면 컨버터를 사용하는 클라이언트와 컨버터를 등록하고 관리하는 클라이언트의 관심사를 명확하게 분리할 수 있다.
특히 컨버터를 사용하는 클라이언트는 ConvertService만 의존하면 되므로, 컨버터를 어떻게 등록하고 관리하는지는 전혀 몰라도 된다.
이렇게 인터페이스를 분리한느 것을 ISP라고 한다.

스프링에 Converter 적용하기

스프링은 내부에서 ConversionService를 제공한다.
WebMvcConfigurer가 제공하는 addFormatters()를 사용해서 추가하고 싶은 컨버터를 등록하면 된다.

실행하면 위의 /hello-v2를 했을때, StringToIntegerConverter가 호출되는 것을 확인할 수 있다.

그런데 사실 이 컨버터가 없을때도 잘 실행이 된다.
그 이유는 스프링이 내부에서 수 많은 기본 컨버터들을 제공하기 때문이다.

컨버터를 추가하면 추가한 컨버터가 기본 컨버터보다 높은 우선순위를 가지게 된다.

이번에는 스프링이 처리할 수 없는 ipPort를 넣어보자.

http://localhost:8080/ip-port?ipPort=127.0.0.1:8080
로 들어갔을때, IpPort가 컨버터를 통해 분리되는 것을 확인할 수 있다.

처리 과정

@RequestPara@RequestParam을 처리하는 ArgumentResolverRequestParamMethodArgumentResolver에서 ConversionService를 사용해서 타입을 변환한다.
부모 클래스와 다양한 외부 클래스를 호출하는 등 복잡한 내부 과정을 거치기 때문에 대략 이렇게 처리되는 것으로 이해해도 충분하다.

더 깊이있게 확인하고 싶으면 IpPortConvert에 디버그 브레이크 포인트를 걸어서 확인해보자.

디버거 브레이크는 위 사진과 같이 빨간색 동그라미가 나오도록 클락하고 오른쪽 위에 벌레모양 버튼을 통해 디버그 모드로 실행하면 된다.

0개의 댓글