[Spring] 예제를 통해 템블릿 메서드 패턴에 대해 알아보기

2024. 12. 31. 11:18·Web

 

개요

만약 잘 만들어진 애플리케이션에서 각각의 클래스안에 있는 메서드에 실행시간 등 정형화된 로그를 추가하라는 미션이 주어졌을 때 제일 쉬운 방법은 각 메서드마다 로그를 출력하는 코드를 추가하는 것 일것이다.

예)

    private final OrderServiceV3 orderService;
    private final LogTrace trace; 

    @GetMapping("/v3/request")
    public String request(String itemId) {
        TraceStatus status = null;
        try {
            status = trace.begin("OrderController.request()"); // 로그 출력 시작
            orderService.orderItem(itemId);
            trace.end(status); // 로그 출력 끝
            return "ok";
        }catch (Exception e) {
            trace.exception(status, e);
            throw e; // 예외를 꼭 다시 던져주어야 한다.
        }
    }

 

하지만 위 예제에서는 핵심 기능보다 로그를 출력해야 하는 부가 기능 코드가 훨씬 더 많고 복잡하다. 

핵심기능과 부가기능에 대해 간략하게 설명하자면 

핵심 기능은 해당 객체가 제공하는 고유의 기능이다. 위 예제에서는 orderService.orderItem(itemId);가 핵심 기능이다.

부가기능은 핵심기능을 보조하기 위해 제공되는 기능이다. 예를 들어서 로그 추적, 트랜잭션 기능이 있다. 

 

만약 로그를 출력하는 클래스가 수백, 수천개가 된다면 로그 출력 코드만 수천, 수만줄이 될 것이고 소위 말하는 배보다 배꼽이 더 큰 상황이 될 것이다.

 

이러한 문제를 더 효율적으로 처리하는 방법이 있을까? 

좋은 설계는 변하는것과 변하지 않는 것을 분리하는 것이다. 

여기서 핵심 기능은 변하는 것이고, 로그 출력하는 부분은 변하지 않는 부분이다. 

이 둘을 분리해서 모듈화 하는게 더 효율적일 것이다. 

템플릿 메서드 패턴은 모듈화를 통해 이러한 문제를 해결하는 디자인 패턴이다.

 

 

템플릿 메서드 패턴(Template Method Pattern)

템플릿 메서드 패턴 이란?

템플릿 메서드 패턴은 여러 클래스에서 공통으로 사용하는 메서드를 템플릿화 하여 상위 클래스에 정의하고, 하위클래스마다 세부 동작 사항을 다르게 구현하는 패턴이다. 

변하지 않는 기능은 상위 클래스에 정의하고 자주 변경되는 기능은 하위클래스에 만들도록 한다. 

 

그래서 어떻게 사용하는데?

@Slf4j
public class TemplateMethodTest {

    @Test
    void templateMethodV0(){
        logic1();
        logic2();
    }

    private void logic1(){
        long startTime = System.currentTimeMillis();

        // 비즈니스 로직 실행
        log.info("비즈니스 로직1 실행");
        // 비즈니스 로직 종료
        long endTime = System.currentTimeMillis();
        long resultTime = endTime - startTime;
        log.info("resultTime={}", resultTime);
    }

    private void logic2(){
        long startTime = System.currentTimeMillis();

        // 비즈니스 로직 실행
        log.info("비즈니스 로직2 실행");

        // 비즈니스 로직 종료
        long endTime = System.currentTimeMillis();
        long resultTime = endTime - startTime;
        log.info("resultTime={}", resultTime);
    }
 }

만약 실행시간을 측정하는 테스트 코드를 작성해보자. 

간단한 두 테스트 메서드를 작성했다. logic1(), logic2() 메서드 둘다 실행시간을 측정하는 코드가 중복되어져 있고 핵심기능과 부가기능이 함께 존재한다. 

이제 템플릿 메서드 패턴을 사용해서 핵심 기능과 부가기능을 분리해보자.

템플릿 메서드 패턴 예

 

@Slf4j
public abstract class AbstractTemplate {

    public void execute(){
        long startTime = System.currentTimeMillis();

        // 비즈니스 로직 실행
        call();
        // 비즈니스 로직 종료
        
        long endTime = System.currentTimeMillis();
        long resultTime = endTime - startTime;
        log.info("resultTime={}", resultTime);
    }

    protected abstract void call();
}

변하지 않는 부분(실행시간 측정)은 여기다가 만들고

call() 메서드를 추상 메서드로 만든 후 상속을 받는 객체에 따라 달라지도록 설계를 한다. 

 

템플릿 메서드 패턴은 이름 그대로 템플릿을 사용하는 방식이다. 템플릿은 거대한 틀이라고 생각하고 변하지 않는 부분을 몰아둔다. 템플릿 메서드 패턴은 부모 클래스에 변하지 않는 템플릿 코드를 두고 변하는 부분은 자식 클래스에 두고 상속과 오버라이딩을 사용해서 처리한다. 

@Slf4j
public class SubClassLogic1 extends AbstractTemplate{
    @Override
    protected void call() {
      log.info("비즈니스 로직 1 실행");
    }
}

이런식으로 템플릿을 만든 부모클래스를 상속 받고 추상 메서드(call())을 오버라이딩 해 핵심 기능을 여기다 작성한다. 

 

