Design Pattern

ν”„λ‘μ‹œ νŒ¨ν„΄ 정리

devJK93 2025. 4. 1.

πŸ“ ν”„λ‘μ‹œλž€ 무엇인가?

ν”„λ‘μ‹œ(Proxy)λž€ ν΄λΌμ΄μ–ΈνŠΈκ°€ μ„œλ²„μ— 직접 μš”μ²­ν•˜μ§€ μ•Šκ³ , 쀑간에 λŒ€λ¦¬ 객체λ₯Ό 톡해 κ°„μ ‘μ μœΌλ‘œ μš”μ²­μ„ μˆ˜ν–‰ν•˜λŠ” ꡬ쑰이닀. μ΄λ•Œ μ€‘κ°„μ˜ λŒ€λ¦¬ 객체가 λ°”λ‘œ 'ν”„λ‘μ‹œ'이닀. ν”„λ‘μ‹œλŠ” ν΄λΌμ΄μ–ΈνŠΈμ™€ μ„œλ²„ 사이에 μœ„μΉ˜ν•˜μ—¬ μš”μ²­μ„ κ°€λ‘œμ±„κ±°λ‚˜, 뢀가적인 μž‘μ—…μ„ μˆ˜ν–‰ν•˜κ±°λ‚˜, μš”μ²­μ„ μ œμ–΄ν•  수 μžˆλ‹€.

 

일반적으둜 ν΄λΌμ΄μ–ΈνŠΈλŠ” μžμ‹ μ΄ μš”μ²­ν•˜λŠ” λŒ€μƒμ΄ μ‹€μ œ μ„œλ²„μΈμ§€, ν”„λ‘μ‹œμΈμ§€λ₯Ό μ•Œμ§€ λͺ»ν•œλ‹€. λ”°λΌμ„œ ν”„λ‘μ‹œλŠ” μ„œλ²„μ™€ λ™μΌν•œ μΈν„°νŽ˜μ΄μŠ€λ₯Ό κ΅¬ν˜„ν•΄μ•Ό ν•œλ‹€. 이둜 인해 ν΄λΌμ΄μ–ΈνŠΈλŠ” ν”„λ‘μ‹œλ“  μ„œλ²„λ“  λ™μΌν•œ λ°©μ‹μœΌλ‘œ ν˜ΈμΆœν•  수 있으며, ꡬ쑰가 λ³€κ²½λ˜μ–΄λ„ ν΄λΌμ΄μ–ΈνŠΈ μ½”λ“œλŠ” μ „ν˜€ 영ν–₯을 받지 μ•ŠλŠ”λ‹€.

ν”„λ‘μ‹œ νŒ¨ν„΄ 정리 - πŸ“ ν”„λ‘μ‹œλž€ 무엇인가?
μ„œλ²„μ™€ ν”„λ‘μ‹œλŠ” 같은 μΈν„°νŽ˜μ΄μŠ€λ₯Ό κ΅¬ν˜„

πŸ—οΈ ν”„λ‘μ‹œ νŒ¨ν„΄μ˜ ꡬ쑰

ν”„λ‘μ‹œ νŒ¨ν„΄μ˜ κΈ°λ³Έ κ΅¬μ‘°λŠ” λ‹€μŒκ³Ό κ°™λ‹€.

  • Client: μš”μ²­μ„ λ³΄λ‚΄λŠ” 주체이닀.
  • Proxy: μ„œλ²„ λŒ€μ‹  μš”μ²­μ„ λ°›μ•„ μ²˜λ¦¬ν•˜κ±°λ‚˜, μ„œλ²„μ—κ²Œ μ „λ‹¬ν•˜λŠ” 쀑간 객체이닀.
  • RealSubject (Server): μ‹€μ œ λΉ„μ¦ˆλ‹ˆμŠ€ λ‘œμ§μ„ μ²˜λ¦¬ν•˜λŠ” μ„œλ²„ 객체이닀.

ν”„λ‘μ‹œ νŒ¨ν„΄ 정리 - πŸ—οΈ ν”„λ‘μ‹œ νŒ¨ν„΄μ˜ ꡬ쑰
ν”„λ‘μ‹œ νŒ¨ν„΄

이 μ„Έ κ°μ²΄λŠ” λ‹€μŒκ³Ό 같은 μΈν„°νŽ˜μ΄μŠ€ ꡬ쑰λ₯Ό κ°–λŠ”λ‹€.

public interface Subject {
    String operation();
}

public class RealSubject implements Subject {
    public String operation() {
        return "μ‹€μ œ 객체 호좜 κ²°κ³Ό";
    }
}

public class Proxy implements Subject {
    private RealSubject realSubject;

    public Proxy(RealSubject realSubject) {
        this.realSubject = realSubject;
    }

