π μ΄ κΈμ μΈνλ°μμ μ 곡νλ "μ€νλ§ ν΅μ¬ μ리 - κ³ κΈνΈ" κ°μλ₯Ό μκ°νλ©΄μ μ 리ν λ΄μ©μ λ°νμΌλ‘ μμ±ν κΈμ λλ€.
[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κ°λ₯Ό λ§λ€μ΄μΌ νλ€. (ν΄λμ€ κΈ°λ° νλ‘μλ λ³ΈμΈμ λΆλͺ¨ ν΄λμ€μλ§ μ μ©ν μ μκΈ° λλ¬Έμ΄λ€)
νλ‘μ ν΄λμ€λ₯Ό νλλ§ λ§λ€μ΄μ λͺ¨λ κ³³μ μ μ©ν μλ μμκΉ ? > λμ  νλ‘μ κΈ°μ μ μμ보μ.