# ํƒ€์ž… ์ปจ๋ฒ„ํ„ฐ(Type Converter)
Study Repository

ํƒ€์ž… ์ปจ๋ฒ„ํ„ฐ(Type Converter)

by rlaehddnd0422

์Šคํ”„๋ง ํƒ€์ž… ์ปจ๋ฒ„ํ„ฐ

์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์„ ๊ฐœ๋ฐœํ•˜๋‹ค ๋ณด๋ฉด, ๋ฌธ์ž๋ฅผ ์ˆซ์ž๋กœ ๋ณ€ํ™˜ํ•˜๊ฑฐ๋‚˜ ์ˆซ์ž๋ฅผ ๋ฌธ์ž๋กœ ๋ณ€ํ™˜ํ•˜๋Š” ๊ฒƒ์ฒ˜๋Ÿผ ํƒ€์ž…์„ ๋ณ€ํ™˜ํ•ด์•ผ ํ•˜๋Š” ๊ฒฝ์šฐ๊ฐ€ ์ƒ๋‹นํžˆ ๋งŽ์Šต๋‹ˆ๋‹ค.

@GetMapping("/hello-v1")
public String helloV1(@RequestParam Integer data)
{
    System.out.println("data = " + data);
    return "Ok";
}

์Šคํ”„๋ง์€ ๊ธฐ๋ณธ์ ์œผ๋กœ ๋ฌธ์ž -> ์ˆซ์ž(String To Integer) , ์ˆซ์ž -> ๋ฌธ์ž (Long,Int .. To String) ๋ณ€ํ™˜์˜ ๊ฒฝ์šฐ์—์„œ๋Š” ์ž๋™์ ์œผ๋กœ ๋ณ€ํ™˜ํ•ด ์ฃผ์ง€๋งŒ, ๊ฐœ๋ฐœ์ž๊ฐ€ ์ƒˆ๋กœ ๋งŒ๋“  ํƒ€์ž…์„ String์œผ๋กœ ๋ณ€ํ™˜ํ•œ๋‹ค๋˜์ง€, String์„ ์ƒˆ๋กœ๋งŒ๋“  ํƒ€์ž…์œผ๋กœ ๋ณ€ํ™˜ํ•˜๊ณ  ์‹ถ์€ ๊ฒฝ์šฐ์— ์ œ๊ณตํ•˜๋Š” ๋„๊ตฌ๊ฐ€ ๋ฐ”๋กœ ์ปจ๋ฒ„ํ„ฐ์ž…๋‹ˆ๋‹ค.

 

์ปจ๋ฒ„ํ„ฐ ์ธํ„ฐํŽ˜์ด์Šค๋Š” ์ด๋ ‡์Šต๋‹ˆ๋‹ค.

@FunctionalInterface
public interface Converter<S, T> {

   /**
    * Convert the source object of type {@code S} to target type {@code T}.
    * @param source the source object to convert, which must be an instance of {@code S} (never {@code null})
    * @return the converted object, which must be an instance of {@code T} (potentially {@code null})
    * @throws IllegalArgumentException if the source cannot be converted to the desired target type
    */
   @Nullable
   T convert(S source);

