์‹ฑ๊ธ€ํ†ค ์ปจํ…Œ์ด๋„ˆ

2022. 2. 6. 12:40ใ†Backend/๐ŸŒฟ Spring

๊น€์˜ํ•œ ๊ฐ•์‚ฌ๋‹˜์˜ ์Šคํ”„๋ง ํ•ต์‹ฌ ์›๋ฆฌ - ๊ธฐ๋ณธํŽธ ์ˆ˜๊ฐ• ํ›„ ์ •๋ฆฌํ•œ ํฌ์ŠคํŒ…์ž…๋‹ˆ๋‹ค.

 

 

์Šคํ”„๋ง ํ•ต์‹ฌ ์›๋ฆฌ - ๊ธฐ๋ณธํŽธ - ์ธํ”„๋Ÿฐ | ๊ฐ•์˜

์Šคํ”„๋ง ์ž…๋ฌธ์ž๊ฐ€ ์˜ˆ์ œ๋ฅผ ๋งŒ๋“ค์–ด๊ฐ€๋ฉด์„œ ์Šคํ”„๋ง์˜ ํ•ต์‹ฌ ์›๋ฆฌ๋ฅผ ์ดํ•ดํ•˜๊ณ , ์Šคํ”„๋ง ๊ธฐ๋ณธ๊ธฐ๋ฅผ ํ™•์‹คํžˆ ๋‹ค์งˆ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค., - ๊ฐ•์˜ ์†Œ๊ฐœ | ์ธํ”„๋Ÿฐ...

www.inflearn.com

 

 

1. ์›น ์–ดํ”Œ๋ฆฌ์ผ€์ด์…˜๊ณผ ์‹ฑ๊ธ€ํ†ค

๋ฌธ์ œ์ : ๊ณ ๊ฐ ์š”์ฒญ์ด ์˜ฌ ๋•Œ๋งˆ๋‹ค ๊ฐœ๋ณ„ ๊ฐ์ฒด๋ฅผ ๋งŒ๋“ค์–ด๋‚ธ๋‹ค. 1000๋ฒˆ์˜ ์š”์ฒญ์ด ์˜ค๋ฉด, 1000๊ฐœ์˜ ์ƒˆ๋กœ์šด ๊ฐ์ฒด๋ฅผ ์ƒ์„ฑํ•˜๋Š”๋ฐ ์ด๋Š” ๋ฉ”๋ชจ๋ฆฌ ๋‚ญ๋น„๋ฅผ ์ดˆ๋ž˜ํ•œ๋‹ค.

ํ•ด๊ฒฐ์ฑ…: ํ•ด๋‹น ๊ฐ์ฒด๊ฐ€ ๋”ฑ 1๊ฐœ๋งŒ ์ƒ์„ฑ๋˜๊ณ , ๊ณต์œ ํ•˜๋„๋ก ์„ค๊ณ„ํ•˜๋ฉด ๋œ๋‹ค. => ์‹ฑ๊ธ€ํ†ค ํŒจํ„ด

 

์‹ฑ๊ธ€ํ†ค ํŒจํ„ด

JVM ๋‚ด์—์„œ, ํด๋ž˜์Šค์˜ ์ธ์Šคํ„ด์Šค๊ฐ€ ๋”ฑ 1๊ฐœ๋งŒ ์ƒ์„ฑ๋˜๋Š” ๊ฒƒ์„ ๋ณด์žฅํ•˜๋Š” ๋””์ž์ธ ํŒจํ„ด. 

 

 

public class SingletonService {
 
    // ์ƒ์„ฑ์ž๋ฅผ private ์œผ๋กœ ์ œํ•œํ–ˆ๊ธฐ์—, ์™ธ๋ถ€ ํด๋ž˜์Šค์—์„œ new ๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์ƒ์„ฑ์ž๋ฅผ ๋งŒ๋“ค ์ˆ˜ ์—†๋‹ค.
    private SingletonService() {
    }
 
    private static final SingletonService instance = new SingletonService(); 
 
