5. 템플릿
1. 다시 보는 개방 폐쇄 원칙(OCP)
클래스나 모듈은 확장에는 열려 있어야 하고, 변경에는 닫혀 있어야 한다.
우리가 작성한 코드를 보면 다음과 같은 특징이 있다. (두 특징을 가진 코드가 혼재되어 있다)
변경을 통해서 기능의 확장을 이루려 하는 코드가 있다. (
ExRateProvider)한번 작성되면 변경되지 않는 코드가 있다. (
PaymentService)
우리는 더욱더 유연한 코드를 만들기 위해서 다음을 생각해보아야 한다.
변화의 특성이 다른 부분을 기억하고,
각각 다른 목적과 이유에 의해 다른 시점에 독립적으로 변경될 수 있는 효율적인 구조를 만들어야 한다.

템플릿
템플릿 - 코드 중에는 변경이 거의 일어나지 않으며, 일정한 패턴으로 유지되는 특성을 가진 부분
콜백 - 자유롭게 변경이 되는 특성을 가지 부분으로부터 독립시켜 효과적으로 사용할 수 있도록 하는 방법
2. WebApiExRateProvider 리팩토링
기존 WebApiExRateProvider 클래스 리팩토링 포인트
WebApiExRateProvider 클래스 리팩토링 포인트 Java 20 에서
@Deprecated된URL제거불필요한
throws IOException제거WebApiExRateProvider코드의 예외 처리try-with-resource를 이용한 리소스 반환 (AutoCloseable)
3. 변하는 코드 분리하기 - 메서드 추출
WebApiExRateProvider 구성
URI 를 준비하고, 예외처리를 위한 작업을 하는 코드 (웹을 통해서 API 를 호출한다면, URI 를 준비하는 과정은 고정되어 있다 - 변경X)
API 를 실행하고, 서버로부터 받은 응답을 가져오는 코드 (API 를 호출하는 기술과 방법이 변경될 수 있다 - 변경O)
JSON 문자열을 파싱하고, 필요한 환율정보를 파싱하는 코드 (API 응답의 JSON 의 구조에 따라서 정보를 추출하는 방식이 변경 - 변경O)
변하는 성격을 가진 코드를 메서드로 추출하자.
4. 변하지 않는 코드 분리하기 - 메서드 추출
템플릿?
템플릿은 어떤 목적을 위해 만들어둔 모양이 있는 틀
고정된 틀 안에 바꿀 수 있는 부분을 넣어서 사용하도록 만들어진 오브젝트
템플릿 메서드 패턴?
템플릿 메서드 패턴은 고정된 틀의 로직이 가진 템플릿 메서드를 슈퍼 클래스로 두고, 바뀌는 부분을 서브클래스의 메서드에 두는 구조로 이루어진다.
변하지 않는 성격을 가진 코드를 메서드로 추출하자.
5. ApiExecutor 분리 - 인터페이스 도입과 클래스 분리
위 코드에서 변하는 코드와 변하지 않는 코드를 메서드로 분리했다.
하지만, 메서드로 분리된 코드 내에서 변경이 생겨난다면, 전체 코드를 바꾸어주어야 할 수도 있다. (확장성이 떨어짐)
때문에, 인터페이스 도입과 클래스 분리를 통해 영향을 받지 않도록 리팩토링을 해주자.
클래스 분리만 한다면 클라이언트 코드와 결합도가 생겨 여전히 확장성이 떨어진다..
때문에, 인터페이스 도입과 클래스를 분리를 함께 사용해야만 클라이언트 코드에서 변경이 없는 확장성 있는 코드를 만들 수 있다.
변경 가능한 콜백 메서드의 내용을 인터페이스로 분리하자.
인터페이스 :
ApiExecutor클래스 :
SimpleApiExecutor
6. ApiExecutor 콜백과 메서드 주입
콜백?
콜백이란 실행되는 것을 목적으로 다른 오브젝트의 메서드에 전달되는 오브젝트 파라미터로 전달되지만, 값을 참조하기 위함이 아니라, 특정 로직을 담은 메서드를 실행시키기 위함이 목적.
하나의 메서드를 가진 인터페이스 타입(SAM : Single Abstract Method) 의 오브젝트 또는 람다 오브젝트
변하는 코드를 콜백으로 생각할 수 있다.
템플릿 콜백 패턴은 전략 패턴의 특별한 케이스
템플릿 = 전략 패턴의 컨텍스트
콜백은 = 전략 패턴의 전략
템플릿 콜백 패턴은 메서드 하나만 가진 전략 인터페이스(SAM)를 사용하는 전략 패턴
메서드 주입
의존 오브젝트가 메서드 호출 시점에 파라미터로 전달되는 방식
의존관계 주입의 한 종류
보통 DI 는 스프링 컨테이너가 올라가는 시점에 이루어진다.
하지만 메서드 주입은 호출할 때마다 이루어진다.
메서드 호출 주입(method call injection) 이라고도 한다.
템플릿 내에 구체화된 코드를 템플릿을 사용하는 쪽(Client)으로 분리하자.
7. ExRateExtractor 콜백
템플릿 콜백 패턴 작업 흐름
템플릿 콜백 패턴의 작업 흐름을 살펴보자.
클라이언트가 콜백을 생성한다. (람다, 인스턴스 생성)
클라이언트가 템플릿에 콜백을 전달하면서 호출한다.
템플릿 Workflow 시작
참조정보 생성 (우리 코드로 치면
URI인스턴스)콜백에 참조정보를 전달하면서 호출한다.
콜백 메서드는
Client final변수 참조가 가능하다.콜백 메서드 작업 수행
콜백 작업 결과 리턴
템플릿 Workflow 진행
템플릿 Workflow 마무리
템플릿이 클라이언트에 템플릿 작업 결과 리턴

