본문 바로가기

개발/디자인패턴

디자인 패턴 여러가지 예시(spring 기반)

 

✅ 1. Singleton Pattern – Spring Bean 관리의 기본

📌 목적

  • 객체를 하나만 생성해서 재사용한다
  • Spring의 모든 @Component, @Service, @Repository, @Controller는 기본적으로 싱글턴으로 관리됨

💡 Spring 방식

@Service
public class UserService {
    public String getUserName() {
        return "홍찬";
    }
}
@RestController
@RequiredArgsConstructor
public class UserController {
    private final UserService userService;

    @GetMapping("/user")
    public String getUser() {
        return userService.getUserName();
    }
}

👉 Spring 컨테이너가 UserService 인스턴스를 하나만 생성해서 모든 곳에 주입 (DI)함


✅ 2. Factory Method Pattern – Bean 생성 로직 분리

📌 목적

  • 조건에 따라 다른 객체를 생성하고 싶을 때

💡 Spring 방식 (@Bean 사용)

public interface NotificationService {
    void send(String msg);
}

@Service
public class EmailService implements NotificationService {
    public void send(String msg) {
        System.out.println("EMAIL: " + msg);
    }
}

@Service
public class SmsService implements NotificationService {
    public void send(String msg) {
        System.out.println("SMS: " + msg);
    }
}
@Component
public class NotificationFactory {
    private final Map<String, NotificationService> map;

    public NotificationFactory(List<NotificationService> services) {
        map = services.stream().collect(Collectors.toMap(
            s -> s.getClass().getSimpleName(), s -> s
        ));
    }

    public NotificationService get(String type) {
        return map.get(type);
    }
}
@RestController
@RequiredArgsConstructor
public class NotificationController {
    private final NotificationFactory factory;

    @GetMapping("/notify")
    public void notify(@RequestParam String type) {
        factory.get(type + "Service").send("가입 축하!");
    }
}

✅ 3. Builder Pattern – DTO 생성에 자주 사용

📌 목적

  • 복잡한 객체 생성 시 필드 순서 없이 유연하게 설정

💡 Spring + Lombok 예시

@Getter
@Builder
@AllArgsConstructor
public class UserDTO {
    private String name;
    private String email;
    private int age;
}
UserDTO user = UserDTO.builder()
    .name("홍찬")
    .email("hc@example.com")
    .age(33)
    .build();

✅ 4. Strategy Pattern – 유연한 정책 분리

📌 목적

  • 런타임에 알고리즘을 교체 가능하게 설계

💡 할인 정책 서비스 예시

public interface DiscountPolicy {
    int discount(int price);
}

@Component("fixed")
public class FixedDiscount implements DiscountPolicy {
    public int discount(int price) {
        return price - 1000;
    }
}

@Component("rate")
public class RateDiscount implements DiscountPolicy {
    public int discount(int price) {
        return (int)(price * 0.9);
    }
}
@Service
public class OrderService {
    private final Map<String, DiscountPolicy> policyMap;

    public OrderService(Map<String, DiscountPolicy> policyMap) {
        this.policyMap = policyMap;
    }

    public int calculate(String type, int price) {
        return policyMap.get(type).discount(price);
    }
}

✅ 5. Template Method Pattern – 공통 처리 흐름 정의

📌 목적

  • 처리 흐름은 고정, 세부 내용은 오버라이드

💡 공통 로깅 처리

public abstract class AbstractService {
    public void execute() {
        long start = System.currentTimeMillis();
        call(); // 핵심 로직
        long end = System.currentTimeMillis();
        System.out.println("실행 시간: " + (end - start));
    }

    protected abstract void call();
}
@Component
public class UserJoinService extends AbstractService {
    @Override
    protected void call() {
        System.out.println("회원 가입 처리");
    }
}
// 사용 예
userJoinService.execute();

✅ 6. Proxy Pattern – AOP 기반 기능 확장

📌 목적

  • 실제 로직 앞뒤로 공통 기능을 끼워 넣음 (트랜잭션, 로깅 등)

💡 Spring AOP 기반 로깅

@Aspect
@Component
public class LoggingAspect {

    @Around("execution(* com.example.service..*(..))")
    public Object logExecutionTime(ProceedingJoinPoint joinPoint) throws Throwable {
        long start = System.currentTimeMillis();
        Object result = joinPoint.proceed();  // 실제 메서드 실행
        long end = System.currentTimeMillis();
        System.out.println("[LOG] " + joinPoint.getSignature() + " 실행 시간: " + (end - start));
        return result;
    }
}

✅ 7. Observer Pattern – 이벤트 발행/구독

📌 목적

  • 이벤트를 발행하면 여러 컴포넌트가 반응

💡 Spring Event 기반 예시

public class UserJoinedEvent extends ApplicationEvent {
    private final String username;

    public UserJoinedEvent(Object source, String username) {
        super(source);
        this.username = username;
    }

    public String getUsername() { return username; }
}
@Component
public class UserEventListener {
    @EventListener
    public void handleUserJoined(UserJoinedEvent event) {
        System.out.println("유저 가입 처리: " + event.getUsername());
    }
}
// 발행
eventPublisher.publishEvent(new UserJoinedEvent(this, "hongchan"));

🔚 정리: Spring Boot 실무에서 자주 쓰는 패턴 요약

패턴 사용 예 적용 위치

Singleton @Service, @Component Bean 생성 방식
Factory 전략 구현체 선택 인터페이스 기반 객체 생성
Builder DTO 생성 Lombok @Builder
Strategy 정책 변경 (할인, 로직) Map DI로 유연한 선택
Template 공통 처리 흐름 추상 클래스 기반
Proxy AOP (로깅, 트랜잭션) Aspect 구성
Observer 이벤트 알림 @EventListener

 

'개발 > 디자인패턴' 카테고리의 다른 글

디자인패턴 종류  (1) 2025.05.23