μΉ΄ν…Œκ³ λ¦¬ μ—†μŒ

[Spring] λ””μžμΈ νŒ¨ν„΄ : 원본 μ½”λ“œλ₯Ό μ†λŒ€μ§€ μ•Šκ³  λΆ€κ°€κΈ°λŠ₯ μ μš©ν•˜κΈ°

sian han 2025. 6. 11. 18:05

πŸŽ“ 이 글은 μΈν”„λŸ°μ—μ„œ μ œκ³΅ν•˜λŠ” "μŠ€ν”„λ§ ν•΅μ‹¬ μ›λ¦¬ - κ³ κΈ‰νŽΈ" κ°•μ˜λ₯Ό μˆ˜κ°•ν•˜λ©΄μ„œ μ •λ¦¬ν•œ λ‚΄μš©μ„ λ°”νƒ•μœΌλ‘œ μž‘μ„±ν•œ κΈ€μž…λ‹ˆλ‹€.

https://inf.run/FWeFN

 

이전글

 

[Spring] λ””μžμΈ νŒ¨ν„΄ : 적은 μ½”λ“œ μˆ˜μ •μœΌλ‘œ 곡톡 λ‘œμ§μ„ μž‘μ„±ν•˜κΈ°

πŸŽ“ 이 글은 μΈν”„λŸ°μ—μ„œ μ œκ³΅ν•˜λŠ” "μŠ€ν”„λ§ 핡심 원리 - κ³ κΈ‰νŽΈ" κ°•μ˜λ₯Ό μˆ˜κ°•ν•˜λ©΄μ„œ μ •λ¦¬ν•œ λ‚΄μš©μ„ λ°”νƒ•μœΌλ‘œ μž‘μ„±ν•œ κΈ€μž…λ‹ˆλ‹€.https://inf.run/FWeFN 적은 μ½”λ“œ μˆ˜μ •μœΌλ‘œ 곡톡 λ‘œμ§μ„ μž‘μ„±ν•  수 있기 μœ„

feelfreetothink.tistory.com

 

ν…œν”Œλ¦Ώ λ©”μ„œλ“œ νŒ¨ν„΄, μ „λž΅ νŒ¨ν„΄, ν…œν”Œλ¦Ώ 콜백 νŒ¨ν„΄μ„ μ‚¬μš©ν•˜λ©΄ 적은 μ½”λ“œ μˆ˜μ •μœΌλ‘œ κ³΅ν†΅λ‘œμ§(λΆ€κ°€κΈ°λŠ₯) 을 μ μš©ν•  수 μžˆμ—ˆλ‹€. 

κ·ΈλŸ¬λ‚˜ κ²°κ΅­ 원본 μ½”λ“œλ₯Ό μˆ˜μ •ν•΄μ•Όν•œλ‹€λŠ” 단점이 μžˆμ—ˆλ‹€. 원본 μ½”λ“œλ₯Ό μ†λŒ€μ§€ μ•Šκ³  λΆ€κ°€κΈ°λŠ₯을 μ μš©ν•  수 μžˆλŠ” 방법을 μ•Œμ•„λ³΄μž. 

 

κ³΅ν†΅λ‘œμ§(둜그 좔적기) μš”κ΅¬μ‚¬ν•­

더보기

* λͺ¨λ“  PUBLIC λ©”μ„œλ“œμ˜ 호좜과 응닡 정보λ₯Ό 둜그둜 좜λ ₯

* μ• ν”Œλ¦¬μΌ€μ΄μ…˜μ˜ 흐름을 λ³€κ²½ν•˜λ©΄ μ•ˆλ¨ - 둜그λ₯Ό 남긴닀고 ν•΄μ„œ λΉ„μ¦ˆλ‹ˆμŠ€ 둜직의 λ™μž‘μ— 영ν–₯을 μ£Όλ©΄ μ•ˆλ¨

* λ©”μ„œλ“œ ν˜ΈμΆœμ— κ±Έλ¦° μ‹œκ°„

* μ •상 흐름과 μ˜ˆμ™Έ 흐름 ꡬ뢄 - μ˜ˆμ™Έ λ°œμƒ μ‹œ μ˜ˆμ™Έ 정보가 남아야 함

