์คํ๋ง MVC 2ํธ์ ๊ฐ์ ๋ด์ฉ์ ์ ๋ฆฌํ ํฌ์คํ ์ ๋๋ค.
์คํ๋ง ํ์ ์ปจ๋ฒํฐ
๋ฌธ์๋ฅผ ์ซ์๋ก ๋ณํํ๊ฑฐ๋, ์ซ์๋ฅผ ๋ฌธ์๋ก ๋ณํํด์ผ ํ๋ ๊ฒ์ฒ๋ผ ํ์ ์ ๋ณํํด์ผ ํ๋ ๊ฒฝ์ฐ๊ฐ ๋ง๋ค.
HTTP ์ฟผ๋ฆฌ ์คํธ๋ง์ผ๋ก ์ ๋ฌ๋๋ ๋ฐ์ดํฐ๋ ๋ฌธ์์์๋ ๋ถ๊ตฌํ๊ณ @RequestParam
์ ์ฌ์ฉํ๋ฉด ๋ฌธ์๋ฅผ Integer ํ์
์ผ๋ก๋ ๋ฐ์ ์ ์๋ค. ์ด๊ฒ์ ์คํ๋ง์ด ์ค๊ฐ์์ ๋ฌธ์๋ฅผ ์ซ์๋ก, ํ์
์ ๋ณํํด ์ฃผ์๊ธฐ ๋๋ฌธ์ด๋ค.
๋ฌธ์๋ฅผ ์ซ์๋ก ๋ฐ๊พธ๋ ๊ฒ ์ธ์๋ ์ซ์๋ฅผ ๋ฌธ์๋ก ๋ณ๊ฒฝํ๊ฑฐ๋ Boolean ํ์ ์ ์ซ์๋ก ๋ณ๊ฒฝํ๋ ๊ฒ๋ ๊ฐ๋ฅํ๋ฐ, ์ด๋ ์คํ๋ง์ด ํ์ฅ ๊ฐ๋ฅํ ์ปจ๋ฒํฐ ์ธํฐํ์ด์ค๋ฅผ ์ ๊ณตํ๊ธฐ ๋๋ฌธ์ด๋ค. ์ธํฐํ์ด์ค๋ฅผ ๊ตฌํํด์ ๋ฑ๋กํ๋ฉด ๋๊ธฐ ๋๋ฌธ์ ์๋ก์ด ํ์ ์ ๋ง๋ค์ด์ ๋ณํํ๋ ๊ฒ ๋ํ ๊ฐ๋ฅํ๋ค.
Converter
ํ์ ์ปจ๋ฒํฐ๋ฅผ ์ฌ์ฉํ๊ธฐ ์ํด์๋ Converter ์ธํฐํ์ด์ค๋ฅผ ๊ตฌํํ๋ฉด ๋๋๋ฐ, ๋ค์๊ณผ ๊ฐ์ด ๊ตฌํํ ์ ์๋ค.
๋ฟ๋ง ์๋๋ผ String์ ์ฌ์ฉ์๊ฐ ์ ์ํ ํ์ ์ผ๋ก ๋ณ๊ฒฝํ๋ ๊ฒ๋ Converter<String, T> ํ์์ ์ธํฐํ์ด์ค๋ฅผ ๊ตฌํํ๋ฉด ๋๋ค.
public class IntegerToStringConverter implements Converter<Integer, String> {
@Override
public String convert(Integer source) {
return String.valueOf(source);
}
}
ConversionService
ํ์ ์ปจ๋ฒํฐ๋ง ์ด์ฉํ ๊ฒฝ์ฐ ์ปจ๋ฒํฐ๋ฅผ ํ๋ํ๋ ์ง์ ์ฌ์ฉํด์ผ ํ๋ ๋ถํธํจ์ด ์๋๋ฐ, ์ด๋ ํ์ ์ปจ๋ฒํฐ๋ฅผ ๋ฑ๋กํ๊ณ ๊ด๋ฆฌํ๋ฉด์ ํธ๋ฆฌํ๊ฒ ๋ณํ ๊ธฐ๋ฅ์ ์ ๊ณตํ๋ ์ญํ ์ ํ๋ ๊ฒ์ด ์ปจ๋ฒ์ ์๋น์ค์ด๋ค. ์คํ๋ง์ ์ปจ๋ฒ์ ์๋น์ค๋ฅผ ํตํด ๊ฐ๋ณ ์ปจ๋ฒํฐ๋ฅผ ๋ชจ์๋๊ณ ๋ฌถ์ด์ ํธ๋ฆฌํ๊ฒ ์ฌ์ฉํ ์ ์๋ ๊ธฐ๋ฅ์ ์ ๊ณตํ๋ค.
public interface ConversionService {
//์ปจ๋ฒํ
๊ฐ๋ฅ ์ฌ๋ถ ํ์ธ
boolean canConvert(@Nullable Class<?> sourceType, Class<?> targetType);
boolean canConvert(@Nullable TypeDescriptor sourceType, TypeDescriptor
targetType);
//์ปจ๋ฒํ
๊ธฐ๋ฅ
<T> T convert(@Nullable Object source, Class<T> targetType);
Object convert(@Nullable Object source, @Nullable TypeDescriptor sourceType,
TypeDescriptor targetType);
}
์ปจ๋ฒ์ ์๋น์ค๋ ๋ค์๊ณผ ๊ฐ์ด ํ์ฉํ ์ ์๋ค.
public class ConversionServiceTest {
@Test
void conversionService() {
DefaultConversionService conversionService = new DefaultConversionService();
conversionService.addConverter(new StringToIntegerConverter());
conversionService.addConverter(new IntegerToStringConverter());
conversionService.addConverter(new StringToIpPortConverter());
assertThat(conversionService.convert("10", Integer.class)).isEqualTo(10);
assertThat(conversionService.convert(10, String.class)).isEqualTo("10");
}
}
ConversionService ์ธํฐํ์ด์ค๋ฅผ ๊ตฌํํ DefaultConversionService๋ ์ถ๊ฐ์ ์ผ๋ก ์ปจ๋ฒํฐ๋ฅผ ๋ฑ๋กํ๋ ๊ธฐ๋ฅ๋ ์ ๊ณตํ๋ค.
๋ฑ๋ก๊ณผ ์ฌ์ฉ ๋ถ๋ฆฌ
์ปจ๋ฒํฐ๋ฅผ ๋ฑ๋กํ ๋๋ StringToIntegerConverter์ ๊ฐ์ ํ์
์ปจ๋ฒํฐ๋ฅผ ๋ช
ํํ๊ฒ ์์์ผ ํ์ง๋ง, ์ฌ์ฉํ๋ ์
์ฅ์์๋ ํ์
์ปจ๋ฒํฐ์ ๋ํด ๋ชฐ๋ผ๋ ๋๋ค. ํ์
์ปจ๋ฒํฐ๋ค์ ์ปจ๋ฒ์ ์๋น์ค ๋ด๋ถ์ ์จ์ด์ ๋์ํ๊ธฐ ๋๋ฌธ์ด๋ค.(conversionService.convert()
์ด์ฉ)
๋ฐ๋ผ์ ํ์ ์ ๋ณํํ๋ ์ฌ์ฉ์๋ ์ปจ๋ฒ์ ์๋น์ค ์ธํฐํ์ด์ค์๋ง ์์กดํ๋ฉด ๋๋ค.
์ธํฐํ์ด์ค ๋ถ๋ฆฌ ์์น(ISP)
DefaultConversionService๋ ๋ ๊ฐ์ง ์ธํฐํ์ด์ค๋ฅผ ๊ตฌํํ๋๋ฐ, ์ปจ๋ฒํฐ ์ฌ์ฉ์ ์ด์ ์ ๋ง์ถ ์ ์ปจ๋ฒํฐ ๋ฑ๋ก์ ์ด์ ์ ๋ง์ถ ์ด๋ค.
์ด๋ ๊ฒ ์ธํฐํ์ด์ค๋ฅผ ๋ถ๋ฆฌํจ์ผ๋ก์จ ์ปจ๋ฒํฐ๋ฅผ ์ฌ์ฉํ๋ ์ ์ฅ๊ณผ ์ปจ๋ฒํฐ๋ฅผ ๋ฑ๋กํ๊ณ ๊ด๋ฆฌํ๋ ์ ์ฅ์ ๊ด์ฌ์ฌ๋ฅผ ๋ช ํํ๊ฒ ๋ถ๋ฆฌํ ์ ์๊ธฐ ๋๋ฌธ์ ์ปจ๋ฒํฐ๋ฅผ ์ฌ์ฉํ๋ ์ชฝ์์๋ ์์ ์๊ฒ ํ์ํ ๋ฉ์๋๋ง ์๊ฒ ๋๋ค. ๋ฐ๋ผ์ ํด๋ผ์ด์ธํธ๊ฐ ์์ ์ด ์ด์ฉํ์ง ์๋ ๋ฉ์๋์ ์์กดํ์ง ์๋ ์ธํฐํ์ด์ค ๋ถ๋ฆฌ ์์น์ ๋ง์กฑํ๊ณ ์๋ค.
๋ค๋ง ๋ฉ์์ง ์ปจ๋ฒํฐ(HttpMessageConverter)์๋ ์ปจ๋ฒ์ ์๋น์ค๊ฐ ์ ์ฉ๋์ง ์๋๋ฐ, ํนํ ๊ฐ์ฒด๋ฅผ JSON์ผ๋ก ๋ณํํ๋ ๋ฉ์์ง ์ปจ๋ฒํฐ๋ ๋ด๋ถ์ ์ผ๋ก Jackson ๊ฐ์ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ์ฌ์ฉํ๊ธฐ ๋๋ฌธ์ ๋ณํ ๊ฒฐ๊ณผ ๋ํ ํด๋น ๋ผ์ด๋ธ๋ฌ๋ฆฌ์ ๋ฌ๋ ค์๋ค. ๋ง์ฝ JSON์ผ๋ก ๋ง๋ค์ด์ง๋ ์ซ์๋ ๋ ์ง ํฌ๋งท์ ๋ณ๊ฒฝํ๊ณ ์ถ์ผ๋ฉด ํด๋น ๋ผ์ด๋ธ๋ฌ๋ฆฌ๊ฐ ์ ๊ณตํ๋ ์ค์ ์ ํตํด์ ํฌ๋งท์ ์ง์ ํด์ผ ํ๋ค.
์ปจ๋ฒํฐ ์ ์ฉํ๊ธฐ
์คํ๋ง์ ์ ์ฉ
์๋์ ๊ฐ์ด ์คํ๋ง์ ์ปจ๋ฒํฐ๋ฅผ ์ ์ฉํ ์ ์๋ค.
@Configuration
public class WebConfig implements WebMvcConfigurer {
@Override
public void addFormatters(FormatterRegistry registry) {
registry.addConverter(new StringToIntegerConverter());
registry.addConverter(new IntegerToStringConverter());
}
}
์คํ๋ง์ ๋ด๋ถ์ ์ผ๋ก ์ปจ๋ฒ์ ์๋น์ค๋ฅผ ์ง์ํ๊ธฐ ๋๋ฌธ์ WebMvcConfigurer๊ฐ ์ ๊ณตํ๋ addFormatters()
๋ฅผ ์ด์ฉํด ์ถ๊ฐํ๊ณ ์ถ์ ์ปจ๋ฒํฐ๋ฅผ ๋ฑ๋กํ๋ฉด ๋๋ค. ์ด๋ ๊ฒ ํ๋ฉด ์คํ๋ง์ ๋ด๋ถ์์ ์ฌ์ฉํ๋ ์ปจ๋ฒ์ ์๋น์ค์ ์ปจ๋ฒํฐ๋ฅผ ์ถ๊ฐํด ์ค๋ค.
์ถ๊ฐ์ ์ผ๋ก ์คํ๋ง์ ๋ด๋ถ์์ ์๋ง์ ๊ธฐ๋ณธ ์ปจ๋ฒํฐ๋ค์ด ์ ๊ณต๋๊ธฐ ๋๋ฌธ์ ์ง์ ์ปจ๋ฒํฐ๋ฅผ ๋ฑ๋กํ์ง ์์๋ ๊ธฐ๋ณธ์ ์ธ ํ์ ๋ณํ์ด ๊ฐ๋ฅํ๋ค.
๋ทฐ ํ ํ๋ฆฟ์ ์ ์ฉ
ํ์๋ฆฌํ๋ ${{}}
๋ฅผ ์ฌ์ฉํ๋ฉด ์๋์ผ๋ก ์ปจ๋ฒ์ ์๋น์ค๋ฅผ ์ฌ์ฉํด์ ๋ณํ๋ ๊ฒฐ๊ณผ๋ฅผ ์ถ๋ ฅํด ์ค๋ค. ์ด๋ ์คํ๋ง๊ณผ ํตํฉ๋์ด์ ์คํ๋ง์ด ์ ๊ณตํ๋ ์ปจ๋ฒ์ ์๋น์ค๋ฅผ ์ฌ์ฉํ๋ฏ๋ก, ์ฌ์ฉ์๊ฐ ์ง์ ๋ฑ๋กํ ์ปจ๋ฒํฐ๋ฅผ ์ด์ฉํ ์๋ ์๋ค.
Formatter
์ปจ๋ฒํฐ๋ ์ ์ถ๋ ฅ ํ์ ์ ์ ํ์ด ์๋, ๋ฒ์ฉ ํ์ ๋ณํ ๊ธฐ๋ฅ์ ์ ๊ณตํ๋ค.
ํ์ง๋ง ์ผ๋ฐ์ ์ผ๋ก ๋ฌธ์๋ฅผ ๋ค๋ฅธ ํ์ ์ผ๋ก ๋ณํํ๊ฑฐ๋, ๋ค๋ฅธ ํ์ ์ ๋ฌธ์๋ก ๋ณํํ๋ ์ํฉ์ด ๋ค์๋ฅผ ์ฐจ์งํ๋ค. ์ด๋ ๊ฒ ๊ฐ์ฒด๋ฅผ ํน์ ํ ํ์์ ๋ง์ถ์ด ๋ฌธ์๋ก ์ถ๋ ฅํ๊ฑฐ๋ ๊ทธ ๋ฐ๋์ ์ญํ ์ ํ๋ ๊ฒ์ ํนํ๋ ๊ธฐ๋ฅ์ด ํฌ๋งทํฐ์ด๋ค. ํฌ๋งทํฐ๋ ์ปจ๋ฒํฐ์ ํน๋ณํ ๋ฒ์ ์ด๋ผ๊ณ ํ ์ ์๋ค.
public interface Printer<T> {
String print(T object, Locale locale); //๊ฐ์ฒด๋ฅผ ๋ฌธ์๋ก ๋ณ๊ฒฝ
}
public interface Parser<T> {
T parse(String text, Locale locale) throws ParseException; //๋ฌธ์๋ฅผ ๊ฐ์ฒด๋ก ๋ณ๊ฒฝ
}
public interface Formatter<T> extends Printer<T>, Parser<T> { //๋ ์ธํฐํ์ด์ค๋ฅผ ๊ตฌํํ ํฌ๋งทํฐ
}
์๋์ ๊ฐ์ด ์ซ์ 1000์ ๋ฌธ์ 1,000์ผ๋ก ๋ณํํ๋, ์ผํ๋ฅผ ๋ฃ์ด์ฃผ๋ ํฌ๋งท์ ์ ์ฉํด ๋ณผ ์ ์๋ค.
NumberFormat ๊ฐ์ฒด๋ Locale ์ ๋ณด๋ฅผ ํตํด ๋๋ผ๋ณ๋ก ๋ค๋ฅธ ์ซ์ ํฌ๋งท์ ๋ง๋ค์ด์ค๋ค.
public class MyNumberFormatter implements Formatter<Number> {
@Override
public Number parse(String text, Locale locale) throws ParseException {
NumberFormat format = NumberFormat.getInstance(locale);
return format.parse(text);
}
@Override
public String print(Number object, Locale locale) {
return NumberFormat.getInstance(locale).format(object);
}
}
ํฌ๋งทํฐ๋ฅผ ์ง์ํ๋ ์ปจ๋ฒ์ ์๋น์ค
ConversionService์๋ ์ปจ๋ฒํฐ๋ง ๋ฑ๋กํ ์ ์๊ณ ํฌ๋งทํฐ๋ฅผ ๋ฑ๋กํ ์๋ ์๋๋ฐ, ์๊ฐํด ๋ณด๋ฉด ํฌ๋งทํฐ๋ ๊ฐ์ฒด๋ฅผ ๋ฌธ์๋ก, ๋ฌธ์๋ฅผ ๊ฐ์ฒด๋ก ๋ณํํ๋ ํน๋ณํ ์ปจ๋ฒํฐ์ผ ๋ฟ์ด๋ค. ๋ฐ๋ผ์ ํฌ๋งทํฐ๋ฅผ ์ง์ํ๋ ์ปจ๋ฒ์ ์๋น์ค๋ฅผ ์ฌ์ฉํ๋ฉด ์ปจ๋ฒ์ ์๋น์ค์ ํฌ๋งทํฐ๋ฅผ ์ถ๊ฐํ ์ ์๋ค. ๋ด๋ถ์์ ์ด๋ํฐ ํจํด์ ์ฌ์ฉํด์ ํฌ๋งทํฐ๊ฐ ์ปจ๋ฒํฐ์ฒ๋ผ ๋์ํ๋๋ก ์ง์ํ๋ค.
FormattingConversionService๋ ํฌ๋งทํฐ๋ฅผ ์ง์ํ๋ ์ปจ๋ฒ์ ์๋น์ค์ด๋ค.
DefaultFormattingConversionService๋ FormattingConversionService์ ๊ธฐ๋ณธ์ ์ธ ํตํ, ์ซ์ ๊ด๋ จ ๋ช ๊ฐ์ง ๊ธฐ๋ณธ ํฌ๋งทํฐ๋ฅผ ์ถ๊ฐํด์ ์ ๊ณตํ๋ค.
๋ํ FormattingConversionService๋ ConversionService ๊ด๋ จ ๊ธฐ๋ฅ์ ์์๋ฐ๊ธฐ ๋๋ฌธ์ ๊ฒฐ๊ณผ์ ์ผ๋ก ์ปจ๋ฒํฐ๋ ํฌ๋งทํฐ๋ ๋ชจ๋ ๋ฑ๋กํ ์ ์๋ค. ์ฌ์ฉํ ๋๋ ์ปจ๋ฒ์ ์๋น์ค๊ฐ ์ ๊ณตํ๋ convert๋ฅผ ์ฌ์ฉํ๋ฉด ๋๋ค. ์คํ๋ง ๋ถํธ์ ๊ฒฝ์ฐ DefaultFormattingConversionService๋ฅผ ์์๋ฐ์ WebConversionService๋ฅผ ๋ด๋ถ์ ์ผ๋ก ์ฌ์ฉํ๋ค.
์คํ๋ง์ด ์ ๊ณตํ๋ ๊ธฐ๋ณธ ํฌ๋งทํฐ
์คํ๋ง์ ์๋ฐ์์ ๊ธฐ๋ณธ์ผ๋ก ์ ๊ณตํ๋ ํ์ ์ ๋ํด ๋ง์ ํฌ๋งทํฐ๋ฅผ ๊ธฐ๋ณธ์ผ๋ก ์ ๊ณตํ๋ค. ๊ทธ๋ฐ๋ฐ ํฌ๋งทํฐ๋ ๊ธฐ๋ณธ ํ์์ด ์ง์ ๋์ด ์์ด ๊ฐ์ฒด์ ๊ฐ ํ๋๋ง๋ค ๋ค๋ฅธ ํ์์ผ๋ก ํฌ๋งท์ ์ง์ ํ๊ธฐ๋ ์ด๋ ค์ด๋ฐ, ์ด๋ฌํ ๋ฌธ์ ๋ฅผ ํด๊ฒฐํ๊ธฐ ์ํด ์คํ๋ง์ ์ ๋ ธํ ์ด์ ๊ธฐ๋ฐ์ผ๋ก ์ํ๋ ํ์์ ์ง์ ํด์ ์ฌ์ฉํ ์ ์๋ ์ ์ฉํ ํฌ๋งทํฐ ๋ ๊ฐ์ง๋ฅผ ์ ๊ณตํ๊ณ ์๋ค.
@NumberFormat
- ์ซ์ ๊ด๋ จ ํ์ ์ง์ ํฌ๋งทํฐ
@DateTimeFormat
- ๋ ์ง ๊ด๋ จ ํ์ ์ง์ ํฌ๋งทํฐ
@Data
static class Form {
@NumberFormat(pattern = "###,###")
private Integer number;
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private LocalDateTime localDateTime;
}
'Language & Framework > Spring' ์นดํ ๊ณ ๋ฆฌ์ ๋ค๋ฅธ ๊ธ
[์คํฐ๋][์คํ๋ง ๋ถํธ ํต์ฌ ๊ฐ์ด๋] ์์์ฑ ์ปจํ ์คํธ (0) | 2023.03.15 |
---|---|
[์คํ๋ง MVC] ํ์ผ ์ ๋ก๋ (0) | 2023.03.06 |
[์คํ๋ง MVC] API ์์ธ ์ฒ๋ฆฌ (0) | 2023.03.04 |
[์คํ๋ง MVC] ์์ธ ์ฒ๋ฆฌ์ ์ค๋ฅ ํ์ด์ง (0) | 2023.03.03 |
[์คํ๋ง MVC] ๋ก๊ทธ์ธ ์ฒ๋ฆฌ(2) - ํํฐ, ์ธํฐ์ ํฐ (0) | 2023.03.03 |