    // instance ๋ณ€์ˆ˜์— ์ ‘๊ทผํ•  ์ˆ˜ ์žˆ๋Š” ๋ฐฉ๋ฒ•์€, getInstance() ๋ฉ”์„œ๋“œ๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๋ฐฉ๋ฒ•์ด ์œ ์ผํ•˜๋‹ค. ์ƒ์„ฑ์ž ๋งŒ๋“ค์ง€ ๋ชปํ•˜๊ธฐ์—.
    public static SingletonService getInstance() {
        return instance;
    }
 
    public void logic() {
        System.out.println("์‹ฑ๊ธ€ํ†ค ๊ฐ์ฒด ๋กœ์ง ํ˜ธ์ถœ");
    }
}

 

 

private ์œผ๋กœ ์ƒ์„ฑ์ž๋ฅผ ์ œํ•œํ–ˆ๊ธฐ์—, ํ˜น์‹œ๋ผ๋„ ์™ธ๋ถ€์—์„œ new ํ‚ค์›Œ๋“œ๋กœ ๊ฐ์ฒด ์ธ์Šคํ„ด์Šค๊ฐ€ ์ƒ์„ฑ๋˜๋Š” ๊ฒƒ์„ ๋ง‰๋Š”๋‹ค. => ์ด๊ฑธ ์ปดํŒŒ์ผ ๋‹จ๊ณ„์—์„œ ์žก์•„ ์ค„ ์ˆ˜ ์žˆ๋‹ค. ( ์ปดํŒŒ์ผ ๋‹จ๊ณ„์—์„œ ์—๋Ÿฌ๋ฅผ ์žก์„ ์ˆ˜ ์žˆ๋‹ค๋Š” ๊ฒƒ์€, ์ž˜ ์งœ์—ฌ์ง„ ๊ฐ์ฒด์ง€ํ–ฅ ์‹œ์Šคํ…œ์ด๋ž€ ๋œป )

 

 

// SingletonTest.java

public class SingletonTest {
    @Test
    @DisplayName("์‹ฑ๊ธ€ํ†ค ํŒจํ„ด์„ ์ ์šฉํ•œ ๊ฐ์ฒด ์‚ฌ์šฉ") public void singletonServiceTest() {
        SingletonService singletonService1 = SingletonService.getInstance();
        SingletonService singletonService2 = SingletonService.getInstance();
        System.out.println("singletonService1 = " + singletonService1);
        System.out.println("singletonService2 = " + singletonService2);
 
        assertThat(singletonService1).isSameAs(singletonService2);
    }
}

 

์‹ฑ๊ธ€ํ†ค ํŒจํ„ด ๋ฌธ์ œ์ 

1. ์‹ฑ๊ธ€ํ†ค ํŒจํ„ด์„ ๊ตฌํ˜„ํ•˜๋Š”๋ฐ ๋งŽ์€ ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•ด์•ผํ•œ๋‹ค.
2. ์˜์กด๊ด€๊ณ„์ƒ ํด๋ผ์ด์–ธํŠธ๊ฐ€ ๊ตฌ์ฒด ํด๋ž˜์Šค์— ์˜์กดํ•œ๋‹ค. ( = DIP๋ฅผ ์œ„๋ฐ˜ํ•œ๋‹ค. ์—ญํ• ์—๋งŒ ์˜์กดํ•ด์•ผํ•˜๋Š”๋ฐ ๊ตฌ์ฒด ํด๋ž˜์Šค์— ์˜์กดํ–ˆ์œผ๋‹ˆ DIP ์œ„๋ฐ˜)

ํด๋ผ์ด์–ธํŠธ๊ฐ€ ๊ตฌ์ฒด ํด๋ž˜์Šค์— ์˜์กดํ•ด์„œ OCP ์›์น™์„ ์œ„๋ฐ˜ํ•  ๊ฐ€๋Šฅ์„ฑ์ด ๋†’๋‹ค. ํ…Œ์ŠคํŠธํ•˜๊ธฐ ์–ด๋ ต๋‹ค.
3. ๋‚ด๋ถ€ ์†์„ฑ์„ ๋ณ€๊ฒฝํ•˜๊ฑฐ๋‚˜ ์ดˆ๊ธฐํ™” ํ•˜๊ธฐ ์–ด๋ ต๋‹ค.
4. private 
์ƒ์„ฑ์ž๋กœ ์ž์‹ ํด๋ž˜์Šค๋ฅผ ๋งŒ๋“ค๊ธฐ ์–ด๋ ต๋‹ค.

 