    public String operation() {
        // λΆ€κ°€ κΈ°λŠ₯ μˆ˜ν–‰
        System.out.println("ν”„λ‘μ‹œμ—μ„œ μΆ”κ°€ μž‘μ—… μˆ˜ν–‰");
        return realSubject.operation();
    }
}
  

μ΄λ ‡κ²Œ κ΅¬μ„±λ˜λ©΄ ν΄λΌμ΄μ–ΈνŠΈλŠ” μ•„λž˜μ²˜λŸΌ μ‚¬μš©ν•  수 μžˆλ‹€.

public class Client {
    private Subject subject;

    public Client(Subject subject) {
        this.subject = subject;
    }

    public void execute() {
        String result = subject.operation();
        System.out.println("κ²°κ³Ό: " + result);
    }
}
  

이 κ΅¬μ‘°μ—μ„œ ν΄λΌμ΄μ–ΈνŠΈλŠ” Proxyλ₯Ό μ£Όμž…λ°›μ•„λ„, RealSubjectλ₯Ό μ£Όμž…λ°›μ•„λ„ μ „ν˜€ μ•Œ 수 μ—†μœΌλ©°, μ½”λ“œμ˜ μˆ˜μ • 없이 λ™μΌν•˜κ²Œ λ™μž‘ν•œλ‹€.

ν”„λ‘μ‹œ νŒ¨ν„΄κ³Ό λ°μ½”λ ˆμ΄ν„° νŒ¨ν„΄

GOF λ””μžμΈ νŒ¨ν„΄μ—μ„œλŠ” ν”„λ‘μ‹œ νŒ¨ν„΄κ³Ό λ°μ½”λ ˆμ΄ν„° νŒ¨ν„΄μ„ κ΅¬λΆ„ν•œλ‹€. λ‘˜ λ‹€ κ΅¬μ‘°μ μœΌλ‘œλŠ” μœ μ‚¬ν•˜μ§€λ§Œ, μ˜λ„(intent)κ°€ λ‹€λ₯΄λ‹€.

ν”„λ‘μ‹œ νŒ¨ν„΄ 정리 - ν”„λ‘μ‹œ νŒ¨ν„΄κ³Ό λ°μ½”λ ˆμ΄ν„° νŒ¨ν„΄
λ°μ½”λ ˆμ΄ν„° νŒ¨ν„΄

1. ν”„λ‘μ‹œ νŒ¨ν„΄ - μ ‘κ·Ό μ œμ–΄ λͺ©μ 

ν”„λ‘μ‹œ νŒ¨ν„΄μ€ 보톡 접근을 μ œμ–΄ν•˜κΈ° μœ„ν•΄ μ‚¬μš©λœλ‹€.

  • κΆŒν•œμ— λ”°λ₯Έ μ ‘κ·Ό 차단
  • κ²°κ³Όλ₯Ό μΊμ‹±ν•΄μ„œ μ„±λŠ₯ ν–₯상
  • 지연 λ‘œλ”©

μ˜ˆμ‹œ: μΊμ‹œ ν”„λ‘μ‹œ

public class CacheProxy implements Subject {
    private Subject target;
    private String cache;

    public CacheProxy(Subject target) {
        this.target = target;
    }

    public String operation() {
        if (cache == null) {
            cache = target.operation();
        }
        return cache;
    }
}
  

ν•œ 번만 μ„œλ²„μ— μ ‘κ·Όν•˜κ³ , μ΄ν›„μ—λŠ” μΊμ‹œλœ κ²°κ³Όλ₯Ό λ¦¬ν„΄ν•œλ‹€.

2. λ°μ½”λ ˆμ΄ν„° νŒ¨ν„΄ - κΈ°λŠ₯ ν™•μž₯ λͺ©μ 

λ°μ½”λ ˆμ΄ν„° νŒ¨ν„΄μ€ κΈ°μ‘΄ κΈ°λŠ₯에 μƒˆλ‘œμš΄ κΈ°λŠ₯을 μΆ”κ°€ν•˜λŠ” 데 μ‚¬μš©λœλ‹€. κ΅¬μ‘°λŠ” ν”„λ‘μ‹œμ™€ 거의 λ™μΌν•˜μ§€λ§Œ, λͺ©μ μ΄ λ‹€λ₯΄λ‹€.

μ˜ˆμ‹œ: λ©”μ‹œμ§€ λ³€κ²½ λ°μ½”λ ˆμ΄ν„°

public class MessageDecorator implements Subject {
    private Subject target;

    public MessageDecorator(Subject target) {
        this.target = target;
    }

    public String operation() {
        String result = target.operation();
        return "[λ°μ½”λ ˆμ΄μ…˜] " + result;
    }
}
  

ν”„λ‘μ‹œλ₯Ό μ—°μ†μ μœΌλ‘œ μ—°κ²°ν•˜λŠ” 체인 ν˜•νƒœλ‘œλ„ κ°€λŠ₯ν•˜λ‹€.

