Back-End (Web)/Spring

TypeConverter

JABHACK 2025. 1. 10. 11:10

TypeConverter

📌 Spring에서 객체의 타입을 서로 변환하는 데 사용되는 인터페이스로 Spring의 데이터 바인딩 과정에서 문자열을 특정 객체로 변환하거나 하나의 객체 타입을 다른 타입으로 변환할 때 사용한다.

  • 문자를 숫자로, 숫자를 문자로 변환하는 등 Web Application을 만들다보면 Type을 변환해야 하는 경우가 많이 발생한다.

 

  • 결론
    1. 요청 파라미터로 전달하는 10 값은 실제로는 문자열(String) 10이다.
    2. @RequestParam을 사용하면 문자 10을 Integer 타입의 숫자 10으로 변환된다.
    3. @ModelAttribute, @PathVariable 에서도 타입 변환을 확인할 수 있다.
    • Spring 내부에서 누군가가 타입을 자동으로 변환한다.

 

Converter Interface

  • Spring이 제공하는 인터페이스
    • implements하여 Converter로 등록하면 된다.
  • Converter는 모든 타입(T)에 적용할 수 있다.
  • 개발자가 새로운 Type을 만들어서 사용할 수 있도록 만든다.
    • 변환하고자 하는 타입에 맞춰서 Type Converter를 구현하고 등록하면 된다.

 

Converter

📌 데이터 타입 간 변환을 처리하는 인터페이스 , 주로 웹 요청 파라미터를 Java 객체로 변환하거나 그 반대로 변환할 때 사용되며 커스텀 변환 로직을 정의할 수 있다.

 

  • 주의점
    • org.springframework.core.convert.converter
    • Spring의 Converter와 같은 이름을 가진 Interface가 많으니 주의해야 한다.

 

 

  • 코드 예시
    • String → Integer
    • Converter<S, T> 에서 S는 변환할 Source T는 변환할 Type으로 설정하면 된다.
@Slf4j
public class StringToIntegerConverter implements Converter<String, Integer> {
	
	@Override
	public Integer convert(String source) {
		log.info("source = {}", source);
		// 검증
		return Integer.valueOf(source);
	}
	
}
  • 파라미터로 들어온 source가 Interger로 변환된다.
  • Integer → String
@Slf4j
public class IntegerToStringConverter implements Converter<Integer, String> {
	
	@Override
	public String convert(Integer source) {
		log.info("source = {}", source);
		return String.valueOf(source);
	}
}
  • 파라미터로 들어온 source가 String으로 변환된다.
  • String → Person
@Getter
public class Person {
	
		// 이름
		private String name;
		// 나이
		private int age;
	
		public Person(String name, int age) {
			this.name = name;
			this.age = age;
		}
	
}
  • 요청 예시
    • localhost:8080/type-converter?person=wonuk:1200
public class StringToPersonConverter implements Converter<String, Person> {
		// source = "wonuk:1200"
		@Override
		public Person convert(String source) {
			// ':' 를 구분자로 나누어 배열로 만든다.
			String[] parts = source.split(":");
	
			// 첫번째 배열은 이름이다. -> wonuk
	    String name = parts[0];
	    // 두번째 배열은 개월수이다. -> 1200
	    int months = Integer.parseInt(parts[1]);
	    
			// 개월수 나누기 12로 나이를 구하는 로직 (12개월 단위만 고려)
			int age = months / 12;
	
			return new Person(name, age);
		}
}
public class PersonToStringConverter implements Converter<Person, String> {
		
		@Override
		public String convert(Person source) {
				// 이름
				String name = source.getName();
				// 개월수
				int months = source.getAge * 12;
				// "wonuk:1200"
				return name + ":" + months;
		}
	
}

 

  • TypeConverter 사용
    • 구현은 단순하게 직접 메서드를 구현하여 모듈화 하면된다.
    • TypeConverter 를 생성하여 직접 사용하면 컨트롤러에서 변환하는 방식과 큰 차이가 없다.
PersonToStringConverter converter = new PersonToStringConverter();
String source = "wonuk:1200";
converter.convert(source);

 

  • Converter를 편리하게 등록하고 사용할 수 있도록 만들어주는 기능이 필요하다.
  • Spring은 String, Integer, Enum등 자주 사용되는 타입에 대한 컨버터를 제공하고 사용할 수 있도록 등록되어 있다.

 

 