๊ฒฐ๋ก ์ ์œผ๋กœ ์œ ์—ฐ์„ฑ์ด ๋–จ์–ด์ง„๋‹ค.
์•ˆํ‹ฐํŒจํ„ด์œผ๋กœ ๋ถˆ๋ฆฌ๊ธฐ๋„ ํ•œ๋‹ค.

 

ํ•ด๊ฒฐ : ์Šคํ”„๋ง ์ปจํ…Œ์ด๋„ˆ๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ์Šคํ”„๋ง ๋นˆ ์ž์ฒด๊ฐ€ ์‹ฑ๊ธ€ํ†ค์œผ๋กœ ๊ด€๋ฆฌ๋œ๋‹ค.

 

 

2. ์‹ฑ๊ธ€ํ†ค ์ปจํ…Œ์ด๋„ˆ

 

์Šคํ”„๋ง ์ปจํ…Œ์ด๋„ˆ์—์„œ ์‹ฑ๊ธ€ํ†ค ํŒจํ„ด

์Šคํ”„๋ง ์ปจํ…Œ์ด๋„ˆ์— ๋นˆ ๊ฐ์ฒด๋ฅผ ๋“ฑ๋กํ•˜๋ฉด, ์œ„ ์ฝ”๋“œ์ฒ˜๋Ÿผ ๋ณต์žกํ•˜๊ฒŒ ์‹ฑ๊ธ€ํ†ค ํŒจํ„ด์„ ์ ์šฉํ•˜์ง€ ์•Š์•„๋„, ๊ฐ์ฒด ์ธ์Šคํ„ด์Šค๋ฅผ ์‹ฑ๊ธ€ํ†ค์œผ๋กœ ๊ด€๋ฆฌํ•œ๋‹ค.

๋•๋ถ„์— ์‹ฑ๊ธ€ํ†ค์˜ ๋ชจ๋“  ๋‹จ์ ์„ ํ•ด๊ฒฐํ•˜๋ฉด์„œ ๊ฐ์ฒด๋ฅผ ์‹ฑ๊ธ€ํ†ค์œผ๋กœ ์œ ์ง€ํ•  ์ˆ˜ ์žˆ๋‹ค.

 

// AppConfig.java
@Configuration
public class AppConfig {

    @Bean
    public MemberService memberService() {
        return new MemberServiceImpl(memberRepository());
    }
    
    ...
}


// SingleTonTest.java
public class SingletonTest {
 
    @Test
    @DisplayName("์Šคํ”„๋ง ์ปจํ…Œ์ด๋„ˆ์™€ ์‹ฑ๊ธ€ํ†ค")
    void springContainer() {
        ApplicationContext ac = new AnnotationConfigApplicationContext(AppConfig.class);
        MemberService memberService1 = ac.getBean("memberService", MemberService.class);
        MemberService memberService2 = ac.getBean("memberService", MemberService.class);
 
        assertThat(memberService1).isSameAs(memberService2);
    }
 
}

AnnotationConfigApplicationContext ๋ฅผ ํ†ตํ•ด ์Šคํ”„๋ง ์ปจํ…Œ์ด๋„ˆ ๋ฐฉ์‹์œผ๋กœ memberService ๋ผ๋Š” ์ด๋ฆ„์˜ ๋นˆ์„ 2๋ฒˆ ์ƒ์„ฑํ–ˆ๊ณ , assertThat() ์˜ ๊ฒฐ๊ณผ ๋‘ ๊ฐ์ฒด๋Š” (memberService1, memberService2) ๊ฒฐ๊ตญ ๊ฐ™๋‹ค๋Š” ๊ฒƒ์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ๋‹ค.

 

์ด์ „์— ์‹ฑ๊ธ€ํ†ค ํŒจํ„ด์„ ์ ์šฉํ•˜๊ธฐ ์œ„ํ•ด์„œ๋Š” private ์ ‘๊ทผ ์ œ์–ด์ž๋ฅผ ์„ ์–ธํ•˜๋Š” ๋“ฑ ๋ณ„๋„์˜ ์ž‘์—…์ด ๋งŽ์ด ํ•„์š”ํ–ˆ์ง€๋งŒ, ์Šคํ”„๋ง ์ปจํ…Œ์ด๋„ˆ๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ๋ณ„๋„ ์ž‘์—…์—†์ด ์‹ฑ๊ธ€ํ†ค์„ ์œ ์ง€ํ•  ์ˆ˜ ์žˆ๋‹ค.

 

 

