Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
189 changes: 189 additions & 0 deletions jihyeon/08.템플릿_메서드_패턴.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,189 @@
# **템플릿 메서드 패턴 (Template Method Pattern)**

알고리즘의 전체 흐름은 부모 클래스가 정의하고, 일부 단계의 구현만 자식 클래스에 맡기는 패턴
**순서는 부모가 정하고, 세부 동작은 자식이 구현하는 구조**

### **문제 상황**

여러 클래스가 비슷한 처리 흐름을 가지지만, 일부 단계만 다를 때가 존재

예를 들어 차와 커피는 모두:

1. 물을 끓이고
2. 재료를 우려내고
3. 컵에 따르고
4. 첨가물을 넣습니다

전체 흐름은 비슷하지만, 실제로 우리는 방식과 첨가물은 다르다.

이걸 각 클래스가 전부 직접 구현하면:

- 공통 코드가 중복
- 알고리즘 흐름이 흩어짐
- 수정할 때 여러 클래스를 함께 고쳐야 함

### **해결책**

공통된 절차는 부모 클래스에 두고, 달라지는 단계만 추상 메서드로 분리

```ts
abstract class CaffeineBeverage {
prepareRecipe(): void {
this.boilWater();
this.brew();
this.pourInCup();
this.addCondiments();
}

protected abstract brew(): void;
protected abstract addCondiments(): void;

protected boilWater(): void {
console.log("Boiling water");
}

protected pourInCup(): void {
console.log("Pouring into cup");
}
}
```

자식 클래스는 필요한 부분만 구현

```ts
class Tea extends CaffeineBeverage {
protected brew(): void {
console.log("Steeping the tea");
}

protected addCondiments(): void {
console.log("Adding Lemon");
}
}

class Coffee extends CaffeineBeverage {
protected brew(): void {
console.log("Dripping Coffee through filter");
}

protected addCondiments(): void {
console.log("Adding Sugar and Milk");
}
}
```

## **핵심 구조**

- **Template Method**: 전체 순서를 정의하는 메서드
- **Primitive Operation**: 자식이 구현해야 하는 단계
- **Hook**: 필요할 때만 선택적으로 오버라이드하는 확장 포인트

### **Hook**

훅은 자식 클래스가 **선택적으로 개입할 수 있는 지점**

```ts
protected customerWantsCondiments(): boolean {
return true;
}
```

자식 클래스는 필요하면 이 메서드를 오버라이드해서 특정 단계를 건너뛸 수 있다.

## **프론트엔드 개발에서의 사례**

전통적인 클래스 상속 코드보다, **프레임워크가 전체 흐름을 잡고 개발자가 일부 단계만 구현하는 구조**에서 템플릿 메서드 패턴과 비슷한 개념을 자주 볼 수 있다.

#### **1. React 컴포넌트 생명주기**

클래스형 컴포넌트에서는 React가 전체 렌더링 흐름을 관리하고, 개발자는 특정 생명주기 메서드만 구현했습니다.

```ts
class MyComponent extends React.Component {
componentDidMount() {
console.log("데이터 요청");
}

componentWillUnmount() {
console.log("정리 작업");
}

render() {
return <div>Hello</div>;
}
}
```

- React가 전체 생명주기를 통제
- 개발자는 정해진 시점의 동작만 작성

즉, **프레임워크가 흐름을 갖고 있고 사용자가 일부 단계를 채우는 구조**라는 점에서 템플릿 메서드와 닮아 있다.

#### **2. 폼 처리 흐름**

폼도 보통 비슷한 흐름

1. 입력값 검증
2. 요청 전처리
3. API 호출
4. 성공/실패 후처리

제출 절차는 고정하고, 폼마다 검증이나 성공 처리만 다르게 둘 수 있다.

```ts
abstract class BaseFormHandler<T> {
async submit(values: T) {
if (!this.validate(values)) return;
const payload = this.serialize(values);
await this.request(payload);
this.onSuccess();
}

protected abstract validate(values: T): boolean;
protected abstract serialize(values: T): unknown;
protected abstract request(payload: unknown): Promise<void>;

protected onSuccess() {
console.log("success");
}
}
```

회원가입 폼, 로그인 폼, 프로필 수정 폼이 같은 제출 흐름을 공유할 때 유용하다.

### **3. 디자인 시스템 컴포넌트 내부 구조**

디자인 시스템에서도 내부적으로 비슷한 구조가 나올 수 있다.

예를 들어 여러 입력 컴포넌트가 공통적으로:

1. 공통 wrapper 렌더링
2. label 처리
3. 에러 메시지 처리
4. 실제 input 요소 렌더링

이라는 흐름을 가진다면, 공통 렌더링 구조를 상위 레벨에 두고 실제 input 부분만 바꿀 수 있다.

- TextField
- PasswordField
- SearchField

같은 컴포넌트들이 **공통 레이아웃 흐름은 공유하고, 세부 입력 요소만 다르게 구현**하는 방식

## **장점**

- 공통 로직을 재사용할 수 있음
- 전체 흐름을 부모가 통제할 수 있음
- 중복 코드가 줄어듦
- 구조가 일관되어 유지보수가 쉬움

## **단점**

- 상속에 의존함
- 부모 구조가 바뀌면 자식 클래스에 영향이 큼
- 단계가 많아지면 클래스 구조가 복잡해질 수 있음

## **전략 패턴과의 차이**

- **템플릿 메서드**: 상속으로 흐름을 재사용, 부모가 순서를 고정
- **전략 패턴**: 조합으로 동작을 교체, 알고리즘 자체를 바꿔 끼울 수 있다.