Subject realSubject = new RealSubject();
Subject decorator1 = new MessageDecorator(realSubject);
Subject decorator2 = new TimeDecorator(decorator1);

Client client = new Client(decorator2);
client.execute();
  

이처럼 λ°μ½”λ ˆμ΄ν„°λŠ” 체인을 κ΅¬μ„±ν•΄μ„œ 점점 κΈ°λŠ₯을 μΆ”κ°€ν•΄ λ‚˜κ°ˆ 수 μžˆλ‹€. ν”„λ‘μ‹œ μ—­μ‹œ λ§ˆμ°¬κ°€μ§€λ‘œ ν”„λ‘μ‹œ 체인을 ꡬ성할 수 μžˆλ‹€.

ν”„λ‘μ‹œ νŒ¨ν„΄μ˜ 핡심 정리

  • ν”„λ‘μ‹œλŠ” μ„œλ²„μ™€ λ™μΌν•œ μΈν„°νŽ˜μ΄μŠ€λ₯Ό κ΅¬ν˜„ν•΄μ•Ό ν•œλ‹€.
  • ν”„λ‘μ‹œλ₯Ό λ„μž…ν•΄λ„ ν΄λΌμ΄μ–ΈνŠΈ μ½”λ“œλŠ” λ³€κ²½ 없이 λ™μž‘ν•΄μ•Ό ν•œλ‹€.
  • ν”„λ‘μ‹œλŠ” 체인 ν˜•νƒœλ‘œ μ—¬λŸ¬ 개 μ—°κ²°ν•  수 μžˆλ‹€.
  • μ ‘κ·Ό μ œμ–΄λ‚˜ λΆ€κ°€ κΈ°λŠ₯ μΆ”κ°€ λ“±μ˜ 역할을 μˆ˜ν–‰ν•  수 μžˆλ‹€.
  • ν”„λ‘μ‹œλŠ” μŠ€ν”„λ§ AOPλ‚˜ ν”„λ‘μ‹œ 기반 DI κ΅¬ν˜„μ—μ„œ 맀우 μ€‘μš”ν•˜λ‹€.

μŠ€ν”„λ§μ—μ„œμ˜ μ‹€μ œ ν”„λ‘μ‹œ κ΅¬ν˜„ μ˜ˆμ‹œ

μ•„λž˜λŠ” μŠ€ν”„λ§μ—μ„œ μΈν„°νŽ˜μ΄μŠ€ 기반으둜 ν”„λ‘μ‹œλ₯Ό κ΅¬ν˜„ν•œ μ½”λ“œμ΄λ‹€.

@RequiredArgsConstructor
public class OrderServiceInterfaceProxy implements OrderServiceV1 {
    private final OrderServiceV1 target;
    private final LogTrace logTrace;

    public void orderItem(String itemId) {
        TraceStatus status = null;
        try {
            status = logTrace.begin("OrderService.orderItem()");
            target.orderItem(itemId);
            logTrace.end(status);
        } catch (Exception e) {
            logTrace.exception(status, e);
            throw e;
        }
    }
}
  

이런 λ°©μ‹μœΌλ‘œ OrderController, OrderRepository도 ν”„λ‘μ‹œ κ΅¬ν˜„μ²΄λ₯Ό λ§Œλ“€μ–΄, κΈ°μ‘΄ κ΅¬ν˜„μ²΄λ₯Ό κ°μ‹ΈλŠ” λ°©μ‹μœΌλ‘œ κΈ°λŠ₯을 ν™•μž₯ν•  수 μžˆλ‹€.

κ²°λ‘ 

ν”„λ‘μ‹œ νŒ¨ν„΄μ€ λ‹¨μˆœν•œ ꡬ쑰 κ°™μ§€λ§Œ, 섀계와 μœ μ—°μ„± μΈ‘λ©΄μ—μ„œ 맀우 μ€‘μš”ν•œ νŒ¨ν„΄μ΄λ‹€. 접근을 μ œμ–΄ν•˜κ±°λ‚˜, μ„±λŠ₯을 κ°œμ„ ν•˜κ±°λ‚˜, κΈ°λŠ₯을 ν™•μž₯ν•˜λŠ” 데 μœ μš©ν•˜κ²Œ μ‚¬μš©λœλ‹€. 특히 μŠ€ν”„λ§μ—μ„œλŠ” AOP, νŠΈλžœμž­μ…˜ 처리, λ‘œκΉ… λ“± λ§Žμ€ 뢀뢄이 ν”„λ‘μ‹œ 기반으둜 λ™μž‘ν•˜κ³  μžˆλ‹€. ν”„λ‘μ‹œλ₯Ό μ΄ν•΄ν•˜λŠ” 것은 μŠ€ν”„λ§μ„ μ΄ν•΄ν•˜λŠ” 데 맀우 큰 도움이 λœλ‹€.

λŒ“κΈ€