์‹ฑ๊ธ€ํ†ค ํŒจํ„ด์˜ ์ฃผ์˜์ 

์‹ฑ๊ธ€ํ†ค ๋ฐฉ์‹์€ ์—ฌ๋Ÿฌ ํด๋ผ์ด์–ธํŠธ๊ฐ€ ํ•˜๋‚˜์˜ ๊ฐ™์€ ๊ฐ์ฒด ์ธ์Šคํ„ด์Šค๋ฅผ ๊ณต์œ ํ•˜๊ธฐ ๋•Œ๋ฌธ์—, ์ƒํƒœ๋ฅผ ์œ ์ง€(stateful) ํ•˜๋„๋ก ์„ค๊ณ„ํ•˜๋ฉด ์•ˆ๋œ๋‹ค. ์ฆ‰ ์‹ฑ๊ธ€ํ†ค ๊ฐ์ฒด๋Š” stateless ํ•˜๊ฒŒ ๊ด€๋ฆฌํ•ด์•ผํ•œ๋‹ค.

 

  1. ๋ฌด์ƒํƒœ๋กœ ์„ค๊ณ„ํ•  ๊ฒƒ
  2. ํŠน์ • ํด๋ผ์ด์–ธํŠธ๊ฐ€ ๊ฐ’์„ ๋ณ€๊ฒฝํ•  ์ˆ˜ ์žˆ๋Š” ํ•„๋“œ๊ฐ€ ์žˆ์œผ๋ฉด ์•ˆ๋œ๋‹ค.
  3. ํ•„๋“œ ๋Œ€์‹  ์ž๋ฐ”์—์„œ ๊ณต์œ ๋˜์ง€ ์•Š๋Š” ์ง€์—ญ๋ณ€์ˆ˜, ํŒŒ๋ผ๋ฏธํ„ฐ, ThreadLocal ๋“ฑ์„ ์‚ฌ์šฉํ•ด์•ผํ•œ๋‹ค.
  4. ์Šคํ”„๋ง ๋นˆ์˜ํ•„๋“œ์— ๊ณต์œ  ๊ฐ’์„ ์„ค์ •ํ•˜๋ฉด ์ •๋ง ํฐ ์žฅ์• ๊ฐ€ ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ๋‹ค.

 

์˜ˆ์‹œ๋ฅผ ํ†ตํ•ด ํ™•์ธํ•ด๋ณด์ž. 

 

์•„๋ž˜ StatefulService ์˜ order() ๋ฉ”์„œ๋“œ ๋‚ด๋ถ€์—์„œ price ๊ฐ’์„ ๋ณ€๊ฒฝํ•  ์ˆ˜ ์žˆ๋Š” ์ง€์ ์ด ์žˆ๋‹ค.

order() ๋ฉ”์„œ๋“œ์˜ ์ฑ…์ž„์€ ๊ทธ์ € ์–ด๋–ค name ์˜ ์ƒํ’ˆ์„ ์–ผ๋งˆ์˜ price ๋กœ ์ฃผ๋ฌธํ–ˆ์Œ์„ ์ถœ๋ ฅํ•˜๋Š” ๋ฐ์„œ ๋๋‚˜์•ผํ•œ๋‹ค.

ํ•˜์ง€๋งŒ this.price = price ์ฝ”๋“œ์—์„œ ํ•ด๋‹น ํ•„๋“œ ๊ฐ’์„ ๋ณ€๊ฒฝํ•ด๋ฒ„๋ฆฌ๊ธฐ ๋•Œ๋ฌธ์— ๋ฌธ์ œ ์œ ๋ฐœ์˜ ๊ฐ€๋Šฅ์„ฑ์„ ์ง€๋‹ˆ๊ฒŒ ๋๋‹ค.

// StatefulService

public class StatefulService {
    private int price;
 
