Spring Converter

후니팍·2023년 5월 20일
1
post-thumbnail

정의

스프링에서 사용되는 Converter는 데이터 변환을 수행하는 함수형 인터페이스입니다.
주로 Spring의 데이터 바인딩, 폼 처리, 데이터 유효성 검증 등에서 사용됩니다.
소스 객체를 대상 객체로 변환하는 기능을 제공합니다.

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

S 타입의 객체가 input으로 들어오면 T 타입의 객체로 변환해주는 역할을 합니다.


학습 계기

우테코 지하철 미션을 진행하면서 노선에 역을 등록하는 API를 설계하는 것이 미션에 포함되었습니다.

{
    "direction": "UP",
    "standardStationName": "구일",
    "newStationName": "가산디지털단지",
    "distance": 1
}

위는 해당 API의 request body인데요. 노선에 역을 등록할 때 "윗쪽에 추가" 혹은 "밑쪽에 추가"라는 것을 명시해야 했습니다. 그래야 기준역에서 올바른 방향으로 새로운 역이 추가되니까요:)

처음에는 String 형태의 json이 들어오기 때문에 아래와 같이 String 자료형으로 설정해주었습니다.

public class StationRegisterInLineRequest {

    @NotBlank(message = "direction 이 비어있습니다.")
    private final String direction;
    @NotBlank(message = "standardStationName 이 비어있습니다.")
    private final String standardStationName;
    @NotBlank(message = "newStationName 이 비어있습니다.")
    private final String newStationName;
    @NotNull(message = "distance 가 null 입니다.")
    @Positive(message = "거리는 양의 정수만 가능합니다.")
    private final Integer distance;

	...

}

이때, String으로 들어오는 "direction": "UP" 값을 request dto에서 바로 enum으로 설정할 수 있지 않을까 생각했습니다. 아래처럼 말이죠.

public class StationRegisterInLineRequest {

    @NotNull(message = "direction 이 null 입니다.")
    private final SubwayDirection direction;
    @NotBlank(message = "standardStationName 이 비어있습니다.")
    private final String standardStationName;
    @NotBlank(message = "newStationName 이 비어있습니다.")
    private final String newStationName;
    @NotNull(message = "distance 가 null 입니다.")
    @Positive(message = "거리는 양의 정수만 가능합니다.")
    private final Integer distance;

	...

}

Converter 선택

request body의 필드로 enum을 사용하는 방법을 알아보았을 때, FormatterConverter가 주로 쓰인다는 것을 배웠습니다.

  • Formatter

    • Object <-> String 간의 변환을 담당한다.
    • Locale에 따라 다국화하는 기능을 제공한다.
  • Converter

    • Source가 들어오면 Target을 반환한다.
    • Thread-safe 해서 스프링 빈으로 등록해서 사용할 수 있다.

Locale을 지원하는 Formatter는 원하는대로 타입 전환을 할 수 있는 Converter가 현재 서비스에 더 어울린다고 생각했습니다. Converter의 특별한 버전을 Formatter라고 이해했고, Formatter는 문자에 특화된 기능이라고 생각했습니다.

지금 서비스는 객체간의 타입 변환을 목적으로 했기 때문에 Converter를 선택했습니다.


Config 등록

Converter의 구현체를 만들어 String 값을 enum으로 바꾸는 convert 메소드를 구현했습니다.

@Component
public class StringToSubwayDirectionConverter implements Converter<String, SubwayDirection> {

    @Override
    public SubwayDirection convert(final String value) {
        return SubwayDirection.from(value);
    }
}

구현한 Converter를 적용하기 위해 config에 등록해주었습니다.

@Configuration
public class WebConfig implements WebMvcConfigurer {

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

enum에도 따로 설정을 해주어야하는데요. json 형태로 값을 받기 때문에 enum의 생성자에 @JsonCreater로 json 값을 java 코드로 직렬화해야합니다.

public enum SubwayDirection {

    UP("up"), DOWN("down");

    private final String directionName;

    SubwayDirection(final String directionName) {
        this.directionName = directionName;
    }

    @JsonCreator
    public static SubwayDirection from(final String input) {
        return Arrays.stream(SubwayDirection.values())
                .filter(value -> input.equalsIgnoreCase(value.directionName))
                .findFirst()
                .orElseThrow(InvalidDirectionException::new);
    }
}

결과

json 값을 java 코드의 request body에서 enum 타입으로 설정에 성공한 것을 볼 수 있습니다.

잘못된 문자를 입력하면 아래와 같이 설정한 오류를 올바르게 오류도 보여줍니다.


마무리

누군가 알려줘서 찾은 불편이 아닌, 제가 찾은 불편을 스스로 해결해서 뿌듯합니다.

profile
영차영차

1개의 댓글

comment-user-thumbnail
2023년 5월 20일

불편 해결하는 모습이 정말 개발자같네요

답글 달기