Spring의 다양한 Converter

📌 Spring에서 제공하는 다양한 Converter 인터페이스가 존재하며 이들은 Spring의 데이터 바인딩, 요청/응답 처리, 속성 값 주입 등에 사용되고 ConversionService를 통해 등록 및 관리된다.

 

  1. Converter
    • 기본적인 변환을 담당하는 인터페이스
    • 단일 타입에서 단일 타입으로 변환할 때 사용한다.
      • Converter<Source, Type>
  2. ConverterFactory
    • 클래스 계층 구조가 복잡한 경우 사용
    • 기본 타입과 다양한 서브 타입 간의 변환을 지원한다.
  3. GenericConverter
    • 다양한 타입 간의 유연한 변환을 지원한다.
    • 복잡한 타입 변환 로직을 구현할 때 유리하다.
  4. ConditionalGenericConverter
    • GenericConverter 의 확장형으로 특정 조건에서만 타입 변환을 수행한다.
    • 추가적으로 matches() 를 통해 변환 가능 여부를 판단할 수 있다.

 

ConversionService

📌 Spring은 Converter를 모아서 편리하게 관리하고 사용할 수 있게 해주는 기능을 제공한다. 이것이 Conversion Service 이다.

 

ConversionService 인터페이스

 

  1. canConvert()
    • Convert 가능 여부를 확인하는 기능
  2. convert()
    • 실제 변환하는 기능

 

 

DefaultConversionService

📌 Spring의 표준 ConversionService로 기본 제공 Converter와 확장 가능성을 통해 다양한 타입 변환을 유연하게 처리할 수 있도록 지원한다.

 

  • DefaultConversionService
    • ConversionService를 구현한 구현체

 

ConvertRegistry에 다양한 Converter를 등록한다.

 

 

 

  • ConverterRegistry
    • Converter를 등록하고 관리하는 기능을 제공한다.

import static org.assertj.core.api.Assertions.*;

public class ConversionServiceTest {

    @Test
    void defaultConversionService() {
        // given
        DefaultConversionService dcs = new DefaultConversionService();
        dcs.addConverter(new StringToPersonConverter());
        Person wonuk = new Person("wonuk", 100);
        
				// when
        Person stringToPerson = dcs.convert("wonuk:1200", Person.class);
        
				// then
        assertThat(stringToPerson.getName()).isEqualTo(wonuk.getName());
        assertThat(stringToPerson.getAge()).isEqualTo(wonuk.getAge());
    }

}
  • 컨버터를 사용할 때는 종류를 몰라도된다.
  • 컨버터는 ConversionService 내부에서 숨겨진채 제공된다.
    • 반환 타입, 파라미터 타입, 제네릭 등으로 ConversionService가 컨버터를 찾는다.
  • 즉, 클라이언트는 ConversionService 인터페이스만 의존하면 된다.
    • 컨버터 등록과 사용의 분리

 

ISP(인터페이스 분리 원칙, Interface Segregation Principal)

📌 클라이언트가 자신이 사용하지 않는 메서드에 의존하지 않도록 인터페이스를 분리하는 원칙

  • DefaultConversionService
    1. ConversionRegistry : 컨버터 등록
    2. ConversionService ****: ****컨버터 사용
  • 인터페이스를 분리하면 컨버터를 사용하는 클라이언트는 필요한 메서드만 알면된다.
  • ConversionRegistry 가 변경되어도 ConversionService와 연관이 없다.
  • Spring은 내부적으로 위와같이 등록, 사용이 분리된 인터페이스들이 아주 많다.
Spring은 내부적으로 ConversionService를 사용해 타입을 변환한다. 대표적으로 @RequestParam , @PathVariable, @ModelAttribute 등이 해당 기능을 사용한다.

 

Converter 요약

Spring의 **Converter**는 데이터 타입 간 변환을 처리하는 인터페이스입니다. 주로 Spring의 데이터 바인딩 또는 사용자 정의 변환 작업에 사용됩니다. 간단한 입력 값 변환에서부터 복잡한 객체 변환까지 유연하게 지원합니다.