    public void order(String name, int price) {
        System.out.println("name = " + name + " price = " + price);
        this.price = price; // ์—ฌ๊ธฐ๊ฐ€ ๋ฌธ์ œ ์ง€์ 
    }
 
    public int getPrice() {
        return price;
    }
}

 

๊ทธ๋ ‡๊ธฐ ๋•Œ๋ฌธ์— ํ…Œ์ŠคํŠธ์ฝ”๋“œ์—์„œ ์‹ฑ๊ธ€ํ†ค ๊ฐ์ฒด๊ฐ€ ์„œ๋กœ ๋‹ค๋ฅธ price ๊ฐ’์„ ๋งค๊ฐœ๋ณ€์ˆ˜๋กœ ๋ฐ›๋”๋ผ๋„, ๋‘ ๊ฐ์ฒด๊ฐ€ ๋ฆฌํ„ดํ•˜๋Š” getPrice() ์˜ ๊ฐ’์€ ๊ฐ™๊ฒŒ ๋œ๋‹ค.

// StatefulServiceTest

public class StatefulServiceTest {
    @Test
    void statefulServiceSingleton() {
 
        ApplicationContext ac = new AnnotationConfigApplicationContext(TestConfig.class);
        StatefulService statefulService1 = ac.getBean(StatefulService.class);
        StatefulService statefulService2 = ac.getBean(StatefulService.class);
 
 
        // Thread A : A ์‚ฌ์šฉ์ž 10000์› ์ฃผ๋ฌธ
        statefulService1.order("userA", 10000);
 
        // Thread B : B ์‚ฌ์šฉ์ž 20000์› ์ฃผ๋ฌธ
        statefulService2.order("userB", 20000);
 
        int price = statefulService1.getPrice();
        System.out.println("price = " + price); // 10000 ์„ ๊ธฐ๋Œ€ํ–ˆ์ง€๋งŒ, 20000 ์ด ๋‚˜์˜จ๋‹ค.
        assertThat(price).isNotEqualTo(10000);
        assertThat(statefulService1).isSameAs(statefulService2);
    }
}

 

๋งˆ์ง€๋ง‰ ์ค„์˜  assertThat(statefulService1).isSameAs(statefulService2) ์ฝ”๋“œ๊ฐ€ ํ†ต๊ณผ๋˜๋ฉด์„œ statefulService1 ๊ณผ statefulService2 ๋Š” ๊ฐ™์€ ๊ฐ์ฒด์ž„์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ๋‹ค.

 

๋”ฐ๋ผ์„œ StatefulService ์ฝ”๋“œ๋Š” ์•„๋ž˜์ฒ˜๋Ÿผ stateless ํ•˜๊ฒŒ ์„ค๊ณ„ํ•˜๋Š” ๊ฒƒ์ด ์˜ณ๋‹ค.

public class StatefulService {
    private int price;
 
    public int order(String name, int price) {
        System.out.println("name = " + name + " price = " + price);
        // wrong
        // this.price = price;
        
        // right
        return price;  // ๊ฐ’ ํ• ๋‹น์ด ์•„๋‹Œ ๊ณง๋ฐ”๋กœ ๋ฆฌํ„ดํ•˜๋„๋ก ์ˆ˜์ •
    }
 
    public int getPrice() {
        return price;
    }
}

 

๊ฒฐ๋ก  

๊ณต์œ  ํ•„๋“œ๋Š” ํ•ญ์ƒ ์กฐ์‹ฌํ•ด์•ผํ•˜๋ฉฐ, ์Šคํ”„๋ง ๋นˆ์€ stateless ํ•˜๊ฒŒ ์„ค๊ณ„ํ•ด์•ผํ•œ๋‹ค.

 

 

3. @Configuration ๊ณผ ์‹ฑ๊ธ€ํ†ค

 

@Configuration
public class AppConfig {
 
    @Bean
    public MemberService memberService() {
        return new MemberServiceImpl(memberRepository());
    }
 
    @Bean
    public OrderService orderService() {
        return new OrderServiceImpl(memberRepository(), discountPolicy());
    }
 
    @Bean
    public MemberRepository memberRepository() {
        return new MemoryMemberRepository();
    }
    
	...
    
}

 

์œ„ ์ฝ”๋“œ์—์„œ