ExRateExtractor 콜백
extractExRate 메서드의 기능도 ApiExecutor 인터페이스와 같이 콜백 패턴으로 변경해보자.
8. ApiTemplate
ApiTemplate 의 설명
환율정보 API 로부터 환율을 가져오는 기능을 제공하는 오브젝트
API 호출과 정보 추출의 기본 틀 제공
두가지 콜백 이용
ApiExecutor: 환율 정보 API 호출ExRateExtractor: API 응답값 추출
유사한 여러 오브젝트에서 재사용 가능
템플릿 분리
템플릿을 분리해보자.
템플릿의 경우 내용을 코드의 변경이 있을 이유가 거의 없다.
때문에, 확장을 기반한 설계를 하지 않아도 된다. (인터페이스 사용하지 않아도 된다!)
기존 메서드명(
runApiForExRate)을 변경해보자. (getExRate)
WebApiExRateProvider입장에서 코드가 상당히 간결해졌다.
콜백 변경
기존 SimpleApiExecutor 는 자바에서 제공하는 가장 기본적인 방법으로 API 를 호출했다.
SimpleApiExecutor 는 자바에서 제공하는 가장 기본적인 방법으로 API 를 호출했다. 자바 11에 도입된 HttpClient 를 사용해 조금 더 모던 한 방식을 도입해보자.
ApiExecutor 콜백 추가
자연스레 콜백을 변경하며 기능은 유연하게 변경할 수 있다.
그렇다면 지금까지 학습한 템플릿 콜백 패턴이 스프링과 무슨 연관이 있는가?
9. 디폴트 콜백과 템플릿 빈
디폴트 콜백
현재 코드에서는 템플릿을 실행시키기 위해서는 2개의 콜백이 필요하다.
무조건 적으로 콜백을 넣어주는 것이 아니라, 디폴트 콜백을 사용하는 코드를 추가해보자.
콜백을 0개, 1개 받는 템플릿을 추가했다.
템플릿 빈
만약 ApiTemplate 템플릿이 다른 클래스에서도 사용될 경우를 생각해서 스프링 빈으로 등록할 수 있을까?
-> 가능하다!
빈은 기본적으로 싱글톤으로 관리되기 때문에, 전역적으로 상태가 관리되면 안된다.
템플릿에 변경 가능한 상태가 있는 경우, 빈으로 등록해서는 안된다!
템플릿에 변경 가능한 상태가 있다면 템플릿이 아니겠지?
또, 생각해보면 ApiTemplate 생성 시 디폴트 콜백을 지정해주는 것이 코드 가독성 측면에서 더 좋을 것이다. ApiTemplate 에 생성자를 추가하고 PaymentConfig 에서 디폴트 콜백을 지정해주자. (토비가 권장하는 방법)
10. 스프링이 제공하는 템플릿
RestTemplate
스프링이 제공하는 가장 오래된 동기 방식의 REST 클라이언트 기술 중 하나이다.
GET, POST 메서드를 사용하는 간단한 HTTP API 를 호출할 때 사용하기에 편리하다. 다양한 HTTP API 기술을 이용하도록 만들 수 있다.
최근에 스프링에 추가된
RestClient을 이용하면 모던한 API 스타일로 된 HTTP API 를 호출하는 코드를 만들 수 있다. 여러가지 콜백 오브젝트를 지원한다.기존에
ApiTemplate을 사용하던 로직을RestTemplate을 사용하는 로직으로 변경해보자.
현재 코드에서는 콜백을 사용하지 않아서, 우리가 지금까지 살펴본 템플릿 메서드 패턴으로 보여지지 않을 수 있다.
하지만, 정말 많은 전략들이 있다.
그 중 하나인
JdkClientHttpRequestFactory를 추가해서 실행해보면 문제 없이 실행된다.
JdbcTemplate
SQL 쿼리를 수행하거나 등록, 수정, 프로시저를 호출할 때 사용할 수 있는 템플릿이다.
스프링 6 에는
JdbcTemplate을 좀 더 모던하게 만든JdbcClient가 추가되었다.JdbcTemplate에서 사용하는RowMapper와 같은 콜백을 사용할 수 있다.
TransactionTemplate
스프링의 트랜잭션 추상화 기술과 함께 사용 가능한 데이터 트랜잭션 작업용 템플릿이다.
@Transactional이 제공하는 트랜잭션 경계설정 기능을TransactionTemplate으로도 모두 적용할 수 있다.JDBC, JPA, Mybatis, Hibernate 등의 다양한 데이터 기술에 모두 사용이 가능하다.
이 외에도 수 많은 템플릿을 스프링이 제공한다.
Last updated