이제 테스트를 진행해보자.

    @Test
    void templateMethodV1(){
        AbstractTemplate template1 = new SubClassLogic1();
        template1.execute();

        AbstractTemplate template2 = new SubClassLogic2();
        template2.execute();
    }

템플릿 메서드 실행결과

 

그럼 어떻게 실행될까?

템플릿 메서드 인스턴스 호출 그림

template1.execute(); 를 실행하면 템플릿 로직인 AbstractTemplate.execute()를 실행한다. 여기서 중간에 call() 메서드를 호출하는데 이 부분은 오버라이딩 되어있는 SubclassLogic1 인스턴스인 SubclassLogic1.call() 메서드가 호출된다. 

이렇게 다형성을 사용해서 변하는 부분과 변하지 않는 부분을 분리하는 방법을 템플릿 메서드 패턴이라고 할 수 있다.

 

익명 내부 클래스를 이용한 템플릿 메서드 단점 보완

예제에서는 SubclassLogic1, SubclassLogic2 처럼 클래스를 계속 만들어야 하는 단점이 있다.

이럴 때 익명 내부 클래스를 사용하면 익명 내부 클래스를 사용하여 이런 단점을 보완할 수 있다.

        AbstractTemplate template1 = new AbstractTemplate() {
            @Override
            protected void call() {
                log.info("비즈니스 로직 1 실행");
            }
        };

객체를 생성할 때 뒤에 '{ }' 을 붙이면 객체를 생성함과 동시에 구현체를 만들 수 있다.

 

익명 내부클래스의 클래스 정보를 한번 찍어보자

log.info("클래스 1 이름 = {}", template1.getClass());

 

실행결과

11:15:08.047 [Test worker] INFO hello.advanced.trace.template.TemplateMethodTest -- 클래스 1 이름 = class hello.advanced.trace.template.TemplateMethodTest$1

실행결과를 보면 자바가 임의로 만들어주는 익명 내부 클래스 이름은 'TemplateMethodTest$1'인 것을 확인할 수 있다.

여기서 $1같은 임의의 숫자는 자바가 붙여주는 것이다. 

 

그럼 단점은 없을까?

템플릿 메서드 패턴은 상속을 이용한다. 따라서 상속에서 오는 단점들을 그대로 안고간다. 특히 자식 클래스가 부모 클래스와 컴파일 시점에 강하게 결합이 되는 문제가 있다. 상속을 받는다는것은 특정 부모 클래스를 의존하고 있다는 뜻이다.  

 

자식 클래스 입장에서는 부모 클래스 부모 클래스의 기능을 전혀 사용하지 않는다. 그럼에도 불구하고 템플릿 메서드 패턴을 위해 부모클래스를 상속받고 있다. 그래서 부모 클래스를 사용하면 자식 클래스에도 영향을 줄 수 있다.

 

이러한 단점들을 보완하면서 템플릿 메서드 패턴과 비슷한 패턴이 있는데

그 패턴이 바로 전략 패턴(Strategy Pattern)이다.

 

'Web' 카테고리의 다른 글

[Spring] 예제를 통해 프록시(Proxy), 데코레이터(Decorator) 패턴에 대해 알아보기  (0) 2025.01.02
[Spring] 예제를 통해 전략 패턴(Strategy Pattern)에 대해 알아보기  (0) 2025.01.02
[Spring Boot] Test 클래스에서 lombok을 사용하고 싶을 때  (1) 2024.11.18
서블릿과 멀티쓰레드 이해 및 HTTP API, CSR/SSR 개념 정리  (2) 2024.09.21
'Web' 카테고리의 다른 글
  • [Spring] 예제를 통해 프록시(Proxy), 데코레이터(Decorator) 패턴에 대해 알아보기
  • [Spring] 예제를 통해 전략 패턴(Strategy Pattern)에 대해 알아보기
  • [Spring Boot] Test 클래스에서 lombok을 사용하고 싶을 때
  • 서블릿과 멀티쓰레드 이해 및 HTTP API, CSR/SSR 개념 정리
Economy98
Economy98
공부하고 기록하기
  • Economy98
    Economy_Dev
    Economy98
  • 전체
    오늘
    어제
    • 분류 전체보기 (74)
      • Spring Framework (11)
      • BOJ, Programmers (22)
      • Java (4)
      • JDBC (6)
      • JPA (9)
      • Spring Transaction (3)
      • Algorithm (1)
      • Web (5)
      • Projects (2)
        • 쇼핑몰 프로젝트 (0)
        • 열람실 & 도서관 프로젝트 (2)
      • Network (2)
      • 나의 공부방 (5)
      • 끄적끄적 (1)
      • Error Log (3)
      • CS (0)
  • 블로그 메뉴

    • 홈
    • 태그
    • 방명록
    • Github
  • 링크

    • Github
  • 공지사항

  • 인기 글

  • 태그

    propagation
    스프링부트
    백준
    JPA
    정렬
    자바
    Spring
    백준 풀이
    restful api
    예외 처리
    자바 문제
    다이나믹 프로그래밍
    트랜잭션
    자바 문제 풀이
    java
    백준 자바 풀이
    브루트포스 알고리즘
    그리디 알고리즘
    스프링
    jdbc
  • 최근 댓글

  • 최근 글

  • hELLO· Designed By정상우.v4.10.1
Economy98
[Spring] 예제를 통해 템블릿 메서드 패턴에 대해 알아보기
상단으로

티스토리툴바