memberService ๋นˆ์„ ๋งŒ๋“œ๋Š” ์ฝ”๋“œ๋Š” memberRepository() ๋ฅผ ํ˜ธ์ถœํ•œ๋‹ค. 

โžก๏ธ ์ด ๋ฉ”์„œ๋“œ๋ฅผ ํ˜ธ์ถœํ•˜๋ฉด new MemoryMemberRepository() ๋ฅผ ํ˜ธ์ถœํ•œ๋‹ค. 

 

orderService ๋นˆ์„ ๋งŒ๋“œ๋Š” ์ฝ”๋“œ๋„ ๋™์ผํ•˜๊ฒŒ memberRepository() ๋ฅผ ํ˜ธ์ถœํ•œ๋‹ค. 

โžก๏ธ ์ด ๋ฉ”์„œ๋“œ๋ฅผ ํ˜ธ์ถœํ•˜๋ฉด new MemoryMemberRepository() ๋ฅผ ํ˜ธ์ถœํ•œ๋‹ค.

 

๊ฒฐ๊ณผ์ ์œผ๋กœ ๊ฐ๊ฐ ๋‹ค๋ฅธ MemoryMemberRepository ๊ฐ€ ๋‘๋ฒˆ ์ƒ์„ฑ๋˜๋ฉด์„œ ์‹ฑ๊ธ€ํ†ค์ด ๊นจ์ง€๋Š” ๊ฒƒ ์ฒ˜๋Ÿผ ๋ณด์ธ๋‹ค.

ํ…Œ์ŠคํŠธ ์ฝ”๋“œ๋ฅผ ํ†ตํ•ด ๋‘ ๊ฐ์ฒด๊ฐ€ ์‹ค์ œ๋กœ ๋‹ค๋ฅธ์ง€ ํ™•์ธํ•ด๋ณด์ž. 

public class ConfigurationSingletonTest {
 
    @Test
    @DisplayName("์„œ๋กœ ๋‹ค๋ฅธ ๋นˆ ๊ฐ์ฒด์—์„œ ์ƒ์„ฑํ•œ ํ•˜๋‚˜์˜ ๊ฐ์ฒด๋Š” ๋™์ผํ•œ๊ฐ€?")
    void configurationTest() {
        ApplicationContext ac = new AnnotationConfigApplicationContext(AppConfig.class);
        MemberServiceImpl memberService = ac.getBean("memberService", MemberServiceImpl.class);
        OrderServiceImpl orderService = ac.getBean("orderService", OrderServiceImpl.class);
 
        MemberRepository memberRepository = ac.getBean("memberRepository", MemberRepository.class);
        // ์„œ๋กœ ๋‹ค๋ฅธ bean ๊ฐ์ฒด๋กœ๋ถ€ํ„ฐ MemberRepository ๋ฅผ ํ˜ธ์ถœ
        MemberRepository memberRepository1 = memberService.getMemberRepository();
        MemberRepository memberRepository2 = orderService.getMemberRepository();
 
        System.out.println(memberRepository1 == memberRepository2 && memberRepository == memberRepository2);
        // ๊ฒฐ๊ตญ ๋ชจ๋‘ ๊ฐ™์€ ์ฃผ์†Œ๊ฐ’์„ ๊ณต์œ ํ•˜๊ธฐ์—, ๋™์ผํ•œ ๊ฐ์ฒด๋กœ ๋ณผ ์ˆ˜ ์žˆ๋‹ค.
 
        Assertions.assertThat(memberService.getMemberRepository()).isSameAs(memberRepository); // ๊ฐ™๋‹ค
        Assertions.assertThat(orderService.getMemberRepository()).isSameAs(memberRepository);  // ๊ฐ™๋‹ค
    }
}


@Configuration
public class AppConfig {
 
    @Bean
    public MemberService memberService() {
        System.out.println("call AppConfig.memberService");
        return new MemberServiceImpl(memberRepository());
    }
 
    @Bean
    public OrderService orderService() {
        System.out.println("call AppConfig.orderService");
        return new OrderServiceImpl(memberRepository(), discountPolicy());
    }
 
    @Bean
    public MemberRepository memberRepository() {
        System.out.println("call AppConfig.memberRepository");
        return new MemoryMemberRepository();
 
    }
 
