이 글에서 얻는 것

  • Factory 패턴으로 객체 생성을 유연하게 관리합니다.
  • Strategy 패턴으로 알고리즘을 런타임에 교체합니다.
  • Template Method 패턴으로 공통 로직을 재사용합니다.
  • 각 패턴의 사용 시점과 트레이드오프를 판단할 수 있습니다.

0) 디자인 패턴은 “반복되는 설계 문제의 해결책”

디자인 패턴은 소프트웨어 설계에서 자주 발생하는 문제에 대한 검증된 해결책입니다.

GoF(Gang of Four) 패턴 분류:

  • 생성 패턴: 객체 생성 메커니즘 (Factory, Builder, Singleton)
  • 구조 패턴: 클래스/객체 조합 (Adapter, Decorator, Proxy)
  • 행위 패턴: 객체 간 협력 (Strategy, Template Method, Observer)

이 글에서는 백엔드에서 가장 많이 쓰이는 3가지를 다룹니다.

1) Factory 패턴: 객체 생성을 캡슐화

“객체 생성 로직을 별도 클래스로 분리”

1-1) 문제 상황

// ❌ 클라이언트가 구체 클래스에 의존
public class PaymentService {
    public void processPayment(String type, int amount) {
        Payment payment;

        if (type.equals("CARD")) {
            payment = new CardPayment();
        } else if (type.equals("BANK")) {
            payment = new BankTransferPayment();
        } else if (type.equals("PAYPAL")) {
            payment = new PayPalPayment();
        } else {
            throw new IllegalArgumentException("Unknown payment type");
        }

        payment.pay(amount);
    }
}

문제점:

  • 새 결제 수단 추가 시 PaymentService 수정 필요
  • if-else가 여러 곳에 중복
  • OCP(개방-폐쇄 원칙) 위반

1-2) Factory Method 패턴 적용

// 인터페이스
public interface Payment {
    void pay(int amount);
}

// 구현체들
public class CardPayment implements Payment {
    @Override
    public void pay(int amount) {
        System.out.println("Card payment: " + amount);
    }
}

public class BankTransferPayment implements Payment {
    @Override
    public void pay(int amount) {
        System.out.println("Bank transfer: " + amount);
    }
}

// Factory 클래스
public class PaymentFactory {
    public static Payment createPayment(String type) {
        switch (type) {
            case "CARD":
                return new CardPayment();
            case "BANK":
                return new BankTransferPayment();
            case "PAYPAL":
                return new PayPalPayment();
            default:
                throw new IllegalArgumentException("Unknown payment type: " + type);
        }
    }
}

// ✅ 사용
public class PaymentService {
    public void processPayment(String type, int amount) {
        Payment payment = PaymentFactory.createPayment(type);  // Factory 사용
        payment.pay(amount);
    }
}

장점:

  • 객체 생성 로직이 한 곳에 집중
  • 클라이언트는 인터페이스만 의존
  • 새 타입 추가 시 Factory만 수정

1-3) Spring에서의 Factory 패턴

// Spring이 Factory 역할
@Configuration
public class PaymentConfig {

    @Bean
    public Payment cardPayment() {
        return new CardPayment();
    }

    @Bean
    public Payment bankPayment() {
        return new BankTransferPayment();
    }

    // 팩토리 메서드
    @Bean
    public PaymentFactory paymentFactory(List<Payment> payments) {
        return new PaymentFactory(payments);
    }
}

@Service
public class PaymentService {
    private final PaymentFactory factory;

    public PaymentService(PaymentFactory factory) {
        this.factory = factory;
    }

    public void processPayment(String type, int amount) {
        Payment payment = factory.getPayment(type);
        payment.pay(amount);
    }
}

👉 다음 편: 디자인 패턴 필수 (Part 2: Strategy, 실무 적용)