스프링의 컨버전 서비스를 공부하다 보면 의문이 생긴다. 개별 컨버터를 직접 사용하면 파라미터 하나만 던지면 되는데, 컨버전 서비스는 데이터와 반환 타입까지 굳이 두 개를 적어야 한다.
// 1. 컨버터 직접 사용 (파라미터 1개)
Integer result = integerToStringConverter.convert(data);
// 2. 컨버전 서비스 사용 (파라미터 2개)
Integer result = conversionService.convert(data, Integer.class);
코드만 보면 2번이 더 길고 불편해 보인다. 그런데 왜 스프링은 이 방식을 권장하고, 다들 이게 더 "편하다"고 말하는 걸까? 그 이면에 숨겨진 ISP(인터페이스 분리 원칙)의 실체를 정리해 보았다.
가장 큰 차이는 사용자가 알아야 할 정보의 양이다.
A컨버터, 문자를 바꿀 땐 B컨버터... 사용하는 쪽에서 모든 컨버터의 존재와 이름을 다 알고 있어야 한다. (식당 요리사 이름을 일일이 외워야 주문이 가능한 상황)컨버전 서비스의 파라미터가 2개가 된 이유는, 수많은 컨버터를 단 하나의 인터페이스(convert)로 통합했기 때문이다. 구체적인 대상을 지정하는 대신 목적지(Target Type)을 알려줘야 하기 때문에 파라미터가 추가된 것이다.
여기서 ISP(Interface Segregation Principle) 개념이 등장한다. ISP의 핵심은 "사용자는 자신이 사용하지 않는 메서드에 의존하도록 강제되어서는 안 된다"는 것이다.
컨버전 서비스를 쓰면 클라이언트(컨트롤러 등)는 수많은 컨버터 구현체들을 몰라도 된다. 오직 ConversionService라는 단일 창구만 바라보면 된다.
ConversionService 하나만 주입(DI)받으면 끝난다.convert()를 직접 호출할 일도 별로 없다. 스프링이 내부적으로 컨버전 서비스를 사용하기 때문에, 우리는 등록만 해두면 @RequestParam 등에서 자동 변환 혜택을 누릴 수 있다.파라미터 하나를 더 적는 수고로움은 나중에 수십 개의 컨트롤러 코드를 일일이 수정해야 할 대참사를 막기 위한 일종의 보험료와 같다.
"사용자(클라이언트)를 바보로 만들수록 시스템은 더 견고해진다."
이것이 컨버전 서비스가 파라미터를 두 개나 받으면서도 "편리한 도구"라고 불리는 진짜 이유였다.
| 방식 | 파라미터 | 의존성 | 유연성 |
|---|---|---|---|
| 개별 컨버터 | 1개 (데이터) | 모든 컨버터 구현체를 알아야 함 | 낮음 (교체 시 코드 수정 필요) |
| 컨버전 서비스 | 2개 (데이터 + 타입) | 컨버전 서비스 인터페이스만 알면 됨 | 높음 (구현체 숨김, ISP/DIP 준수) |