ํ์ ์ปจ๋ฒํฐ(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>
'๐ Backend > MVC Pattern' ์นดํ ๊ณ ๋ฆฌ์ ๋ค๋ฅธ ๊ธ
์๋ธ๋ฆฟ์ ์ด์ฉํ HTML Form ํ์ผ ์ ๋ก๋ (0) | 2023.03.30 |
---|---|
ํฌ๋งทํฐ(Formatter) (0) | 2023.03.29 |
API์์์ ์์ธ ์ฒ๋ฆฌ (0) | 2023.03.28 |
Spring - ์๋ธ๋ฆฟ ์์ธ ์ฒ๋ฆฌ ( Exception ) (0) | 2023.03.28 |
ArgumentResolver ํ์ฉ (0) | 2023.03.24 |
๋ธ๋ก๊ทธ์ ์ ๋ณด
Study Repository
rlaehddnd0422