    @Bean
    public DiscountPolicy discountPolicy() {
        return new RateDiscountPolicy();
    }
}

์ถœ๋ ฅ๋ฌธ์„ ์‚ฝ์ž…ํ•˜์—ฌ, ๊ฐ๊ฐ ๊ฐ์ฒด๊ฐ€ ๋ช‡๋ฒˆ ์‹คํ–‰๋˜๋Š”์ง€ ํ™•์ธํ•ด๋ณด์ž. 

 

๊ฐ€์ •

>>> memberService ์Šคํ”„๋ง ๋นˆ ์ƒ์„ฑํ•  ๋•Œ

"call AppConfig.memberService"
"call AppConfig.memberRepository"

 

>>> orderService ์Šคํ”„๋ง ๋นˆ ์ƒ์„ฑํ•  ๋•Œ
"call AppConfig.orderService"
"call AppConfig.memberRepository"

 

>>> memberRepository ์Šคํ”„๋ง ๋นˆ ์ƒ์„ฑํ•  ๋•Œ

"call AppConfig.memberRepository"

------------------------------------------

๋™์ผํ•œ memberRepository ๊ฐ€ ์„ธ๋ฒˆ ์ถœ๋ ฅ๋  ๊ฒƒ์œผ๋กœ ์˜ˆ์ƒ

 

 

์‹ค์ œ ๊ฒ€์ฆ

์˜ˆ์ƒ๊ณผ ๋‹ค๋ฅด๊ฒŒ memberRepository ๋Š” ํ•œ๋ฒˆ๋งŒ ์ถœ๋ ฅ๋œ ๊ฒƒ์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ๋‹ค.

 

๊ทธ๋ ‡๋‹ค๋ฉด ์™œ ์ค‘๋ณต๋˜๋Š” ์ธ์Šคํ„ด์Šค๋Š” ํ•œ๋ฒˆ๋งŒ ํ˜ธ์ถœ๋˜๋Š” ๊ฒƒ์ผ๊นŒ?

์™œ๋ƒํ•˜๋ฉด @Configuration ์–ด๋…ธํ…Œ์ด์…˜์ด ๋ถ™์œผ๋ฉด CGLIB ์ด๋ผ๋Š” ๋ฐ”์ดํŠธ์ฝ”๋“œ ์กฐ์ž‘ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๊ฐ€ ๋™์ž‘ํ•˜๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค.

์ฝ”๋“œ๋กœ ๋” ์ž์„ธํžˆ ์•Œ์•„๋ณด์ž. 

public class ConfigurationSingletonTest {
  
    @Test
    void configutationDeep() {
        ApplicationContext ac = new AnnotationConfigApplicationContext(AppConfig.class);
        
        AppConfig bean = ac.getBean(AppConfig.class);
        MemberServiceImpl memberService = ac.getBean("memberService", MemberServiceImpl.class);

        System.out.println(" bean = " + memberService.getClass());
        System.out.println(" bean = " + bean.getClass());
    }
}

๋‘๋ฒˆ์งธ ์ถœ๋ ฅ๋ฌธ์˜ ๊ฒฐ๊ณผ๋ฅผ ์˜ˆ์ƒํ•˜๋ฉด bean = class hello.core.AppConfig ์ด ๋‚˜์™€์•ผํ•œ๋‹ค.

์™œ๋ƒํ•˜๋ฉด ์ฒซ๋ฒˆ์งธ ์ถœ๋ ฅ๋ฌธ์˜ ๊ฒฐ๊ณผ๋Š” bean = class hello.core.member.MemberServiceImpl ์ด๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค.

 

ํ•˜์ง€๋งŒ ์‹ค์ œ ์ฝ˜์†”์— ์ฐํžŒ ๊ฒฐ๊ณผ๋ฅผ ํ™•์ธํ•˜๋ฉด,

 

AppConfig ๋’ค์— ๋ถ€๊ฐ€์ ์ธ ์ •๋ณด๊ฐ€ ๋ถ™์–ด ์ถœ๋ ฅ๋œ๋‹ค. ๊ทธ๋ฆฌ๊ณ  ํ•ด๋‹น ๋ถ€๊ฐ€ ์ •๋ณด๋ฅผ ์ž˜ ์ฝ์–ด๋ณด๋ฉด GCLIB ์ด๋ผ๋Š” ๋ฌธ์ž๋ฅผ ํ™•์ธํ•  ์ˆ˜ ์žˆ๋‹ค. 

