본문 바로가기
Spring

[스프링 핵심 원리 - 고급편] 3. 템플릿 메서드 패턴으로 중복 코드 제거하기

by doogfoot 2023. 12. 28.

[스프링 핵심 원리 - 고급편] 3. 템플릿 메서드 패턴

 

템플릿 메서드 패턴

 

 

 

템플릿 메서드 패턴 - 정의

다수의 책과 문서에서는 템플릿 메서드 패턴을 다음과 같이 정의하고 있습니다.

 

1. 알고리즘의 골격을 정의하고 일부 단계를 하위 클래스로 연기하는 것

 

2. 부모 클래스에서 알고리즘의 골격을 정의하고 자식 클래스들에서 오버라이드를 이용해 특정 단계를 개발하는 패턴

 

3. 상속을 통해 슈퍼 클래스의 기능을 확장할 때 사용하는 방법, 변하지 않는 기능은 슈퍼클래스에 만들어 두고 자주 변경되며 확장할 기능은 서브 클래스에서 만드는 패턴

 

4. 변하지 않는 기능은 슈퍼클래스에 만들어두고 자주 변경되며 확장할 기능은 서브클래스에서 만들도록 하는것

 

공통적인 내용만 정리하면 변하지 않는 전체 흐름은 부모 클래스에 정의하고 중간에 변경되는 로직은 자식 클래스에서 재정의하는 패턴을 의미합니다.

 

 

템플릿 메서드 패턴 - 예시

예시로 두가지 서비스를 만드는데 각각의 서비스에서 실행 로그를 출력하는 코드를 개발한다고 가정해 보겠습니다.

public class Select {
	public void SelectEmp(){
    		log.info("서비스 실행");
    		selectEmp(); // 핵심 로직
    		log.info("서비스 종료");
	}
}

public class Insert {
	public void InsertEmp(){
    		log.info("서비스 실행");
    		insertEmp(); // 핵심 로직
    		log.info("서비스 종료");
	}
}

 

위의 두 서비스의 공통점은 로직의 흐름이 비슷하다는 점입니다.

 

서비스 실행 로그 -> 핵심 로직 수행 -> 서비스 종료 로그 순으로 진행된다는 점이 똑같습니다.

 

위와 같이 서비스를 개발하더라도 실행에는 전혀 문제가 없습니다만 앞으로 유지 보수에 있어서 여러 가지 문제가 발생할 수 있습니다.

 

예를 들어 로거 변수명을 "log"에서 "logger"로 변경한다던가, 로그로 남길 메시지를 "서비스 실행"에서 "서비스 시작"으로 변경하는 등의 수정이 필요할 수 있습니다.

 

그때마다 모든 서비스를 찾아가서 변경할 수도 있지만 서비스가 100개, 1000개로 늘어나면 수정이 쉽지 않고 시간이 오래 걸립니다.

 

public abstract class AbstractServiceTemplate {
	public void execute() {
    	log.info("서비스 실행");
    	logic();
    	log.info("서비스 종료");
    }
    
    protected abstract void logic();    
}


public class Select extends AbstractServiceTemplate {
	public void logic() {
    		selectEmp(); // 핵심 로직    		
	}
}

public class Insert extends AbstractServiceTemplate {
	public void logic() {    		
    		insertEmp(); // 핵심 로직    		
	}
}

 

위와 같이 템플릿 메서드 패턴을 사용하여 코드를 리팩토링 할 수 있습니다.

 

두 서비스의 부모 클래스가 되는 추상클래스(AbstractServiceTemplate)를 하나 만들고 서비스의 공통된 흐름을 정의했습니다.

 

그리고 서비스별로 달라지는 부분은 logic()이라는 추상 메서드로 선언하여 자식 클래스에서 구현하도록 강제했습니다.

 

이렇게 코드를 리팩토링 하면 서비스가 늘어나더라도 로그를 찍는 코드가 중복되지 않아서 유지보수를 효율적으로 할 수 있습니다.

 

public void executeService() {
    AbstractServiceTemplate service1 = new Select();
    service1.excute();
    
    AbstractServiceTemplate service2 = new Insert();
    service2.excute();
}

 

각각의 서비스를 호출하는 방법은 위와 같이 할 수 있습니다.

 

 

템플릿 메서드 패턴 - 장점

공통적으로 사용하는 로직을 하나의 코드로 만들어 중복을 방지할 수 있습니다.

 

 

템플릿 메서드 패턴 - 단점

템플릿 메서드 패턴은 Java의 상속을 이용하여 문제점을 해결하는 패턴입니다. 

 

따라서 상속에서 오는 단점을 그대로 가지고 가게 됩니다.

 

상속을 이용하면 부모 클래스의 불필요한 기능까지 상속받으며 부모 클래스와 의존성이 강하게 생깁니다.

 

부모 클래스에 변경이 생기면 자식 클래스를 변경해야 되는 경우도 발생할 수 있습니다.

 

상속을 위한 추가적인 클래스를 만들어야 사용할 수 있어서 리소스가 많아지고 복잡할 수 있습니다.

 

서비스 로직의 흐름의 다양하다면 오히려 유지보수가 더 어려워질 수도 있습니다.

 

 

템플릿 메서드 패턴 - 정리

이제 이 패턴의 이름이 왜 템플릿 메서드 패턴인지 이해할 수 있게 되었습니다.

 

템플릿은 틀, 포맷, 형식과 패턴 등을 의미합니다.

 

공통된 부분은 템플릿으로 만들고 일부 로직만 변경하고 싶을 때 사용할 수 있습니다.

 

개발하면서 변하지 않는 공통부분과 변하는 부분을 식별하고 나누는 습관을 가지면 좋을 것 같습니다.

 

댓글