   /**
    * Construct a composed {@link Converter} that first applies this {@link Converter}
    * to its input, and then applies the {@code after} {@link Converter} to the
    * result.
    * @param after the {@link Converter} to apply after this {@link Converter}
    * is applied
    * @param <U> the type of output of both the {@code after} {@link Converter}
    * and the composed {@link Converter}
    * @return a composed {@link Converter} that first applies this {@link Converter}
    * and then applies the {@code after} {@link Converter}
    * @since 5.3
    */
   default <U> Converter<S, U> andThen(Converter<? super T, ? extends U> after) {
      Assert.notNull(after, "After Converter must not be null");
      return (S s) -> {
         T initialResult = convert(s);
         return (initialResult != null ? after.convert(initialResult) : null);
      };
   }

}
  • ์Šคํ”„๋ง์€ ํ™•์žฅ ๊ฐ€๋Šฅํ•œ ์ปจ๋ฒ„ํ„ฐ ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค.
  • ๊ฐœ๋ฐœ์ž๋Š” ์Šคํ”„๋ง์— ์ถ”๊ฐ€์ ์ธ ํƒ€์ž… ๋ณ€ํ™˜์ด ํ•„์š”ํ•˜๋ฉด ์ด ์ปจ๋ฒ„ํ„ฐ ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ๊ตฌํ˜„ ๋ฐ ์Šคํ”„๋ง ๋ถ€ํŠธ์— ๋“ฑ๋กํ•˜๋ฉด ๋ฉ๋‹ˆ๋‹ค.
  • <S,T> ์—์„œ S๋Š” ๋Œ€์ƒ ํƒ€์ž…, T๋Š” ๋ณ€ํ™˜ํ•  ํƒ€์ž…
  • ํƒ€์ž…๋ณ€ํ™˜ ๋กœ์ง์„ convert ๋ฉ”์†Œ๋“œ์— ๊ตฌํ˜„ํ•˜๋ฉด ๋ฉ๋‹ˆ๋‹ค.

ex) Integer -> String

@Slf4j
public class IntegerToStringConverter implements Converter<Integer,String> {

    @Override
    public String convert(Integer source) {
        log.info("convert source = {}",source);
        return String.valueOf(source);
    }
}

๋ฌธ์ž๋ฅผ ์ˆซ์ž๋กœ, ์ˆซ์ž๋ฅผ ๋ฌธ์ž๋กœ ๋ณ€ํ™˜ํ•ด์ฃผ๋Š” ์ปจ๋ฒ„ํ„ฐ๋Š” ์‚ฌ์‹ค ์Šคํ”„๋ง์ด ๊ธฐ๋ณธ์ ์œผ๋กœ ์ œ๊ณตํ•˜๋Š” ์ปจ๋ฒ„ํ„ฐ๋กœ ์ด๋ฏธ ๋“ฑ๋ก๋˜์–ด ์žˆ์Šต๋‹ˆ๋‹ค.

ํ•˜์ง€๋งŒ ์ถ”๊ฐ€ํ•œ ์ปจ๋ฒ„ํ„ฐ๊ฐ€ ๊ธฐ๋ณธ ์ปจ๋ฒ„ํ„ฐ๋ณด๋‹ค ๋†’์€ ์šฐ์„ ์ˆœ์œ„๋ฅผ ๊ฐ€์ง€๊ธฐ ๋•Œ๋ฌธ์— ๋งŒ์•ฝ ์ด ์ปจ๋ฒ„ํ„ฐ๋ฅผ ๋“ฑ๋กํ•ด์„œ ์‚ฌ์šฉํ•˜๋ฉด ์ด ์ปจ๋ฒ„ํ„ฐ๊ฐ€ ์ ์šฉ๋ฉ๋‹ˆ๋‹ค.

 

์ด๋ฒˆ์—๋Š” ์‚ฌ์šฉ์ž ์ •์˜ ํƒ€์ž…์„ String์œผ๋กœ ๋ณ€ํ™˜ํ•˜๋Š” ์ปจ๋ฒ„ํ„ฐ๋ฅผ ๋งŒ๋“ค์–ด๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค.

IpPort( ์‚ฌ์šฉ์ž ์ •์˜ ํƒ€์ž… ) -> String

public class IpPort {

    private String ip;
    private int port;

    public IpPort(String ip, int port) {
        this.ip = ip;
        this.port = port;
    }
}
@Slf4j
public class IpPortToStringConverter implements Converter<IpPort,String> {
    @Override
    public String convert(IpPort source) {
        log.info("convert source = {}", source);
        return source.getIp() + ":" + String.valueOf(source.getPort());
    }
}

S : IpPort โ–ถ๏ธŽ T : String

๋ฐ˜๋Œ€๋กœ String์„ IpPort๋กœ ๋ฐ”๊ฟ€์ˆ˜๋„ ์žˆ์Šต๋‹ˆ๋‹ค. 

