| 일 | 월 | 화 | 수 | 목 | 금 | 토 |
|---|---|---|---|---|---|---|
| 1 | 2 | 3 | 4 | 5 | 6 | |
| 7 | 8 | 9 | 10 | 11 | 12 | 13 |
| 14 | 15 | 16 | 17 | 18 | 19 | 20 |
| 21 | 22 | 23 | 24 | 25 | 26 | 27 |
| 28 | 29 | 30 |
- 스프링 배치
- spring boot
- 스프링
- Effective Java
- 자바
- 프록시 패턴
- 프로그래머스
- 템플릿 메서드 패턴
- 성능 개선
- 추상클래스
- Spring Batch
- GoF 23
- 트러블슈팅
- 코드카타
- 로드밸런서
- 이펙티브 자바
- java
- 디자인 패턴
- 스케줄러
- 백엔드
- Til
- lv1
- 토스
- 배치
- 계산기
- DB
- 빌더 패턴
- redis
- Spring
- 김영한
- Today
- Total
김코딩
스프링 MVC - 타입 변환은 어떻게 이루어질까? (Converter & Formatter) 본문
나의 생각은 틀렸었다...
오늘은 스프링 MVC에서 컨버터(Converter)와 포매터(Formatter)에 대해 알아보는 시간을 가졌습니다.
사실 지금까지 개발을 진행하면서 @RequestParam, @PathVariable 등의 파라미터를 입력할 때, 뒤에 타입만 지정하면 (예: @RequestParam Long id) 그냥 파라미터로 Long 타입을 넘겨주는거로 알고있었습니다.
하지만 이번에 타입 컨버터와 포매터에 대한 내용을 학습하면서, 실제로는 모든 HTTP 요청 파라미터 값이 문자(String)로 들어오고, 스프링 내에서 자동으로 타입 변환이 이루어지고 있다는 것을 알고 충격을 받았습니다.
그래서 타입 변환을 왜 해주는거야?
만약 스프링이 자동으로 타입 변환을 해주지 않는다면, 우리는 다음과 같이 직접 변환 코드를 작성해야 합니다
@GetMapping("/hello-v1")
public String helloV1(HttpServletRequest request) {
String dataParam = request.getParameter("data"); // 항상 String
Integer data = Integer.valueOf(dataParam); // 직접 Integer 형으로 형변환
System.out.println("data = " + data);
return "ok";
}
이 코드를 호출하는 URL을 작성해보겠습니다.
http://localhost:8080/hello-v1?data=10
여기에서 data=10은 문자 "10"이라는 값으로 서버에 전달하게 됩니다.
하지만 우리는 이 값을 Integer 타입으로 받고 싶습니다.
이렇게 매번 HttpServletRequest 에서 파라미터를 꺼내서, Integer.valueOf() 같은 변환 코드를 직접 작성해야 합니다.
코드가 번거롭고, 실수할 가능성도 높아지겠죠?
그런데 스프링에서는 아래처럼 훨씬 간단하게 작성할 수 있습니다
@GetMapping("/hello-v1")
public String hello(@RequestParam Integer data) {
System.out.println("data = " + data);
return "ok";
}
@RequestParam 만 사용하면,
스프링이 내부적으로 타입 컨버터를 이용해 String → Integer 변환을 자동으로 처리해줍니다!
그렇다면, 이 "타입 변환"은 누가 처리해줄까?
스프링에서는 이러한 타입 변환 기능을 Converter 라는 인터페이스를 통해 처리합니다.
Converter<S, T> 라는 인터페이스는 S(원래 타입) -> T(변경하고 싶은 타입) 으로 변환하는 기능을 제공합니다.
가장 기본적인 형태는 다음과 같습니다.
public interface Converter<S, T> {
T convert(S source);
}
- S: 변환 "전" 타입 (예: String)
- T: 변환 "후" 타입 (예: Integer)
예를 들어, String → Integer 로 변환하는 Converter 를 직접 만들어볼 수 있습니다
@Slf4j
public class StringToIntegerConverter implements Converter<String, Integer> {
@Override
public Integer convert(String source) {
log.info("convert source={}", source);
return Integer.valueOf(source);
}
}
이렇게 convert() 메서드 안에서 원하는 변환 로직을 구현하면 됩니다.
그런데 이렇게 Converter 를 하나하나 직접 호출해서 쓰면 불편하겠죠?
그래서 스프링은 이 Converter 들을 모아서 관리하는 ConversionService 라는 기능도 제공합니다.
ConversionService 란?
ConversionService 는 여러 Converter 를 등록해두고,
필요할 때마다 알아서 적절한 Converter 를 찾아서 변환해주는 서비스입니다.
DefaultConversionService conversionService = new DefaultConversionService();
conversionService.addConverter(new StringToIntegerConverter());
Integer result = conversionService.convert("10", Integer.class);
System.out.println("result = " + result);
이렇게 하면 우리는 직접 Integer.valueOf() 를 호출할 필요 없이,
conversionService 가 알아서 적절한 Converter 를 사용해서 변환해줍니다!
만약 ConversionService 가 없다면?
ConversionService를 사용하지 않는다면, 저희가 만든 Converter를 직접 사용해야 합니다.
// 직접 Converter 인스턴스 생성
StringToIntegerConverter converter = new StringToIntegerConverter();
// 직접 convert() 메서드 호출
Integer result = converter.convert("10");
System.out.println("result = " + result);
하지만 이렇게 직접 사용하면 Converter 를 사용하는 코드가 Converter 에 직접 의존하게 됩니다.
만약 새로운 타입 변환이 필요하다면?
→ 또 새로운 Converter 를 직접 생성하고, 직접 호출해야 합니다.
ConversionService 를 사용하면?
스프링은 이런 문제를 해결하기 위해
Converter 들을 통합 관리하는 ConversionService 를 제공합니다.
// ConversionService 생성
DefaultConversionService conversionService = new DefaultConversionService();
// Converter 등록
conversionService.addConverter(new StringToIntegerConverter());
// 변환 사용
Integer result = conversionService.convert("10", Integer.class);
System.out.println("result = " + result);
ConversionService 를 사용하면 좋은 점
- Converter 를 직접 사용할 필요 없음
- 어떤 Converter 가 있는지 몰라도 ->ConversionService 가 알아서 찾아서 적용
- Converter 추가/확장 시 -> 등록만 하면 됨 (코드 수정 X)
그렇다면, 우리가 만든 Converter 를 스프링 MVC 에 적용(통합) 하려면 어떻게 해야 할까?
스프링 MVC 에서는 이미 내부적으로 ConversionService 를 사용하고 있습니다.
우리는 단지 우리가 만든 Converter 를 스프링 MVC 의 ConversionService 에 등록하기만 하면 됩니다.
WebConfig 를 통한 Converter 등록
스프링에서는 WebMvcConfigurer 의
addFormatters(FormatterRegistry registry) 메서드를 통해 Converter 를 등록할 수 있습니다.
@Configuration
public class WebConfig implements WebMvcConfigurer {
@Override
public void addFormatters(FormatterRegistry registry) {
// 우리가 만든 Converter 등록
registry.addConverter(new StringToIntegerConverter());
}
}
우리가 WebConfig 에 Converter 를 등록하면,
스프링 MVC 에서 사용하는 ConversionService 에 등록됩니다.
그 결과 @RequestParam, @PathVariable 등에서 우리가 등록한 Converter 가 적용됩니다.
만약 동일한 타입 변환에 대해 스프링이 기본 제공하는 Converter 가 있다면, 우리가 등록한 Converter 가 우선 적용됩니다.
적용 결과
이제 아래와 같이 작성한 컨트롤러에서
@GetMapping("/hello")
public String hello(@RequestParam Integer data) {
System.out.println("data = " + data);
return "ok";
}
@RequestParam 으로 들어온 String "10" 값이 → Integer 로 자동 변환됩니다!
(우리가 등록한 Converter 가 적용됨)
Converter 는 모든 타입을 변환할 수 있어서 아주 유용하지만,
실제로 웹 애플리케이션에서는 "문자 <-> 객체" 변환이 가장 많이 사용됩니다. (예: 날짜 출력, 숫자 포맷 등)
이럴 때는 Converter 보다는 Formatter 를 사용하는 것이 더 적합합니다.
이번에는 Formatter 에 대해 알아보겠습니다.
Formatter는 무엇일까?
앞서 살펴본 Converter 는 모든 타입 ↔ 모든 타입 변환을 처리할 수 있습니다.
하지만 웹 애플리케이션에서 가장 흔하게 필요한 변환은 무엇일까요?
바로 문자 ↔ 객체 변환입니다!
예를 들어:
- 날짜(Date/LocalDateTime)를 "yyyy-MM-dd" 형식으로 보여주기
- 숫자(Integer)를 "1,000" 처럼 쉼표 넣어서 보여주기
- 문자열로 들어온 "1,000" 을 숫자 1000 으로 변환하기
Formatter 는 두 가지 메서드를 제공합니다
public interface Formatter<T> {
// 객체 → 문자열
String print(T object, Locale locale);
// 문자열 → 객체
T parse(String text, Locale locale) throws ParseException;
}
직접 Formatter 만들어보기
예를 들어, 숫자에 쉼표(,) 를 넣어주는 Formatter 를 만들어볼 수 있습니다
@Slf4j
public class MyNumberFormatter implements Formatter<Number> {
@Override
public Number parse(String text, Locale locale) throws ParseException {
log.info("parse text={}, locale={}", text, locale);
NumberFormat format = NumberFormat.getInstance(locale);
return format.parse(text);
}
@Override
public String print(Number object, Locale locale) {
log.info("print object={}, locale={}", object, locale);
return NumberFormat.getInstance(locale).format(object);
}
}
Formatter 등록 방법
Formatter 도 Converter 와 마찬가지로
WebMvcConfigurer 를 통해 등록할 수 있습니다
@Configuration
public class WebConfig implements WebMvcConfigurer {
@Override
public void addFormatters(FormatterRegistry registry) {
// 우리가 만든 Converter 등록
registry.addConverter(new StringToIntegerConverter());
// Formatter 등록
registry.addFormatter(new MyNumberFormatter());
}
}
사용 예시
컨트롤러에서 아래와 같이 사용하면 됩니다
@GetMapping("/formatter-test")
public String formatterTest(@RequestParam Integer number) {
System.out.println("number = " + number);
return "ok";
}
요청 URI
http://localhost:8080/formatter-test?number=1,000
입력 값 "1,000" 이 → Integer 1000 으로 변환됩니다. (Formatter 가 적용됨)
Formatter가 사용되는 대표적인 경우
- @RequestParam, @PathVariable
- Thymeleaf 템플릿 ( ${{object}} 문법에서 자동 적용)
- Form 입력/출력 시 자동 적용
참고: 스프링 기본 제공 Formatter
스프링은 이미 여러 Formatter 를 기본으로 제공합니다:
- @NumberFormat
@NumberFormat(pattern = "###,###")
private Integer price;
-> price 값이 10000 이라면 화면에 "10,000" 처럼 출력됩니다
- @DateTimeFormat
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private LocalDateTime createdDate;
-> createdDate 값이 2024-06-05T10:30:00 이라면 화면에 "2024-06-05 10:30:00" 처럼 출력됩니다
정리하기
Converter → 데이터의 타입을 변환하기 위한 것
예를 들어:
- String "10" → Integer 10
- String "true" → Boolean true
- IpPort "127.0.0.1:8080" → IpPort 객체
어떤 타입 → 다른 타입으로 변환
형식(포맷)은 신경 안 씀
→ 그냥 타입 맞추기!
Formatter → 데이터의 형식(포맷)을 맞추기 위한 것
주로 문자 ↔ 객체 변환 + "출력 형식"까지 관리
- 날짜(LocalDateTime) → "yyyy-MM-dd" 형식으로 출력
- 숫자(Integer 10000) → "10,000" 처럼 쉼표 추가 출력
- 반대로 입력 받을 때 "1,000" → Integer 1000 으로 변환
Converter 와 Formatter 는 ConversionService 에 등록해서 사용한다.
-> 이를 위해 WebMvcConfigurer 의 addFormatters() 메서드를 통해 등록할 수 있다.
'스프링' 카테고리의 다른 글
| QueryDSL 기본 가이드 (1) | 2025.06.30 |
|---|---|
| JPA - 연관관계 편의 메서드 (1) | 2025.06.27 |
| 반복되는 로그인 체크, 필터 하나로 끝내기(Filter 사용법) (0) | 2025.05.22 |
| 여긴 못지나간다.(@Valid) (0) | 2025.05.19 |
| Spring Container와 Bean이 헷갈리는 이들에게 (0) | 2025.05.15 |