Converter의 주요 특징

  1. 데이터 타입 변환:
    • 소스 타입(Source Type)에서 대상 타입(Target Type)으로 변환.
    • 예: String → Integer, String → LocalDate.
  2. 간결한 인터페이스:
    • 구현이 간단하며 특정 변환 작업에 집중.
  3. 범용 사용 가능:
    • Spring의 데이터 바인딩, 요청 파라미터 변환, 커스텀 변환 로직 등에 사용.
  4. 확장 가능:
    • Spring이 기본으로 제공하는 변환기 외에도 사용자 정의 변환기를 구현할 수 있음.

Converter 인터페이스

public interface Converter<S, T> {
    T convert(S source);
}
  • S: 소스 데이터 타입 (변환 전 데이터 타입).
  • T: 대상 데이터 타입 (변환 후 데이터 타입).

Converter의 사용 사례

1. 문자열을 날짜로 변환 (String → LocalDate)

public class StringToLocalDateConverter implements Converter<String, LocalDate> {

    @Override
    public LocalDate convert(String source) {
        // 문자열을 LocalDate로 변환
        return LocalDate.parse(source, DateTimeFormatter.ISO_DATE);
    }
}

Spring에서 Converter 등록

Spring에서는 변환기를 전역적으로 등록하거나, 특정 컨텍스트에서 사용할 수 있습니다.

1. 전역 등록

@Configuration
public class WebConfig implements WebMvcConfigurer {

    @Override
    public void addFormatters(FormatterRegistry registry) {
        // 커스텀 Converter 등록
        registry.addConverter(new StringToLocalDateConverter());
    }
}

Spring의 기본 Converter

Spring은 이미 다양한 기본 변환기를 제공합니다:

  • Primitive 타입 변환: String → int, String → double 등.
  • Java 날짜/시간 변환: String → LocalDate, String → LocalDateTime.
  • Enum 변환: String → Enum.

Converter와 일상적인 비유

비유: 데이터를 변환하는 "도구 상자"

  • Converter는 한 가지 타입의 데이터를 다른 타입으로 변환하는 간단한 도구입니다.
  • 예를 들어, 숫자 데이터를 텍스트로 변환하는 계산기처럼, Converter는 입력 값을 원하는 형태로 만들어줍니다.

Converter 사용 예제

컨트롤러에서 사용

@RestController
@RequestMapping("/api")
public class ExampleController {

    @GetMapping("/date")
    public String getDate(@RequestParam String date, Converter<String, LocalDate> converter) {
        LocalDate localDate = converter.convert(date);
        return "Converted date: " + localDate.toString();
    }
}

요청 예

GET /api/date?date=2023-12-25

결과

Converted date: 2023-12-25

Converter와 관련된 확장

  1. Formatter:
    • Converter의 확장형으로, 데이터를 특정 형식(포맷)으로 변환하고 역변환도 지원.
    • 예: 날짜를 특정 형식으로 변환하거나 파싱.
  2. GenericConverter:
    • 보다 범용적인 변환을 지원하는 고급 형태의 Converter.
  3. ConversionService:
    • 여러 변환기를 한데 모아 관리하고, 필요한 변환기를 자동으로 찾아주는 Spring의 변환 관리 서비스.

Converter와 HttpMessageConverter의 차이

  • Converter:
    • 데이터 타입 간 변환.
    • String → Integer, String → LocalDate 등 간단한 데이터 변환에 초점.
  • HttpMessageConverter:
    • HTTP 요청/응답 데이터를 변환.
    • JSON → Java 객체, Java 객체 → JSON 등, 네트워크 통신에 초점.

결론

Spring의 Converter는 데이터 타입을 변환하는 단순하고 강력한 도구입니다. 데이터를 변환하는 간단한 작업부터, 프로젝트 전반에서 데이터 바인딩에 활용됩니다.

비유: 필요한 데이터를 원하는 형태로 "가공"하는 변환기입니다.

 

 

'Back-End (Web) > Spring' 카테고리의 다른 글

API 예외처리  (0) 2025.01.19
Formatter  (0) 2025.01.11
HttpMessageConverter  (0) 2025.01.09
ArgumentResolver  (0) 2025.01.08
[Spring] 스프링 정리  (0) 2025.01.07