String -> IpPort(์‚ฌ์šฉ์ž ์ •์˜ ํƒ€์ž…)

@Slf4j
public class StringToIpPortConverter implements Converter<String, IpPort> {

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

        return new IpPort(ip,port);
    }
}

์ด๋ ‡๊ฒŒ ์‚ฌ์šฉ์ž๊ฐ€ ์ปจ๋ฒ„ํ„ฐ ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ๊ตฌํ˜„ํ•ด ์‚ฌ์šฉ์ž๊ฐ€ ์ •์˜ํ•œ ํƒ€์ž…์„ ์ปจ๋ฒ„ํŒ…ํ•ด๋ณด์•˜์Šต๋‹ˆ๋‹ค.

Conversion Service 

์Šคํ”„๋ง์€ ์œ„์—์„œ ๋งŒ๋“  ํƒ€์ž… ์ปจ๋ฒ„ํ„ฐ๋ฅผ ๋“ฑ๋กํ•˜๊ณ  ๊ด€๋ฆฌํ•˜๋Š” ๊ธฐ๋Šฅ์„ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค. โ–ถ๏ธŽ ConversionService

public interface ConversionService {

  	boolean canConvert(@Nullable Class<?> sourceType, Class<?> targetType);

  	boolean canConvert(@Nullable TypeDescriptor sourceType, TypeDescriptor targetType);

	@Nullable
	<T> T convert(@Nullable Object source, Class<T> targetType);

	@Nullable
	Object convert(@Nullable Object source, @Nullable TypeDescriptor sourceType, TypeDescriptor targetType);

}
  • ConversionService ์ธํ„ฐํŽ˜์ด์Šค๋Š” ๋‹จ์ˆœํžˆ canConvert๋ฅผ ํ†ตํ•ด ์ปจ๋ฒ„ํŒ…์ด ๊ฐ€๋Šฅํ•œ์ง€ ํ™•์ธํ•˜๋Š” ๊ธฐ๋Šฅ๊ณผ convert๋ฅผ ํ†ตํ•ด ์ปจ๋ฒ„ํŒ…ํ•˜๋Š” ๊ธฐ๋Šฅ์„ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค.
  • ConversionServcie ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ๊ตฌํ˜„ํ•œ DefaultFormattingConversionService๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ์ปจ๋ฒ„ํ„ฐ๋ฅผ ๋“ฑ๋กํ•˜๊ณ  ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

 

ConverterRegistry - ์ปจ๋ฒ„ํ„ฐ ๋“ฑ๋ก ์ธํ„ฐํŽ˜์ด์Šค
ConversionService - ์ปจ๋ฒ„ํ„ฐ ์‚ฌ์šฉ ์ธํ„ฐํŽ˜์ด์Šค 
์ธํ„ฐํŽ˜์ด์Šค ๋ถ„๋ฆฌ ์›์น™ (ISP)๋ฅผ ์ ์šฉํ•œ ๊ฒƒ์„ ๋ณผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์Šคํ”„๋ง์€ ๋‚ด๋ถ€์—์„œ ConversionService๋ฅผ ์‚ฌ์šฉํ•ด์„œ ํƒ€์ž…์„ ๋ณ€ํ™˜ํ•ฉ๋‹ˆ๋‹ค. 

๋งŒ๋“  Converter๋ฅผ ์Šคํ”„๋ง์— ์ ์šฉํ•˜๋ ค๋ฉด WevMvcConfigurer๊ฐ€ ์ œ๊ณตํ•˜๋Š” addFormatters()๋ฅผ ์‚ฌ์šฉํ•ด ์ถ”๊ฐ€ํ•˜๊ณ  ์‹ถ์€ ์ปจ๋ฒ„ํ„ฐ๋ฅผ ConversionService์— ์ถ”๊ฐ€ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์›น ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์— ์ปจ๋ฒ„ํ„ฐ ์ ์šฉํ•˜๋Š” ๋ฐฉ๋ฒ•
WebMvcConfigurer ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ๊ตฌํ˜„ํ•œ ๊ตฌํ˜„์ฒด์— addFormatters ๋ฉ”์†Œ๋“œ๋ฅผ ์˜ค๋ฒ„๋ผ์ด๋”ฉํ•ด registry.addConverter(new ์ ์šฉํ•  ์ปจ๋ฒ„ํ„ฐ()); ๋กœ ์›น ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์— ๋งŒ๋“  ์ปจ๋ฒ„ํ„ฐ๋ฅผ ์ ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
@Configuration
public class WebConfig implements WebMvcConfigurer {

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

}

