문자를 숫자로 변환하거나 숫자를 문자로 변환해야하는 것처럼 애플리케이션을 개발하다 보면 타입을 변환해야 하는 경우가 많다.
타입변경의 예시
@RestController
public class HelloController {
@GetMapping("/hello-v1")
public String helloV1(HttpServletRequest request) {
String data = request.getParameter("data"); //문자 타입 조회
Integer intValue = Integer.valueOf(data); //숫자 타입으로 변경
System.out.println("intValue = " + intValue);
return "ok";
}
}
String data = request.getParameter("data")
HTTP 요청 파라미터는 모두 문자로 처리된다. 따라서 요청 파라미터를 자바에서 다른 타입으로 변환해서 사용하고 싶으면 다음과 같이 숫자로 변환하는 과정을 거쳐야 한다.
스프링이 제공하는 타입변경
@GetMapping("/hello-v2")
public String helloV2(@RequestParam Integer data) {
System.out.println("data = " + data);
return "ok";
}
http://localhost:8080/hello-v2?data=10
HTTP 쿼리 스트링으로 전달하는 data=10
부분에서 10은 숫자가 아닌 문자 10이다. 스프링이 제공하는 @RequestParam
을 사용하면 문자 10을 숫자 10으로 편리하게 받을 수 있다.
스프링과 타입변환
개발자가 직접 하나씩 타입을 변경하는 것은 너무 번거롭다. 하지만 어쩔수 없이 수작업으로 Boolean
타입을 숫자로 받아야 할 때도 있다.
package org.springframework.core.convert.converter;
public interface Converter<S, T> {
T convert(S source);
}
스프링은 확장 가능ㄹ한 컨버터 인터페이스를 제공한다. 개발자는 스프링에 추가적인 타입 변환이 필요하면 이 컨버터 인터페이스를 구현해서 등록하면 된다.
문자를 숫자로 변경하는 Converter
package hello.typeconverter.converter;
import lombok.extern.slf4j.Slf4j;
import org.springframework.core.convert.converter.Converter;
@Slf4j
public class StringToIntegerConverter implements Converter<String, Integer> {
@Override
public Integer convert(String source) {
log.info("convert source={}", source);
return Integer.valueOf(source);
}
}
숫자를 문자로 변환하는 타입 Converter
package hello.typeconverter.converter;
import lombok.extern.slf4j.Slf4j;
import org.springframework.core.convert.converter.Converter;
@Slf4j
public class IntegerToStringConverter implements Converter<Integer, String> {
@Override
public String convert(Integer source) {
log.info("convert source={}", source);
return String.valueOf(source);
}
}
Converter test code 예시
class ConverterTest {
@Test
void stringToInteger() {
StringToIntegerConverter converter = new StringToIntegerConverter();
Integer result = converter.convert("10");
assertThat(result).isEqualTo(10);
}
}
IpPort
package hello.typeconverter.type;
import lombok.EqualsAndHashCode;
import lombok.Getter;
@Getter
@EqualsAndHashCode
public class IpPort {
private String ip;
private int port;
public IpPort(String ip, int port) {
this.ip = ip;
this.port = port;
}
}
@EqualsAndHashCode
를 넣으면 모든 필드를 사용해서 equals(),hashcode()
를 생성한다 따라서 모든 필드의 값이 같다면 a.equals(b)
의 결과가 참이된다.StringToIpPortConverter - 컨버터
package hello.typeconverter.converter;
import hello.typeconverter.type.IpPort;
import lombok.extern.slf4j.Slf4j;
import org.springframework.core.convert.converter.Converter;
@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);
}
}
IpPortToStringConverter
package hello.typeconverter.converter;
import hello.typeconverter.type.IpPort;
import lombok.extern.slf4j.Slf4j;
import org.springframework.core.convert.converter.Converter;
@Slf4j
public class IpPortToStringConverter implements Converter<IpPort, String> {
@Override
public String convert(IpPort source) {
log.info("convert source={}", source);
return source.getIp() + ":" + source.getPort();
}
}
IpPort
객체를 입력하면 127.0.0.1:8080
같은 문자를 반환한다.스프링에서 제공하는 개별 Converter
들을 모아놓고 그것들을 묶어서 편리하게 사용할 수 있는 기능을 제공하는 것을 컨버젼 서비스라한다.
컨버젼 서비스 테스트 코드
public class ConversionServiceTest {
@Test
void conversionService() {
//등록
DefaultConversionService conversionService = new
DefaultConversionService();
conversionService.addConverter(new StringToIntegerConverter());
conversionService.addConverter(new IntegerToStringConverter());
conversionService.addConverter(new StringToIpPortConverter());
conversionService.addConverter(new IpPortToStringConverter());
//사용
assertThat(conversionService.convert("10",Integer.class)).isEqualTo(10);
assertThat(conversionService.convert(10, String.class)).isEqualTo("10");
IpPort ipPort = conversionService.convert("127.0.0.1:8080", IpPort.class);
assertThat(ipPort).isEqualTo(new IpPort("127.0.0.1", 8080));
String ipPortString = conversionService.convert(new IpPort("127.0.0.1", 8080)
, String.class);
assertThat(ipPortString).isEqualTo("127.0.0.1:8080");
}
}
WebConfig - 컨버터 등록
@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());
}
}
conversionService
를 제공한다. 우리는 WebMvcConfigurer
가 제공하는 addFormatters()
를 사용해서 추가하고 싶은 컨버터를 등록하면 된다Formatter는 객체를 문자로 변경하고 문자를 객체로 변경하는 두가지 기능을 모두 수행한다
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"
으로 그러니까, 1000 단위로 쉼표가 들어가는 포맷을 적용해보자. 그리고 그 반대도 처 리해주는 포맷터를 만들어보자.
@Slf4j
public class MyNumberFormatter implements Formatter<Number> {
@Override
public Number parse(String text, Locale locale) throws ParseException {
log.info("text={}, locale={}", text, locale);
NumberFormat format = NumberFormat.getInstance(locale);
return format.parse(text);
}
@Override
public String print(Number object, Locale locale) {
log.info("object={}, locale={}", object, locale);
return NumberFormat.getInstance(locale).format(object);
}
}
"1,000"
처럼 숫자 중간의 쉼표를 적용하려면 자바가 기본으로 제공하는 NumberFormat
객체를 사용하면 된다. 이 객체는 Locale
정보를 활용해서 나라별로 다른 숫자 포맷을 만들어준다.
parse()
를 사용해서 문자를 숫자로 변경한다.print()
를 사용해서 객체를 문자로 변경한다.Formatter 적용 코드
@Configuration
public class WebConfig implements WebMvcConfigurer {
@Override
public void addFormatters(FormatterRegistry registry) {
registry.addFormatter(new MyNumberFormatter());
}
}
출처: https://www.inflearn.com/course/%EC%8A%A4%ED%94%84%EB%A7%81-mvc-2/dashboard