메인 콘텐츠로 건너뛰기
Deep Thought
← 목록으로
Design Pattern

템플릿 메서드 패턴: 알고리즘의 골격을 정의하는 행위 패턴

신중선-- views
template-methoddesign-patterninheritancealgorithmcode-reuse

템플릿 메서드 패턴이란?

템플릿 메서드 패턴(Template Method Pattern)은 알고리즘의 골격을 상위 클래스에서 정의하고, 세부 구현을 하위 클래스에 맡기는 행위 디자인 패턴입니다. 상위 클래스는 알고리즘의 실행 순서와 구조를 결정하는 템플릿 메서드를 제공하고, 하위 클래스는 각 단계의 구체적인 구현을 담당합니다.

이 패턴은 "할리우드 원칙(Hollywood Principle)"을 따릅니다. "우리가 당신을 호출할 테니, 당신이 우리를 호출하지 마세요"라는 의미로, 상위 클래스가 전체 흐름을 제어하고 필요한 시점에 하위 클래스의 메서드를 호출하는 구조입니다.

핵심 개념

1. 기본 구조와 구현

템플릿 메서드 패턴은 추상 클래스와 구체 클래스로 구성됩니다. 상위 클래스에서 알고리즘의 뼈대를 정의하고, 하위 클래스에서 세부 구현을 제공합니다.

public abstract class DataProcessor {
    
    // 템플릿 메서드 - 알고리즘의 골격 정의
    public final void processData() {
        readData();
        validateData();
        transformData();
        saveData();
        cleanup();
    }
    
    protected abstract void readData();
    protected abstract void validateData();
    protected abstract void transformData();
    protected abstract void saveData();
    
    // 공통 구현 - 모든 하위 클래스에서 동일하게 사용
    protected final void cleanup() {
        System.out.println("메모리 정리 중...");
    }
}

class CsvDataProcessor extends DataProcessor {
    
    @Override
    protected void readData() {
        System.out.println("CSV 파일에서 데이터 읽기");
    }
    
    @Override
    protected void validateData() {
        System.out.println("CSV 형식 검증");
    }
    
    @Override
    protected void transformData() {
        System.out.println("CSV 데이터를 객체로 변환");
    }
    
    @Override
    protected void saveData() {
        System.out.println("데이터베이스에 저장");
    }
}

2. Hook 메서드 활용

Hook 메서드는 선택적으로 오버라이드할 수 있는 메서드로, 알고리즘의 특정 지점에서 추가 로직을 삽입할 수 있게 해줍니다.

public abstract class WebCrawler {
    
    public final void crawlWebsite(String url) {
        authenticate();
        
        if (shouldRetry()) {
            retryConnection();
        }
        
        extractData(url);
        processData();
        
        if (shouldNotify()) {
            sendNotification();
        }
        
        cleanup();
    }
    
    protected abstract void authenticate();
    protected abstract void extractData(String url);
    protected abstract void processData();
    
    // Hook 메서드 - 기본 구현 제공, 필요시 오버라이드 가능
    protected boolean shouldRetry() {
        return false;
    }
    
    protected boolean shouldNotify() {
        return true;
    }
    
    protected void retryConnection() {
        // 기본 재시도 로직
    }
    
    protected void sendNotification() {
        System.out.println("크롤링 완료 알림");
    }
    
    private void cleanup() {
        System.out.println("리소스 정리");
    }
}

3. Spring Framework에서의 활용

Spring Framework는 템플릿 메서드 패턴을 광범위하게 활용합니다. 특히 JdbcTemplate, RestTemplate 등에서 이 패턴을 확인할 수 있습니다.

// JdbcTemplate의 템플릿 메서드 패턴 활용 예시
@Repository
public class UserRepository {
    
    private final JdbcTemplate jdbcTemplate;
    
    public UserRepository(JdbcTemplate jdbcTemplate) {
        this.jdbcTemplate = jdbcTemplate;
    }
    
    public List<User> findAllUsers() {
        return jdbcTemplate.query(
            "SELECT * FROM users",
            (rs, rowNum) -> new User(
                rs.getLong("id"),
                rs.getString("name"),
                rs.getString("email")
            )
        );
    }
}

// 커스텀 템플릿 클래스 구현
public abstract class ApiTemplate {
    
    public final ApiResponse executeRequest(String endpoint) {
        preProcess();
        
        try {
            ApiResponse response = doExecute(endpoint);
            return postProcess(response);
        } catch (Exception e) {
            return handleError(e);
        } finally {
            cleanup();
        }
    }
    
    protected abstract ApiResponse doExecute(String endpoint);
    
    protected void preProcess() {
        // 공통 전처리 로직
    }
    
    protected ApiResponse postProcess(ApiResponse response) {
        // 기본 후처리 로직
        return response;
    }
    
    protected ApiResponse handleError(Exception e) {
        return ApiResponse.error(e.getMessage());
    }
    
    protected void cleanup() {
        // 리소스 정리
    }
}

4. 장단점과 사용 시점

장점:

  • 코드 중복 제거: 공통 로직을 상위 클래스에 집중
  • 알고리즘 제어: 실행 순서와 구조를 상위 클래스에서 통제
  • 확장성: 새로운 구현체 추가가 용이
  • 일관성: 모든 하위 클래스가 동일한 알고리즘 구조를 따름

단점:

  • 상속 의존성: 클래스 간 강한 결합도
  • 복잡성 증가: 상위 클래스 이해 없이는 하위 클래스 개발 어려움
  • 유연성 제한: 알고리즘 구조 변경 시 모든 하위 클래스에 영향
// 사용 적절한 경우: 동일한 알고리즘, 다른 구현
public abstract class ReportGenerator {
    
    public final String generateReport(List<Data> data) {
        String header = createHeader();
        String body = processData(data);
        String footer = createFooter();
        
        return formatReport(header, body, footer);
    }
    
    protected abstract String createHeader();
    protected abstract String processData(List<Data> data);
    protected abstract String createFooter();
    protected abstract String formatReport(String... parts);
}

// Strategy 패턴이 더 적절한 경우: 다양한 알고리즘 필요
public interface SortingStrategy {
    void sort(int[] array);
}

public class QuickSort implements SortingStrategy {
    public void sort(int[] array) { /* 퀵정렬 구현 */ }
}

정리

구성 요소 역할 특징
Abstract Class 알고리즘 골격 정의 템플릿 메서드는 final로 선언
Template Method 실행 순서 제어 추상 메서드와 Hook 메서드 조합
Abstract Methods 필수 구현 메서드 하위 클래스에서 반드시 구현
Hook Methods 선택적 확장 지점 기본 구현 제공, 필요시 오버라이드
Concrete Class 구체적 구현 제공 추상 메서드를 실제로 구현

템플릿 메서드 패턴은 동일한 알고리즘 구조를 가지지만 세부 구현이 다른 경우에 적합합니다. Spring Framework의 Template 클래스들처럼 공통 로직을 추상화하고 변경 지점만 확장할 수 있도록 설계할 때 매우 유용한 패턴입니다.

References