- String To Integer, Integer To String์€ ๊ธฐ์กด์— ์Šคํ”„๋ง๋ถ€ํŠธ์— ์žˆ๋˜ ์ปจ๋ฒ„ํ„ฐ๋ฅผ ๋ฌด์‹œํ•˜๊ณ  ์šฐ์„ ์ˆœ์œ„๋กœ ์ ์šฉ

 

String To IpPort

@GetMapping("/ip-port")
public String ipPort(@RequestParam IpPort ipPort) {
    System.out.println("ipPort IP = " + ipPort.getIp());
    System.out.println("ipPort PORT = " + ipPort.getPort());
    return "ok";
}

http://localhost:8080/ip-port?ipPort=127.0.0.1:8080

ํŒŒ๋ผ๋ฏธํ„ฐ๋กœ ์ž…๋ ฅ๋ฐ›์€ 127.0.0.1:8080 String์ด ๊ฐ์ฒด ํƒ€์ž…์œผ๋กœ ๋ณ€ํ™˜๋œ ๊ฒƒ์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

Thymeleaf ๋ทฐ ํ…œํ”Œ๋ฆฟ์— ์ปจ๋ฒ„ํ„ฐ ์ ์šฉํ•˜๋Š” ๋ฐฉ๋ฒ•
${{...}}๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ์ž๋™์œผ๋กœ ConversionService๋ฅผ ์‚ฌ์šฉํ•ด์„œ ๋ณ€ํ™˜๋œ ๊ฒฐ๊ณผ๋ฅผ ๋ณด์—ฌ์ค๋‹ˆ๋‹ค.
๋ณ€์ˆ˜ ํ‘œํ˜„์‹ : ${...}
์ปจ๋ฒ„์ „ ์„œ๋น„์Šค ์ ์šฉํ•œ ๋ณ€์ˆ˜ ํ‘œํ˜„์‹ : ${...}
<li>${{ipPort}}: <span th:text="${{ipPort}}" ></span></li>
ํผ์— ์ปจ๋ฒ„ํ„ฐ ์ ์šฉํ•˜๋Š” ๋ฐฉ๋ฒ•
th:field๋กœ ์ง€์ •ํ•˜๋ฉด id, name๋„ ๋™์ผํ•˜๊ฒŒ ์ง€์ •ํ•ด์ค„ ๋ฟ๋งŒ ์•„๋‹ˆ๋ผ ์ปจ๋ฒ„์ „ ์„œ๋น„์Šค๋„ ์ ์šฉ๋ฉ๋‹ˆ๋‹ค. 

๋ Œ๋”๋ง ์ „ 

<form th:object="${form}" th:method="post">
    th:field <input type="text" th:field="*{ipPort}"><br/>
    th:value <input type="text" th:value="*{ipPort}">(๋ณด์—ฌ์ฃผ๊ธฐ ์šฉ๋„)<br/> <input type="submit"/>
</form>

๋ Œ๋”๋ง ํ›„ ์†Œ์Šค์ฝ”๋“œ

<form method="post">
    th:field <input type="text" id="ipPort" name="ipPort" value="127.0.0.1:8080"><br/>
    th:value <input type="text" value="hello.typeconverter.type.IpPort@59cb0946">(๋ณด์—ฌ์ฃผ๊ธฐ ์šฉ๋„)<br/> <input type="submit"/>
</form>

 

๋ธ”๋กœ๊ทธ์˜ ์ •๋ณด

Study Repository

rlaehddnd0422

ํ™œ๋™ํ•˜๊ธฐ