GIF 출처 : https://sigridjin.medium.com/spring-transaction-관리에-대한-메모-f391fd2885b4

1 ) 템플릿&콜백 ( Template & Callback ) 패턴이란?
2 ) 템플릿&콜백의 특징
3 ) 장&단점
템플릿&콜백 ( Template & Callback ) 패턴이란?
: 템플릿&콜백 패턴은 전략 패턴 ( Strategy Pattern ) 의 기본 구조에 익명 내부 클래스를 활용한 방식으로 변하지 않는 처리 흐름을 ① 템플릿 ( Template ) 이 담당하고,
변하는 행위를 ② 콜백 ( Callback )으로 외부에서 주입받아 실행하는 패턴이다.
기존의 전략패턴의 경우 다음과 같은 단점이 존재한다.
알고리즘을 인터페이스 와 구현클래스로 분리하여 사용하기 때문에 기능이 추가될 경우 구현클래스를 새로 생성해야 한다. 이는 전략 개수가 증가할수록 클래스 또한 증가한다는 단점이 있다.
전략에 부가 정보가 필요하면 생성자 및 인스턴스 변수가 필요하다. 이는 행위를 캡슐화하는 전략 패턴이 상태를 포함하는 객체로 변질될 우려가 있다.
전략 객체생성 및 관리의 책임이 클라이언트에게 있다. 스프링 환경에서는 객체 생성 , 생명주기 , 의존성 관리는 컨테이너에게 책임이 있는데 전략 객체의 생성·선택·구성 책임을 클라이언트가 직접 맡게 되는 순간,그 클라이언트는 SRP를 위반하게 된다.
위의 스프링 환경에서 전략 패턴이 드러내는 구조적 문제를 보완하기 위해 자연스럽게 정착한 형태가 바로 템플릿&콜백 ( Template Callback ) 패턴이다.
템플릿 & 콜백 패턴에서는 변하는 행위를 표현하기 위해 보통 단일 메소드 인터페이스(SAM) 를 사용한다. 콜백은 이 인터페이스를 구현한 객체로 전달되며, 과거에는 주로 익명 내부 클래스로 구현되었지만, 현대 자바와 스프링 환경에서는 람다 표현식을 통해 간결하게 표현되는 경우가 많다.
다음은 템플릿&콜백의 작업 흐름을 보여주는 그림이다.

먼저 클라이언트는 템플릿 안에서 실행될 로직을 담은 1 ) 콜백 오브젝트를 생성 하고 , 콜백이 참조할 정보를 제공한다. 이후 만들어진 콜백은 2 ) 클라이언트가 템플릿의 메소드를 호출 할 때 , 파라미터로 전달된다.
템플릿은 정해진 3 ) 작업 흐름을 따라 작업을 진행 하다가 4 ) 내부에서 생성한 참조정보 를 가지고 콜백 오브젝트의 메소드를 호출 한다.
콜백은 클라이언트 메소드에 있는 정보와 템플릿이 제공한 5 ) 참조 정보를 이용 하여 6 ) 작업을 수행 한 후 해당 7 ) 결과를 다시 템플릿에게 돌려준다.
템플릿은 콜백이 돌려준 정보를 사용해서 8 , 9 ) 작업을 마저 수행 하고 경우에 따라 10 ) 최종 결과를 클라이언트에게 다시 돌려주기도 한다.
일반적인 DI에서는 템플릿이 의존 객체를 필드로 보관하고, 생성자나 setter를 통해 주입받아 재사용한다.
class Context {
private Strategy strategy;
public Context(Strategy strategy) {
this.strategy = strategy;
}
public void execute() {
strategy.call();
}
}
반면 템플릿 & 콜백 패턴에서는 필요한 로직을 메소드 호출 시점에 콜백으로 전달하며, 템플릿은 이를 상태로 유지하지 않는다.
public interface Callback {
void call();
}
public class Template {
public void execute(Callback callback) {
System.out.println("공통 로직 시작");
callback.call(); // 변하는 로직 호출
System.out.println("공통 로직 종료");
}
}
public class Client {
public static void main(String[] args) {
Template template = new Template();
// Client에서 템플릿이 메소드 시그니처로 받는 Callback 객체를 내부 클래스로 정의
template.execute(new Callback() {
@Override
public void call() {
System.out.println("변하는 로직");
}
});
}
}
- 장점
① 객체 생성·관리 책임 제거 (SRP 강화)
: 템플릿은 콜백 객체를 생성하거나 보관하지 않는다. 변하는 로직은 클라이언트가 메소드 호출 시점에 전달하며, 템플릿은 실행 흐름 제어에만 집중한다. 이를 통해 템플릿의 책임이 명확해지고 SRP를 잘 지키는 구조가 된다.
② 메소드 단위의 높은 유연성
: 전략 패턴은 전략 객체를 교체하려면 객체 구성 자체를 변경해야 한다. 반면 템플릿&콜백은 메소드 호출마다 다른 콜백을 전달할 수 있다. 이를 통해 같은 템플릿을 훨씬 세밀한 단위로 재사용 가능하다.
③ 상태 없는 템플릿 ( Stateless )
: 템플릿이 콜백을 필드로 보관하지 않기 때문에 상태가 없다. 이로 인해 멀티스레드 환경에서 안전하고 싱글톤으로 사용하기 적합하다.
- 단점
① 코드 가독성 저해
실제 실행되는 로직이 템플릿 클래스와 클라이언트 메소드 내부의 콜백으로
분산되어 존재한다. 특히 익명 내부 클래스나 람다를 과도하게 사용하면 전체 흐름을 한 번에 파악하기 어렵다.
② 디버깅과 추적이 불편할 수 있음
익명 내부 클래스나 람다는 클래스 이름이 없고 스택 트레이스가 직관적이지 않다.
이로인해 디버깅 시 실제 구현 위치를 찾기 어려운 경우가 있다.
ex)
at Template.execute(Template.java:20)
at Client.lambda$method$0(Client.java:35) // 정확히 어떤 부분에서 오류가 발생하였는지 파악하기 어려움