* λ©”μ„œλ“œ 호좜의 깊이 ν‘œν˜„

* HTTP μš”μ²­μ„ ꡬ뢄

* HTTP μš”μ²­ λ‹¨μœ„λ‘œ νŠΉμ • ID λ₯Ό λ‚¨κ²¨μ„œ μ–΄λ–€ HTTP μš”μ²­μ—μ„œ μ‹œμž‘λœ 것인지 λͺ…ν™•ν•˜κ²Œ ꡬ뢄이 κ°€λŠ₯ν•΄μ•Ό 함

* νŠΈλžœμž­μ…˜ ID (DBνŠΈλžœμž­μ…˜X)

* NEW ! λ³΄μ•ˆμƒ 일뢀 λ‘œκ·ΈλŠ” 좜λ ₯ν•˜λ©΄ μ•ˆλœλ‹€. 

 


 

  • ν”„λ‘μ‹œ νŒ¨ν„΄ : ν”„λ‘μ‹œλ‘œ μ ‘κ·Ό μ œμ–΄λ₯Ό ν•˜λŠ” 것 
  • λ°μ½”λ ˆμ΄ν„° νŒ¨ν„΄ : ν”„λ‘μ‹œλ‘œ λΆ€κ°€ κΈ°λŠ₯을 μΆ”κ°€ν•˜λŠ” 것

λ‘˜ λ‹€ ν”„λ‘μ‹œλ₯Ό μ‚¬μš©ν•˜μ§€λ§Œ, μ˜λ„κ°€ λ‹€λ₯΄λ‹€λŠ” 점이 핡심이닀. 

 

β€» ν”„λ‘μ‹œ νŒ¨ν„΄

λͺ©ν‘œ : ν”„λ‘μ‹œλ₯Ό μ‚¬μš©ν•œ μΊμ‹œλ₯Ό ν†΅ν•œ μ ‘κ·Ό μ œμ–΄

 

β–Ά ν”„λ‘μ‹œ νŒ¨ν„΄ ꡬ성

public interface Subject {
    String operation();
}
public class RealSubject implements Subject {
    @Override
    public String operation() {
        log.info("μ‹€μ œ 객체 호좜");
        sleep(1000);
        return "data";
    }

