프로젝트/마이그레이션

스프링에서 전략 패턴(Strategy Pattern) 활용하기

10Biliion 2025. 2. 3. 18:32

 

 

1. 전략 패턴이란?

전략 패턴(Strategy Pattern)은 특정 동작을 캡슐화하여 런타임에 동적으로 변경할 수 있도록 하는 디자인 패턴입니다. 인터페이스를 통해 여러 개의 구현 클래스를 정의하고, 실행 시점에 원하는 구현체를 선택하는 방식으로 동작합니다. 이 패턴을 활용하면 유지보수성과 확장성을 높일 수 있으며, if-else 문을 최소화할 수 있습니다.

2. 전략 패턴을 적용한 코드 예제

📌 예제: 결제 방식 선택 서비스

아래 코드는 온라인 쇼핑몰에서 결제 방식을 선택하는 로직을 전략 패턴으로 구현한 예제입니다. 사용자가 결제 수단을 선택하면 해당 결제 방식에 맞는 서비스가 실행됩니다.

🔹 결제 전략 인터페이스 정의

먼저, PaymentStrategy라는 인터페이스를 정의합니다.

package com.example.payment.service;

import java.util.Map;

public interface PaymentStrategy {
    /**
     * 결제 수행 메서드
     * @param amount 결제 금액
     * @return 결제 결과
     */
    Map<String, Object> processPayment(int amount);
}

🔹 결제 수단별 구현체

각 결제 방식에 대한 구현체를 작성합니다.

1) 신용카드 결제 서비스

package com.example.payment.service.impl;

import com.example.payment.service.PaymentStrategy;
import org.springframework.stereotype.Service;

import java.util.HashMap;
import java.util.Map;

@Service("creditCard")
public class CreditCardPayment implements PaymentStrategy {
    @Override
    public Map<String, Object> processPayment(int amount) {
        Map<String, Object> response = new HashMap<>();
        response.put("status", "success");
        response.put("method", "Credit Card");
        response.put("amount", amount);
        return response;
    }
}

2) 페이팔 결제 서비스

package com.example.payment.service.impl;

import com.example.payment.service.PaymentStrategy;
import org.springframework.stereotype.Service;

import java.util.HashMap;
import java.util.Map;

@Service("paypal")
public class PayPalPayment implements PaymentStrategy {
    @Override
    public Map<String, Object> processPayment(int amount) {
        Map<String, Object> response = new HashMap<>();
        response.put("status", "success");
        response.put("method", "PayPal");
        response.put("amount", amount);
        return response;
    }
}

🔹 결제 컨트롤러 (전략 패턴 적용)

컨트롤러에서는 결제 방식을 동적으로 선택하여 실행할 수 있도록 Map<String, PaymentStrategy>을 활용합니다.

package com.example.payment.controller;

import com.example.payment.service.PaymentStrategy;
import lombok.RequiredArgsConstructor;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;

import java.util.Map;

@RestController
@RequiredArgsConstructor
@RequestMapping("payment")
public class PaymentController {
    
    private final Map<String, PaymentStrategy> paymentStrategies;

    @PostMapping("{method}/{amount}")
    public ResponseEntity<?> processPayment(
            @PathVariable String method,
            @PathVariable int amount) {
        
        PaymentStrategy paymentStrategy = paymentStrategies.get(method);
        
        if (paymentStrategy == null) {
            return ResponseEntity.badRequest().body("지원되지 않는 결제 방식입니다.");
        }
        
        Map<String, Object> response = paymentStrategy.processPayment(amount);
        return ResponseEntity.ok(response);
    }
}

🔹 동작 방식

  • 사용자가 POST /payment/creditCard/1000 요청을 보내면, CreditCardPayment 클래스가 실행됩니다.
  • 사용자가 POST /payment/paypal/5000 요청을 보내면, PayPalPayment 클래스가 실행됩니다.
  • 새로운 결제 수단이 추가되더라도 PaymentController를 수정할 필요 없이 새로운 클래스만 추가하면 됩니다.

3. 전략 패턴을 활용하는 이유

유지보수성 향상

  • 기존 코드 수정 없이 새로운 기능(새로운 결제 방식)을 쉽게 추가할 수 있습니다.

유연성 증가

  • 런타임에 원하는 결제 방식을 선택할 수 있습니다.

if-else 구문 최소화

  • if-else 대신 Map<String, PaymentStrategy>을 활용하여 코드가 간결해집니다.

 

 

 

 

전략 패턴과 스프링 빈 자동 주입 기능을 활용하여 인터페이스 기반의 유연한 설계를 구현할 수 있습니다. 새로운 결제 방식이 추가될 경우, 기존 코드를 수정하지 않고도 쉽게 확장할 수 있습니다. 이 패턴은 결제뿐만 아니라, 알림 시스템, 인증 방식, 데이터 변환, 자동완성 기능 등 다양한 곳에 적용할 수 있습니다.