๊ฒฐ๋ก ๋ถ€ํ„ฐ ์ด์•ผ๊ธฐํ•˜๋ฉด CGLIB (๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ)๊ฐ€ ๊ฐœ์ž…ํ•œ ๊ฒƒ์ด๊ณ , ์™œ ๊ฐœ์ž…ํ–ˆ๋ƒํ•˜๋ฉด AppConfig ํด๋ž˜์Šค๋Š” @Configuration ์–ด๋…ธํ…Œ์ด์…˜์ด ๋ถ™๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค.

์˜ˆ์ƒํ•œ ๊ฒฐ๊ณผ์™€ ๋‹ฌ๋ฆฌ ๋ถ€๊ฐ€ ์ •๋ณด๊ฐ€ ๋ถ™์—ˆ๋‹ค๋Š” ๊ฒƒ์€ ๊ฐœ๋ฐœ์ž๊ฐ€ ์ง์ ‘ ์ƒ์„ฑํ•œ ํด๋ž˜์Šค๊ฐ€ ์•„๋‹Œ, CGLIB ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๊ฐ€ AppConfig ํด๋ž˜์Šค๋ฅผ ์ƒ์†๋ฐ›์•„ ์ž„์˜์˜ ํด๋ž˜์Šค๋ฅผ ๋งŒ๋“ค์–ด ์Šคํ”„๋ง ๋นˆ์œผ๋กœ ๋ฐ˜ํ™˜ํ•œ ๊ฒƒ์ด๋‹ค.

 

 

๋ณธ๋ก ์œผ๋กœ ๋Œ์•„์™€ ์šฐ๋ฆฌ๋Š” memberRepository ๋ผ๋Š” ์ค‘๋ณต๋œ ์ธ์Šคํ„ด์Šค๊ฐ€ ์™œ ํ•œ๋ฒˆ๋งŒ ํ˜ธ์ถœ๋˜๋Š”์ง€ ์ถ”๋ก ํ•  ์ˆ˜ ์žˆ๊ฒŒ ๋๋Š”๋ฐ, ๋ฐ”๋กœ AppConfig@GCLIB ์ด๋ผ๋Š” CGLIB ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ํ†ตํ•ด ์ƒ์„ฑ๋œ ์ž„์˜์˜ ํด๋ž˜์Šค๊ฐ€ ๋ฐ”์ดํŠธ์ฝ”๋“œ๋ฅผ ์กฐ์ž‘ํ•˜์—ฌ MemberRepository ์˜ ์‹ฑ๊ธ€ํ†ค์„ ๋ณด์žฅํ•ด์ฃผ๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค. (์‹ค์ œ๋กœ CGLIB ์˜ ๋‚ด๋ถ€ ๊ธฐ์ˆ ์„ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ์€ ๋งค์šฐ ๋ณต์žกํ•˜๋‹ค๊ณ  ํ•œ๋‹ค.)

 

 

๊ฒฐ๋ก 

@Bean ์–ด๋…ธํ…Œ์ด์…˜๋งŒ ์‚ฌ์šฉํ•˜๋ฉด ์Šคํ”„๋ง ๋นˆ์œผ๋กœ ๋“ฑ๋ก๋˜์ง€๋งŒ, ์‹ฑ๊ธ€ํ†ค์„ ๋ณด์žฅ๋ฐ›์ง€ ๋ชปํ•œ๋‹ค. ์‹ฑ๊ธ€ํ†ค์ด ๋ณด์žฅ๋˜๋Š” ์ด์œ ๋Š” ์Šคํ”„๋ง ์ปจํ…Œ์ด๋„ˆ์— @Configuration ์–ด๋…ธํ…Œ์ด์…˜์ด ์žˆ๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค. (๊ทธ๋Ÿฌ๋ฏ€๋กœ ์Šคํ”„๋ง ์„ค์ • ์ •๋ณด ํด๋ž˜์Šค์—” ํ•ญ์ƒ @Configuration ์„ ๋ถ™์ผ ๊ฒƒ)