    private void sleep(int millis) {
        try {
            Thread.sleep(millis);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}
public class CacheProxy implements Subject {

    private Subject target;
    private String cacheValue;

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

    @Override
    public String operation() {
        log.info("ν”„λ‘μ‹œ 호좜");
        if (cacheValue == null) {
            cacheValue = target.operation();
        }
        return cacheValue;
    }
}

 

ν”„λ‘μ‹œ(CacheProxy) λŠ” 싀체 객체(RealSubject)κ³Ό κ·Έ λͺ¨μ–‘이 κ°™μ•„μ•Όν•˜κΈ° λ•Œλ¬Έμ— Subject μΈν„°νŽ˜μ΄μŠ€λ₯Ό κ΅¬ν˜„ν•΄μ•Ό ν•œλ‹€.

ν”„λ‘μ‹œλŠ” μ‹€μ œ 객체λ₯Ό ν˜ΈμΆœν•΄μ•Όν•˜κΈ° λ•Œλ¬Έμ— 내뢀에 μ‹€μ œ 객체의 μ°Έμ‘°λ₯Ό κ°€μ§€κ³  μžˆμ–΄μ•Ό ν•œλ‹€(target). 

cacheValue 에 값이 있으면 μ‹€μ œ 객체λ₯Ό μ „ν˜€ ν˜ΈμΆœν•˜μ§€ μ•Šκ³  μΊμ‹œκ°’μ„ κ·ΈλŒ€λ‘œ λ°˜ν™˜ν•œλ‹€. λ”°λΌμ„œ 처음 쑰회 μ΄ν›„μ—λŠ” cacheValue μ—μ„œ 맀우 λΉ λ₯΄κ²Œ 데이터λ₯Ό μ‘°νšŒν•  수 μžˆλ‹€. 

 

 

β–Ά ν”„λ‘μ‹œ νŒ¨ν„΄ μ‚¬μš©

@Test
void cacheProxyTest() {
    RealSubject realSubject = new RealSubject();
    CacheProxy cacheProxy = new CacheProxy(realSubject);
    ProxyPatternClient client = new ProxyPatternClient(cacheProxy);
    client.execute();
    client.execute();
    client.execute();
}

 

CacheProxy κ°€ RealSubject λ₯Ό μ£Όμž… λ°›μœΌλ©΄μ„œ CacheProxyκ°€ RealSubject을 μ°Έμ‘°ν•˜λŠ” λŸ°νƒ€μž„ 객체 의쑴 관계가 μ™„μ„±λœλ‹€. 

Client μ—λŠ” RealSubject κ°€ μ•„λ‹Œ CacheProxyλ₯Ό μ£Όμž…ν•œλ‹€.

결과적으둜 μΊμ‹œ ν”„λ‘μ‹œλ₯Ό λ„μž…ν•˜κΈ° μ „μ—λŠ” 3μ΄ˆκ°€ κ±Έλ Έμ§€λ§Œ, μΊμ‹œ ν”„λ‘μ‹œ λ„μž… μ΄ν›„μ—λŠ” 졜초 1번만 1μ΄ˆκ°€ 걸리고 μ΄ν›„μ—λŠ” 거의 μ¦‰μ‹œ λ°˜ν™˜ν•œλ‹€. 

 

ν”„λ‘μ‹œ νŒ¨ν„΄μ˜ 핡심은 RealSubject μ½”λ“œμ™€ ν΄λΌμ΄μ–ΈνŠΈ μ½”λ“œλ₯Ό μ „ν˜€ λ³€κ²½ν•˜μ§€ μ•Šκ³ , ν”„λ‘μ‹œλ₯Ό λ„μž…ν•΄μ„œ μ ‘κ·Ό μ œμ–΄λ₯Ό ν–ˆλ‹€λŠ” 점이닀. 그리고 ν΄λΌμ΄μ–ΈνŠΈ μ½”λ“œμ˜ λ³€κ²½ 없이 자유둭게 ν”„λ‘μ‹œλ₯Ό λ„£κ³  λΊ„ 수 μžˆλ‹€. μ‹€μ œ ν΄λΌμ΄μ–ΈνŠΈ μž…μž₯μ—μ„œλŠ” ν”„λ‘μ‹œ 객체가 μ£Όμž…λ˜μ—ˆλŠ”μ§€, μ‹€μ œ 객체가 μ£Όμž…λ˜μ—ˆλŠ”μ§€ μ•Œμ§€ λͺ»ν•œλ‹€. 

 

 

β€» λ°μ½”λ ˆμ΄ν„° νŒ¨ν„΄ : μΈν„°νŽ˜μ΄μŠ€ 기반

λͺ©ν‘œ : ν”„λ‘μ‹œλ₯Ό μ‚¬μš©ν•˜μ—¬ λΆ€κ°€ κΈ°λŠ₯ μΆ”κ°€ν•˜κΈ°

 

β–Ά λ°μ½”λ ˆμ΄ν„° νŒ¨ν„΄ ꡬ성

public interface Component {
    String operation();
}
public class RealComponent implements Component {
    @Override
    public String operation() {
        log.info("RealComponent μ‹€ν–‰");
        return "data";
    }
}
public class TimeDecorator implements Component {

    private Component component;

    public TimeDecorator(Component component) {
        this.component = component;
    }

    @Override
    public String operation() {
        log.info("TimeDecorator μ‹€ν–‰");
        long startTime = System.currentTimeMillis();

        String result = component.operation();

        long endTime = System.currentTimeMillis();
        long resultTime = endTime - startTime;
        log.info("TimeDecorator μ’…λ£Œ resultTime={}ms", resultTime);
        return result;
    }
}

 

TimeDecorator ν΄λž˜μŠ€λŠ” μ‹€ν–‰ μ‹œκ°„μ„ μΈ‘μ •ν•˜λŠ” λΆ€κ°€κΈ°λŠ₯을 μ œκ³΅ν•œλ‹€. λŒ€μƒμ„ ν˜ΈμΆœν•˜κΈ° 전에 μ‹œκ°„μ„ κ°€μ§€κ³  μžˆλ‹€κ°€, λŒ€μƒμ˜ 호좜이 λλ‚˜λ©΄ 호좜 μ‹œκ°„μ„ 둜그둜 남겨쀀닀. 

ν”„λ‘μ‹œκ°€ ν˜ΈμΆœν•΄μ•Ό ν•˜λŠ” λŒ€μƒμ„ component 에 μ €μž₯ν•œλ‹€. 

opearation() 을 ν˜ΈμΆœν•˜λ©΄ ν”„λ‘μ‹œμ™€ μ—°κ²°λœ λŒ€μƒμ„ ν˜ΈμΆœν•˜κ³ , TimeDecorator λŠ” μ‹€ν–‰μ‹œκ°„μ„ μΈ‘μ •ν•΄μ„œ 좜λ ₯ν•œλ‹€. 

 

 

β–Ά λ°μ½”λ ˆμ΄ν„° νŒ¨ν„΄ μ‚¬μš©

@Test
void decorator2() {
    Component realComponent = new RealComponent();
    Component timeDecorator = new TimeDecorator(realComponent);
    DecoratorPatternClient client = new DecoratorPatternClient(timeDecorator);
    client.execute();
}

 

객체 의쑴 κ΄€κ³„λŠ” client > timeDecorator  > realComponent 이닀.

 

Decorator λŠ” 슀슀둜 μ‘΄μž¬ν•  수 없기에(항상 꾸며쀄 λŒ€μƒμ΄ μžˆμ–΄μ•Ό 함), 내뢀에 호좜 λŒ€μƒμΈ component λ₯Ό κ°€μ§€κ³  μžˆμ–΄μ•Ό ν•œλ‹€. 

ν”„λ‘μ‹œ νŒ¨ν„΄κ³Ό λ°μ½”λ ˆμ΄ν„° νŒ¨ν„΄μ˜ λͺ¨μ–‘은 거의 κ°™λ‹€. κ·Έλž˜μ„œ μ–΄λ–€ 상황에 무엇을 μ‚¬μš©ν•΄μ•Ό ν• μ§€ 잘 λͺ¨λ₯΄κ² λ‹€. 

 

λ””μžμΈ νŒ¨ν„΄μ—μ„œ μ€‘μš”ν•œ 것은 ν•΄λ‹Ή νŒ¨ν„΄μ˜ 겉λͺ¨μ–‘이 μ•„λ‹ˆλΌ κ·Έ νŒ¨ν„΄μ„ λ§Œλ“  μ˜λ„κ°€ 더 μ€‘μš”ν•˜λ‹€. λ”°λΌμ„œ μ˜λ„μ— 따라 νŒ¨ν„΄μ„ κ΅¬λΆ„ν•˜λ‹€.

 

  • ν”„λ‘μ‹œ νŒ¨ν„΄μ˜ μ˜λ„ : λ‹€λ₯Έ 객체에 λŒ€ν•œ 접근을 μ œμ–΄ν•˜κΈ° μœ„ν•΄ λŒ€λ¦¬μžλ₯Ό μ œκ³΅ν•¨
  • λ°μ½”λ ˆμ΄ν„° νŒ¨ν„΄μ˜ μ˜λ„ : 객체에 μΆ”κ°€ μ±…μž„μ„ λ™μ μœΌλ‘œ μΆ”κ°€ν•˜κ³ , κΈ°λŠ₯ ν™•μž₯을 μœ„ν•œ μœ μ—°ν•œ λŒ€μ•ˆ 제곡

 

ν”„λ‘μ‹œλ₯Ό μ‚¬μš©ν•˜κΈ° μœ„ν•΄μ„œλŠ” μ‹€μ œ 객체λ₯Ό μŠ€ν”„λ§ 빈으둜 λ“±λ‘ν•˜μ§€ μ•Šκ³ , ν”„λ‘μ‹œλ₯Ό μ‹€μ œ μŠ€ν”„λ§ 빈 λŒ€μ‹  λ“±λ‘ν•΄μ•Όν•œλ‹€λŠ” 것을 λͺ…μ‹¬ν•˜μž. μ‹€μ œ 객체 λŒ€μ‹ μ— ν”„λ‘μ‹œ 객체λ₯Ό μŠ€ν”„λ§ 빈으둜 λ“±λ‘ν–ˆκΈ° λ•Œλ¬Έμ— μŠ€ν”„λ§ λΉˆμ„ μ£Όμž… λ°›μœΌλ©΄ 싀체 객체 λŒ€μ‹ μ— ν”„λ‘μ‹œ 객체가 μ£Όμž…λœλ‹€. 싀체 κ°μ²΄λŠ” ν”„λ‘μ‹œ 객체가 μ°Έμ‘°ν•˜κ³  있기 λ•Œλ¬Έμ— ν”„λ‘μ‹œλ₯Ό 톡해 μ‹€μ œ 객체λ₯Ό ν˜ΈμΆœν•  수 μžˆλ‹€. μ‹€μ œ κ°μ²΄λŠ” μžλ°” νž™ λ©”λͺ¨λ¦¬μ—λŠ” μ˜¬λΌκ°€μ§€λ§Œ μŠ€ν”„λ§ μ»¨ν…Œμ΄λ„ˆκ°€ κ΄€λ¦¬ν•˜μ§€λŠ” μ•Šκ²Œλœλ‹€. 

 

μ§€κΈˆκΉŒμ§€λŠ” μΈν„°νŽ˜μ΄μŠ€λ₯Ό 기반으둜 ν”„λ‘μ‹œλ₯Ό λ„μž…ν–ˆλ‹€. 

ν•˜μ§€λ§Œ μžλ°”μ˜ λ‹€ν˜•μ„±μ€ μΈν„°νŽ˜μ΄μŠ€λ₯Ό κ΅¬ν˜„ν•˜λ“ , μ•„λ‹ˆλ©΄ 클래슀λ₯Ό μƒμ†ν•˜λ“  μƒμœ„ νƒ€μž…λ§Œ 맞으면 λ‹€ν˜•μ„±μ΄ μ μš©λœλ‹€. 클래슀 기반으둜 상속을 λ°›μ•„μ„œ ν”„λ‘μ‹œλ₯Ό λ§Œλ“œλŠ” 것도 κ°€λŠ₯ν•˜λ‹€. 

 

 

β€» λ°μ½”λ ˆμ΄ν„° νŒ¨ν„΄ : 클래슀 기반 상속

β–Ά λ°μ½”λ ˆμ΄ν„° νŒ¨ν„΄ ꡬ성 

public class ConcreteLogic {

    public String operation() {
        log.info("ConcreteLogic μ‹€ν–‰");
        return "data";
    }
}
public class TimeProxy extends ConcreteLogic {

    private ConcreteLogic concreteLogic;

    public TimeProxy(ConcreteLogic concreteLogic) {
        this.concreteLogic = concreteLogic;
    }

    @Override
    public String operation() {
        log.info("TimeDecorator μ‹€ν–‰");
        long startTime = System.currentTimeMillis();

        String result = concreteLogic.operation();

        long endTime = System.currentTimeMillis();
        long resultTime = endTime - startTime;
        log.info("TimeDecorator μ’…λ£Œ resultTime={}ms", resultTime);
        return result;
    }

}

 

 

 

β–Ά λ°μ½”λ ˆμ΄ν„° νŒ¨ν„΄ μ‚¬μš©

public class ConcreteClient {

    private ConcreteLogic concreteLogic;

    public ConcreteClient(ConcreteLogic concreteLogic) {
        this.concreteLogic = concreteLogic;
    }

    public void execute() {
        concreteLogic.operation();
    }
}
@Test
void addProxy() {
    ConcreteLogic concreteLogic = new ConcreteLogic();
    TimeProxy timeProxy = new TimeProxy(concreteLogic);
    ConcreteClient client = new ConcreteClient(timeProxy);
    client.execute();
}

 

ConcreteClient μƒμ„±μžμ— concreteLogic 이 μ•„λ‹ˆλΌ timeProxy λ₯Ό μ£Όμž…ν•˜κ³  μžˆλ‹€. λ‹€ν˜•μ„±μ— μ˜ν•΄ ConcreteLoic 도 λ“€μ–΄κ°ˆ 수 있고, timeProxy 도 λ“€μ–΄κ°ˆ 수 μžˆλ‹€. 

 

μ΄λ ‡κ²Œ μΈν„°νŽ˜μ΄μŠ€κ°€ 없어도 클래슀 기반으둜 ν”„λ‘μ‹œλ₯Ό 생성할 수 μžˆμ§€λ§Œ, 클래슀 기반 ν”„λ‘μ‹œλŠ” 본인의 λΆ€λͺ¨ ν΄λž˜μŠ€μ—λ§Œ μ μš©ν•  수 μžˆλ‹€. μΈν„°νŽ˜μ΄μŠ€ 기반 ν”„λ‘μ‹œλŠ” μΈν„°νŽ˜μ΄μŠ€λ§Œ κ°™μœΌλ©΄ λͺ¨λ“  곳에 μ μš©ν•  수 μžˆλ‹€. λ”°λΌμ„œ μΈν„°νŽ˜μ΄μŠ€λ₯Ό μ‚¬μš©ν•˜λŠ” 것이 μ—­ν• κ³Ό κ΅¬ν˜„μ„ λͺ…ν™•ν•˜κ²Œ λ‚˜λˆ„κΈ° λ•Œλ¬Έμ— 더 μ’‹λ‹€. 

 

κ·ΈλŸ¬λ‚˜ 이 섀계에도 단점은 μžˆλ‹€. μΈν„°νŽ˜μ΄μŠ€κ°€ ν•„μš”ν•˜λ‹€λŠ” κ·Έ μžμ²΄μ΄λ‹€. λͺ¨λ“  객체에 μΈν„°νŽ˜μ΄μŠ€λ₯Ό λ„μž…ν•΄μ„œ μ—­ν• κ³Ό κ΅¬ν˜„μ„ λ‚˜λˆ„λŠ” 것이 μ’‹μ§€λ§Œ, μ‹€μ œλ‘œλŠ” κ΅¬ν˜„μ„ 거의 λ³€κ²½ν•  일이 μ—†λŠ” ν΄λž˜μŠ€λ„ λ§Žλ‹€. μ΄λ•Œ λ¬΄μž‘μ • μΈν„°νŽ˜μ΄μŠ€λ₯Ό μ‚¬μš©ν•˜λŠ” 것은 번거둭고 κ·Έλ ‡κ²Œ μ‹€μš©μ μ΄μ§€ μ•Šλ‹€.

μ΄λŸ΄λ•ŒλŠ” μΈν„°νŽ˜μ΄μŠ€λ₯Ό μ‚¬μš©ν•˜μ§€μ•Šκ³  ꡬ체클래슀λ₯Ό μ‚¬μš©ν•˜λŠ” 것이 μ’‹λ‹€ (μΈν„°νŽ˜μ΄μŠ€κ°€ 항상 ν•„μš”ν•˜μ§€λŠ” μ•Šλ‹€)

 

μ§€κΈˆκΉŒμ§€ ν”„λ‘μ‹œλ₯Ό μ‚¬μš©ν•΄μ„œ κΈ°μ‘΄ μ½”λ“œλ₯Ό λ³€κ²½ν•˜μ§€ μ•Šκ³  λΆ€κ°€κΈ°λŠ₯을 μ μš©ν•˜λŠ” 방법을 μ•Œμ•„λ΄€λ‹€.

λ¬Έμ œλŠ”, 클래슀 기반 ν”„λ‘μ‹œμ˜ 경우 λΆ€κ°€κΈ°λŠ₯을 μ μš©ν•΄μ•Ό ν•˜λŠ” λŒ€μƒ ν΄λž˜μŠ€κ°€ 100개라면 ν”„λ‘μ‹œ ν΄λž˜μŠ€λ„ 100개λ₯Ό λ§Œλ“€μ–΄μ•Ό ν•œλ‹€. (클래슀 기반 ν”„λ‘μ‹œλŠ” 본인의 λΆ€λͺ¨ ν΄λž˜μŠ€μ—λ§Œ μ μš©ν•  수 있기 λ•Œλ¬Έμ΄λ‹€)

 

ν”„λ‘μ‹œ 클래슀λ₯Ό ν•˜λ‚˜λ§Œ λ§Œλ“€μ–΄μ„œ λͺ¨λ“  곳에 μ μš©ν•  μˆ˜λŠ” μ—†μ„κΉŒ ? > 동적 ν”„λ‘μ‹œ κΈ°μˆ μ„ μ•Œμ•„λ³΄μž.