🚒 싱글톀 μ»¨ν…Œμ΄λ„ˆ?

μ΄μ„±ν˜Β·2021λ…„ 7μ›” 31일
2

Spring

λͺ©λ‘ 보기
3/5
post-thumbnail

이전 κΈ€ - μŠ€ν”„λ§ κΈ°μ΄ˆμ™€ 원리λ₯Ό μ•Œμ•„λ³΄μžπŸŒ³ μ—μ„œ μž‘μ„±λœ μ˜ˆμ œμ½”λ“œλ₯Ό 기반으둜 λ‚΄μš©μ„ μž‘μ„±λ˜μ—ˆμŠ΅λ‹ˆλ‹€.

πŸ”₯ 였늘의 μ£Όμ œλŠ”?

  • μŠ€ν”„λ§μ€ νƒœμƒμ΄ κΈ°μ—…μš© 온라인 μ„œλΉ„μŠ€ κΈ°μˆ μ„ μ§€μ›ν•˜κΈ° μœ„ν•΄ νƒ„μƒν–ˆλ‹€. λŒ€λΆ€λΆ„μ˜ μŠ€ν”„λ§ μ• ν”Œλ¦¬μΌ€μ΄μ…˜μ€ μ›Ή μ• ν”Œλ¦¬μΌ€μ΄μ…˜μ΄κ³ , μ›Ή μ• ν”Œλ¦¬μΌ€μ΄μ…˜μ€ 보톡 μ—¬λŸ¬ 고객이 λ™μ‹œμ— μš”μ²­μ„ ν•œλ‹€.
  • ν˜„μž¬ μ½”λ“œλŠ” μ΄ˆλ‹Ή 1,000번의 μš”μ²­μ΄ λ“€μ–΄μ˜€λ©΄ 1,000개의 객체λ₯Ό μƒμ„±ν•˜μ—¬ μ‚¬μš©ν•˜κ²Œ λœλ‹€. 즉, μš”μ²­μ΄ μžˆμ„λ•Œλ§ˆλ‹€ 같은 객체λ₯Ό μƒˆλ‘œ λ§Œλ“€κ³  μžˆλ‹€λŠ” λœ»μ΄λ‹€.
  • μš°λ¦¬λŠ” λ©”λͺ¨λ¦¬μ˜ 낭비없이 κ°œλ°œμ„ ν•˜κ³ μ‹Άλ‹€. 싱글톀을 μ΄μš©ν•˜μ—¬ 효율적인 μ½”λ“œλ₯Ό λ§Œλ“€μ–΄λ³΄μž.
  • λ‹€μŒ μ½”λ“œλ₯Ό ν†΅ν•˜μ—¬ ν™•μΈν•΄λ³΄μž.
@Configuration
public class AppConfig {

    @Bean
    public MemberService memberService() {
        return new MemberServiceImpl(new MemoryMemberRepository());
    }
}
public class SingletonTest {

    @Test
    @DisplayName("μŠ€ν”„λ§ μ—†λŠ” μˆœμˆ˜ν•œ DI μ»¨ν…Œμ΄λ„ˆ")
    void pureContainer() {
        AppConfig appConfig = new AppConfig();

        //μš”μ²­μ΄ λ“€μ–΄μ˜¬λ–„λ§ˆλ‹€ 객체λ₯Ό 생성
        MemberService memberService1 = appConfig.memberService();
        MemberService memberService2 = appConfig.memberService();

        //참쑰값이 λ‹€λ₯Έ 것을 확인
        System.out.println("memberService1 = " + memberService1);
        System.out.println("memberService2 = " + memberService2);

        assertThat(memberService1).isNotSameAs(memberService2);
    }
}

❗️ κ·Έλž˜μ„œ κ²°κ³Όκ°€ 뭐야?

  • memberService1, memberService2λŠ” 같은 κ΅¬ν˜„κ°μ²΄μ΄λ‹€. λ§Œμ•½ μš”μ²­μ΄ λ“€μ–΄μ˜¬λ•Œλ§ˆλ‹€ 객체λ₯Ό μƒμ„±ν•˜κ²Œ λœλ‹€λ©΄ 이처럼 같은 κ΅¬ν˜„κ°μ²΄λ“€μ΄ 계속 좔가될 것이닀.
  • ν…ŒμŠ€νŠΈ κ²°κ³Όλ₯Ό μ£Όλͺ©ν•΄λ³΄μž. @43e4ca52, @137782a7 은 μƒμ„±λœ 객체가 μ°Έμ‘°ν•˜κ³  μžˆλŠ” μ£Όμ†Œκ°’μ΄λ‹€. 값이 λ‹€λ₯΄λ‹€. 이 λœ»μ€ λ˜‘κ°™μ€ κ΅¬ν˜„κ°μ²΄μ΄μ§€λ§Œ μ„œλ‘œ λ‹€λ₯Έ 곳을 μ°Έμ‘°ν•˜κ³  μžˆλ‹€λŠ” 것이닀.
  • 즉, λΆˆν•„μš”ν•œ λ©”λͺ¨λ¦¬κ°€ λ‚­λΉ„λœλ‹€λŠ” λœ»μ΄λ‹€.
  • 이것을 ν•΄κ²°ν•˜κΈ° μœ„ν•˜μ—¬ 1개의 κ΅¬ν˜„κ°μ²΄λ§Œ μƒμ„±ν•˜κ³  κ³΅μœ ν•˜μ—¬ μ‚¬μš©ν•˜λ„λ‘ λ§Œλ“€ 것이닀. λ‹€λ₯Έ 말둜 μš°λ¦¬λŠ” 싱글톀 νŒ¨ν„΄μ— λŒ€ν•˜μ—¬ μ•Œμ•„λ³Ό 것이닀.

βœ… 싱글톀 νŒ¨ν„΄

  • μ°Έκ³  - 싱글톀 νŒ¨ν„΄(μœ„ν‚€λ°±κ³Ό)
  • 싱글톀 νŒ¨ν„΄(Singleton pattern)을 λ”°λ₯΄λŠ” ν΄λž˜μŠ€λŠ”, μƒμ„±μžκ°€ μ—¬λŸ¬ μ°¨λ‘€ ν˜ΈμΆœλ˜λ”λΌλ„ μ‹€μ œλ‘œ μƒμ„±λ˜λŠ” κ°μ²΄λŠ” ν•˜λ‚˜μ΄κ³  졜초 생성 이후에 호좜된 μƒμ„±μžλŠ” 졜초의 μƒμ„±μžκ°€ μƒμ„±ν•œ 객체λ₯Ό λ¦¬ν„΄ν•œλ‹€.
  • 싱글톀 νŒ¨ν„΄μ„ μƒμ„±ν•˜λŠ” μ½”λ“œλ₯Ό μ‚΄νŽ΄λ³΄μž.
public class SingletonService {

    //1. static μ˜μ—­μ— 객체λ₯Ό λ”± 1개만 생성해둔닀.
    private static final SingletonService instance = new SingletonService();

    //2. public 으둜 μ—΄μ–΄μ„œ 객체 μΈμŠ€ν„°μŠ€κ°€ ν•„μš”ν•˜λ©΄ 이 static λ©”μ„œλ“œλ₯Ό ν†΅ν•΄μ„œλ§Œ μ‘°νšŒν•˜λ„λ‘ ν—ˆμš©ν•œλ‹€.
    public static SingletonService getInstance() {
        return instance;
    }

    //3. μƒμ„±μžλ₯Ό private 으둜 μ„ μ–Έν•΄μ„œ μ™ΈλΆ€μ—μ„œ new ν‚€μ›Œλ“œλ₯Ό μ‚¬μš©ν•œ 객체 생성을 λͺ»ν•˜κ²Œ λ§‰λŠ”λ‹€.
    private SingletonService() {
    }
}
  • static μ˜μ—­μ— 객체λ₯Ό λ”± 1개만 생성해둔닀.
  • 싱글톀 νŒ¨ν„΄μ€ private μƒμ„±μžλ₯Ό μ‚¬μš©ν•΄μ„œ μ™ΈλΆ€μ—μ„œ μž„μ˜λ‘œ new ν‚€μ›Œλ“œλ₯Ό μ‚¬μš©ν•˜μ§€ λͺ»ν•˜λ„둝 막아야 ν•œλ‹€.(μ€‘μš”)
  • static μ˜μ—­μ— 객체(instance)λ₯Ό μƒμ„±ν•˜κ³ , λ§Œμ•½ ν•„μš”ν•˜λ‹€λ©΄ getInstance() λ©”μ†Œλ“œλ₯Ό ν†΅ν•΄μ„œλ§Œ 접근이 κ°€λŠ₯ν•˜λ‹€.
  • ν…ŒμŠ€νŠΈλ₯Ό ν•΄λ³΄μž.
public class SingletonTest {

    @Test
    @DisplayName("싱글톀 νŒ¨ν„΄μ„ μ μš©ν•œ 객체 μ‚¬μš©")
    void singletonServiceTest() {
        SingletonService instance1 = SingletonService.getInstance();
        SingletonService instance2 = SingletonService.getInstance();

        System.out.println("instance1 = " + instance1);
        System.out.println("instance2 = " + instance2);

        assertThat(instance1).isSameAs(instance2);
    }

}
  • μ‹€ν–‰κ²°κ³Ό

  • μ‹€ν–‰κ²°κ³Ό 같은 μ°Έμ‘°κ°’(@9f8ea4d)을 가지고 μžˆλŠ” 객체만 λ¦¬ν„΄λ˜μ—ˆλ‹€.

  • 싱글톀 νŒ¨ν„΄μ„ μ μš©ν•˜λ©΄ μš”μ²­μ΄ 올 λ•Œ λ§ˆλ‹€ 객체λ₯Ό μƒμ„±ν•˜λŠ” 것이 μ•„λ‹ˆλΌ, 이미 λ§Œλ“€μ–΄μ§„ 객체λ₯Ό κ³΅μœ ν•΄μ„œ 효율적으둜 μ‚¬μš©ν•  수 μžˆλ‹€. ν•˜μ§€λ§Œ 싱글톀 νŒ¨ν„΄μ€ λ‹€μŒκ³Ό 같은 수 λ§Žμ€ λ¬Έμ œμ λ“€μ„ 가지고 μžˆλ‹€.

문제점

  • 싱글톀 νŒ¨ν„΄μ„ κ΅¬ν˜„ν•˜λŠ” μ½”λ“œ μžμ²΄κ°€ 많이 λ“€μ–΄κ°„λ‹€.
  • μ˜μ‘΄κ΄€κ³„μƒ ν΄λΌμ΄μ–ΈνŠΈκ°€ ꡬ체 ν΄λž˜μŠ€μ— μ˜μ‘΄ν•œλ‹€.(DIPλ₯Ό μœ„λ°˜)
  • ν΄λΌμ΄μ–ΈνŠΈκ°€ ꡬ체 ν΄λž˜μŠ€μ— μ˜μ‘΄ν•΄μ„œ OCP 원칙을 μœ„λ°˜ν•  κ°€λŠ₯성이 λ†’λ‹€.
  • 싱글톀은 λ§Œλ“€μ–΄μ§€λŠ” 방식이 μ œν•œμ μ΄κΈ° λ•Œλ¬Έμ— ν…ŒμŠ€νŠΈμ—μ„œ μ‚¬μš©λ  λ•Œ mock 였브젝트 λ“±μœΌλ‘œ λŒ€μ²΄ν•˜κΈ°κ°€ νž˜λ“€λ‹€.
  • μ„œλ²„ ν™˜κ²½μ—μ„œλŠ” 싱글톀이 ν•˜λ‚˜λ§Œ λ§Œλ“€μ–΄μ§€λŠ” 것을 보μž₯ν•˜μ§€ λͺ»ν•œλ‹€. μ—¬λŸ¬κ°œμ˜ JVM에 λΆ„μ‚°λΌμ„œ μ„€μΉ˜κ°€ λ˜λŠ” κ²½μš°μ—λ„ 각각 λ…λ¦½μ μœΌλ‘œ μ˜€λΈŒμ νŠΈκ°€ 생기기 λ•Œλ¬Έμ— μ‹±κΈ€ν†€μœΌλ‘œμ„œμ˜ κ°€μΉ˜κ°€ 떨어진닀.
  • μ°Έκ³  - 싱글톀 νŒ¨ν„΄μ˜ 단점(https://ssoco.tistory.com/65)

ν•˜μ§€λ§Œ μš°λ¦¬λŠ” 싱글톀 λ¬Έμ œμ— λŒ€ν•œ 고민을 ν•  ν•„μš”κ°€ μ—†λ‹€. μš°λ¦¬κ°€ ν•™μŠ΅ν•œ μŠ€ν”„λ§ 빈이 λ°”λ‘œ μ‹±κΈ€ν†€μœΌλ‘œ κ΄€λ¦¬λ˜λŠ” λΉˆμ΄λ‹€.

🚒 싱글톀 μ»¨ν…Œμ΄λ„ˆ

  • μŠ€ν”„λ§ μ»¨ν…Œμ΄λ„ˆλŠ” μ‹±κΈ€ν„΄ νŒ¨ν„΄μ„ μ μš©ν•˜μ§€ μ•Šμ•„λ„, 객체 μΈμŠ€ν„΄μŠ€λ₯Ό μ‹±κΈ€ν†€μœΌλ‘œ κ΄€λ¦¬ν•œλ‹€.
  • μŠ€ν”„λ§ μ»¨ν…Œμ΄λ„ˆλŠ” 싱글톀 μ»¨ν…Œμ΄λ„ˆ 역할을 ν•œλ‹€. μ΄λ ‡κ²Œ 싱글톀 객체λ₯Ό μƒμ„±ν•˜κ³  κ΄€λ¦¬ν•˜λŠ” κΈ°λŠ₯을 싱글톀 λ ˆμ§€μŠ€νŠΈλ¦¬λΌ ν•œλ‹€.
  • μŠ€ν”„λ§ μ»¨ν…Œμ΄λ„ˆμ˜ 이런 κΈ°λŠ₯ 덕뢄에 μ‹±κΈ€ν„΄ νŒ¨ν„΄μ˜ λͺ¨λ“  단점을 ν•΄κ²°ν•˜λ©΄μ„œ 객체λ₯Ό μ‹±κΈ€ν†€μœΌλ‘œ μœ μ§€ν•  수 μžˆλ‹€.
    • 싱글톀 νŒ¨ν„΄μ„ μœ„ν•œ μ½”λ“œκ°€ 듀어가지 μ•Šμ•„λ„ 된
    • DIP, OCP, ν…ŒμŠ€νŠΈ, private μƒμ„±μžλ‘œ λΆ€ν„° 자유둭게 싱글톀을 μ‚¬μš©ν•  수 μžˆλ‹€.

μŠ€ν”„λ§ μ»¨ν…Œμ΄λ„ˆ 덕뢄에 μš”μ²­μ΄ 올 λ•Œ λ§ˆλ‹€ 객체λ₯Ό μƒμ„±ν•˜λŠ” 것이 μ•„λ‹ˆλΌ, 이미 λ§Œλ“€μ–΄μ§„ 객체λ₯Ό 곡유 ν•΄μ„œ 효율적으둜 μž¬μ‚¬μš©ν•  수 μžˆλ‹€.

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);

        //참쑰값이 같은 것을 확인
        System.out.println("memberService1 = " + memberService1);
        System.out.println("memberService2 = " + memberService2);

        assertThat(memberService1).isSameAs(memberService2);
    }

}
  • μ‹€ν–‰κ²°κ³Ό

  • 기쑴의 μŠ€ν”„λ§ μ»¨ν…Œμ΄λ„ˆμ—μ„œ μƒμ„±λœ λΉˆμ„ μ‘°νšŒν•΄λ³΄λ©΄, μ—¬λŸ¬ 객체λ₯Ό μƒμ„±ν•˜κ³  μ£Όμž…ν•΄λ„ 같은 객체가 μ£Όμž…λ˜λŠ” 것을 확인할 수 μžˆλ‹€.

  • 이번 ν…ŒμŠ€νŠΈ μ½”λ“œλ₯Ό μ΄μš©ν•˜μ—¬ μŠ€ν”„λ§ μ»¨ν…Œμ΄λ„ˆκ°€ 싱글톀 μ»¨ν…Œμ΄λ„ˆ 역할을 ν•˜λŠ”μ§€ 직접 확인할 수 μžˆμ—ˆλ‹€.

  • ν•˜μ§€λ§Œ 싱글톀 방식을 μ‚¬μš©ν•  λ•ŒλŠ” μ£Όμ˜ν•΄μ•Όν•  사항이 μ‘΄μž¬ν•œλ‹€.

❗️ 싱글톀 λ°©μ‹μ˜ 주의점

  • 객체 μΈμŠ€ν„΄μŠ€λ₯Ό ν•˜λ‚˜λ§Œ μƒμ„±ν•΄μ„œ κ³΅μœ ν•˜λŠ” 싱글톀 방식은 μ—¬λŸ¬ ν΄λΌμ΄μ–ΈνŠΈκ°€ ν•˜λ‚˜μ˜ 같은 객체 μΈμŠ€ν„΄μŠ€λ₯Ό κ³΅μœ ν•˜κΈ° λ•Œλ¬Έμ— 싱글톀 κ°μ²΄λŠ” μƒνƒœλ₯Ό μœ μ§€(stateful)ν•˜κ²Œ μ„€κ³„ν•˜λ©΄ μ•ˆλœλ‹€.
  • λ¬΄μƒνƒœ(stateless)둜 섀계해야 ν•œλ‹€. 즉, 값을 μœ μ§€ν•˜κ±°λ‚˜ λ³€κ²½ν•΄μ•Όν•˜λŠ” ν•„λ“œκ°€ 있으면 μ•ˆλœλ‹€.
  • μ‰¬μš΄ μ˜ˆμ‹œλ‘œ A 고객이 주문을 ν•˜κ³ , B 고객이 주문을 ν•˜κ²Œλ˜λ©΄ 주문내역이 λ’€λ°”λ€” 수 μžˆλ‹€.
  • A, B 고객이 같은 객체λ₯Ό κ³΅μœ ν•˜κΈ° λ•Œλ¬Έμ— μƒνƒœκ°’μ„ λ³€κ²½ν•˜λŠ” ν•„λ“œκ°€ μ‘΄μž¬ν•œλ‹€λ©΄ μƒνƒœκ°€ κ³΅μœ λ˜λŠ” λ¬Έμ œκ°€ λ°œμƒν•œλ‹€.
  • μ½”λ“œλ‘œ μ‚΄νŽ΄λ³΄μž.
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;
    }
}
  • μž„μ‹œλ‘œ μ£Όλ¬Έ(order) μ„œλΉ„μŠ€λ₯Ό λ§Œλ“€μ—ˆλ‹€.
  • μ΄κ³³μ—μ„œ price ν•„λ“œλŠ” μƒνƒœλ₯Ό μœ μ§€ν•˜λ„λ‘ μ„€κ³„λ˜μ–΄ μžˆλ‹€.
    • this.price = price;
  • λ”°λΌμ„œ 가격(price)은 곡유되고 μžˆλ‹€.
  • 이제 ν…ŒμŠ€νŠΈλ₯Ό ν•΄λ³΄μž.
public class StatefulServiceTest {

    @Test
    @DisplayName("μƒνƒœλ₯Ό μœ μ§€ν•˜λŠ” 싱글톀 λ°©μ‹μ˜ 문제점")
    void statefulServiceSingleton() {
        ApplicationContext ac = new AnnotationConfigApplicationContext(TestConfig.class);
        StatefulService statefulService1 = ac.getBean("statefulService", StatefulService.class);
        StatefulService statefulService2 = ac.getBean("statefulService", StatefulService.class);

        //ThreadA : A μ‚¬μš©μžκ°€ 10000원 μ£Όλ¬Έ
        statefulService1.order("userA", 10000);

        //ThreadB : B μ‚¬μš©μžκ°€ 20000원 μ£Όλ¬Έ
        statefulService2.order("userB", 20000);

        //ThreadA : A μ‚¬μš©μžκ°€ μ£Όλ¬Έ κΈˆμ•‘ 쑰회
        int price1 = statefulService1.getPrice();
        System.out.println("price1 = " + price1);

        assertThat(price1).isEqualTo(20000);
    }

    //ν…ŒμŠ€νŠΈλ₯Ό μœ„ν•œ μž„μ‹œ μ„€μ •
    static class TestConfig {

        @Bean
        public StatefulService statefulService() {
            return new StatefulService();
        }

    }

}
  • ν…ŒμŠ€νŠΈλ₯Ό μœ„ν•œ TestConfigλ₯Ό μƒμ„±ν•˜κ³  @Bean νƒœκ·Έλ‘œ ApplicationContext에 λ“±λ‘ν•˜μ˜€λ‹€.
  • 이제 μŠ€ν”„λ§ μ»¨ν…Œμ΄λ„ˆλŠ” StatefulServiceλ₯Ό 싱글톀 객체둜 κ΄€λ¦¬ν•œλ‹€.

  • μŠ€ν”„λ§ μ»¨ν…Œμ΄λ„ˆμ—μ„œ μ‘°νšŒν•œ StatefulServiceλŠ” 같은 객체이닀. λ”°λ‘œ μƒμ„±ν•˜μ˜€μ§€λ§Œ 같은 참쑰값을 ν†΅ν•˜μ—¬ price ν•„λ“œμ— μ ‘κ·Όν•œλ‹€.
  • A μ‚¬μš©μžκ°€ 10,000원 μ£Όλ¬Έν•˜κ³  B μ‚¬μš©μžκ°€ 20,000원 주문을 μ£Όλ¬Έν•˜μ˜€μ„ λ•Œ, A μ‚¬μš©μžμ˜ μ£Όλ¬Έ κΈˆμ•‘μ„ μ‘°νšŒν•˜λ©΄ 20,000원이 λ‚˜μ˜€λŠ” μ•„μ£Ό μœ„ν—˜ν•œ(?) κ²°κ³Όκ°€ λ‚˜μ˜¨λ‹€.

  • ν•„λ“œκ°€ 곡유되기 λ•Œλ¬Έμ— 가격이 바뀐 것이닀. λ”°λΌμ„œ μŠ€ν”„λ§ λΉˆμ€ 항상 λ¬΄μƒνƒœ(stateless)둜 섀계해야 ν•œλ‹€.

πŸ›  @Configurationκ³Ό 싱글톀

  • μš°λ¦¬λŠ” 계속 μŠ€ν”„λ§ μ»¨ν…Œμ΄λ„ˆκ°€ 싱글톀 νŒ¨ν„΄μ„ μœ μ§€ν•˜λ©° 객체λ₯Ό κ΄€λ¦¬ν•˜λŠ”μ§€ μ—¬λΆ€λ₯Ό μ•Œμ•„λ³΄κ³  μžˆλ‹€.
  • κ·Έλ ‡λ‹€λ©΄ λ‹€μŒκ³Ό 같은 μƒν™©μ—μ„œ κΆκΈˆν•¨μ΄ 생길 수 μžˆλ‹€.

  • 이 μ½”λ“œλŠ” μŠ€ν”„λ§ λΉˆμ„ λ“±λ‘ν•˜λŠ” @Configuration 이닀.
  • μ—¬κΈ°μ„œ memberRepository() λ©”μ†Œλ“œλŠ” 2번 호좜되고 μžˆλ‹€. memberService, orderService λΉˆμ„ 등둝할 λ•Œ 각각 호좜이 λ˜λ©΄μ„œ new MemoryMemberRepository() μ½”λ“œλ₯Ό ν†΅ν•΄μ„œ 같은 객체λ₯Ό 2번 λ§Œλ“€ 것이라고 μƒκ°ν•˜κ²Œ λœλ‹€. κ³Όμ—° μ‚¬μ‹€μΌκΉŒ?
  • μŠ€ν”„λ§ μ»¨ν…Œμ΄λ„ˆλŠ” λΉˆμ„ μ‹±κΈ€ν†€μœΌλ‘œ κ΄€λ¦¬ν•œλ‹€κ³  ν–ˆλŠ”λ°... λΉˆμ„ 등둝할 λ•Œ 같은 객체λ₯Ό 2번 ν˜ΈμΆœν•΄μ„œ λ§Œλ“€κ²Œ 되면 싱글톀이 μ•„λ‹Œκ±° μ•„λ‹ˆμ•Ό? 라고 생각해 λ³Ό 수 μžˆλ‹€.
  • 이 κΆκΈˆμ¦λ„ ν…ŒμŠ€νŠΈ μ½”λ“œλ₯Ό 톡해 μ•Œμ•„λ³Ό 수 μžˆλ‹€.
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 memberRepository1 = memberService.getMemberRepository();
        MemberRepository memberRepository2 = orderService.getMemberRepository();
        MemberRepository memberRepository = ac.getBean("memberRepository", MemberRepository.class);

        System.out.println("memberService -> memberRepository = " + memberRepository1);
        System.out.println("orderService -> memberRepository = " + memberRepository2);
        System.out.println("memberRepository -> memberRepository = " + memberRepository);

        assertThat(memberService.getMemberRepository()).isSameAs(memberRepository);
        assertThat(orderService.getMemberRepository()).isSameAs(memberRepository);
    }
}

κ²°κ³Όλ₯Ό μ˜ˆμƒν•΄λ³΄μž

  • μœ„μ— μ‚΄νŽ΄λ³Έ AppConfig μ—μ„œ μ»¨ν…Œμ΄λ„ˆμ— λΉˆμ„ 등둝할 λ•Œ ν˜ΈμΆœλ˜λŠ” new MemoryMemberRepository()λŠ” 총 3λ²ˆμ΄λ‹€.
    • memberService -> AppConfig.memberRepository
    • orderService -> AppConfig.memberRepository
    • memberRepository -> AppConfig.memberRepository
  • 3번 ν˜ΈμΆœλ˜λ©΄μ„œ MemoryMemberRepository 객체λ₯Ό 3개λ₯Ό λ§Œλ“€ 것이닀. κ·Έλ ‡λ‹€λ©΄ λͺ¨λ‘ λ‹€λ₯Έ 객체가 μƒμ„±λ˜μ–΄μ•Ό ν•  것이닀. μ˜ˆμƒμ΄ λ§žμ„κΉŒ?

μ‹€ν–‰κ²°κ³Ό

  • @7824e97e 참쑰값이 λͺ¨λ‘ λ˜‘κ°™λ‹€. μŠ€ν”„λ§ λΉˆμ„ λ“±λ‘ν•˜λ©΄μ„œ 3개의 참쑰값이 λ‹€λ₯Ό 것이라 μ˜ˆμƒν–ˆμ§€λ§Œ ν…ŒμŠ€νŠΈ κ²°κ³ΌλŠ” λͺ¨λ‘ 같은 객체, 즉 μ‹±κΈ€ν†€μœΌλ‘œ κ΄€λ¦¬λ˜κ³  μžˆμŒμ„ ν™•μΈν–ˆλ‹€.
  • 이런 κ²°κ³Όκ°€ κ°€λŠ₯ν•œ 것은 μŠ€ν”„λ§ μ»¨ν…Œμ΄λ„ˆμ—μ„œ 직접 λΉˆμ„ λ“±λ‘ν•˜κ³  κ΄€λ¦¬ν•˜κΈ° λ•Œλ¬Έμ— κ°€λŠ₯ν•œ 것이닀.
  • λ§ˆμ§€λ§‰μœΌλ‘œ @Configuration의 μ€‘μš”ν•œ 역할에 λŒ€ν•˜μ—¬ μ•Œμ•„λ³΄λ„λ‘ ν•˜κ² λ‹€.

🌴 @Configuration

  • 이전 ν…ŒμŠ€νŠΈμ—μ„œ μš°λ¦¬λŠ” 객체λ₯Ό μƒμ„±ν•˜λŠ” μžλ°”μ½”λ“œλ₯Ό 3번 ν˜ΈμΆœν–ˆλ‹€. ν•˜μ§€λ§Œ μŠ€ν”„λ§μ€ 3개의 λ‹€λ₯Έ 객체가 μ•„λ‹Œ λͺ¨λ‘ 같은 객체λ₯Ό λ˜λŒλ €μ£Όμ—ˆλ‹€.
  • 싱글톀을 보μž₯ν•˜λŠ” μŠ€ν”„λ§ μ»¨ν…Œμ΄λ„ˆκ°€ λΉˆμ„ 등둝할 λ•Œ λ°”μ΄νŠΈμ½”λ“œλ₯Ό μ‘°μž‘ν•˜μ—¬ μž„μ˜μ˜ 클래슀λ₯Ό μƒμ„±ν•˜κ³  λ“±λ‘ν•˜κΈ° λ•Œλ¬Έμ΄λ‹€.
  • μ΄ν•΄ν•˜κΈ° μ–΄λ €μ› μ§€λ§Œ λ‚΄μš©μ„ 정리해보도둝 ν•˜κ² λ‹€.
public class ConfigurationSingletonTest {

    @Test
    @DisplayName("μŠ€ν”„λ§ 빈 클래슀 확인")
    void configurationDeepTest() {

      //μŠ€ν”„λ§ μ»¨ν…Œμ΄λ„ˆ 쑰회
      ApplicationContext ac = new AnnotationConfigApplicationContext(AppConfig.class);
      AppConfig bean = ac.getBean(AppConfig.class);

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

    }

}
  • μ½”λ“œλ₯Ό μ‚΄νŽ΄λ³΄λ©΄ μŠ€ν”„λ§ μ»¨ν…Œμ΄λ„ˆμ— λ“±λ‘λœ bean을 μ‘°νšŒν•˜κ³  클래슀λͺ…을 좜λ ₯ν•˜λŠ” μ½”λ“œμ΄λ‹€.
  • AppConfigλŠ” @Configuration 으둜 λ“±λ‘λœ ν΄λž˜μŠ€μ΄λ‹€.

μ‹€ν–‰κ²°κ³Ό

  • bean = class hello.core.AppConfig$$EnhancerBySpringCGLIB$$573a269b
  • 클래슀 정보λ₯Ό ν™•μΈν•΄λ³΄λ‹ˆ $$EnhancerBySpringCGLIB$$573a269b μ΄λΌλŠ” μ΄μƒν•œ λ‚΄μš©μ΄ μΆ”κ°€λ˜μ—ˆλ‹€.
  • 이것은 λ‚΄κ°€ λ§Œλ“  ν΄λž˜μŠ€κ°€ μ•„λ‹ˆλΌ μŠ€ν”„λ§μ΄ CGLIB λΌλŠ” λ°”μ΄νŠΈμ½”λ“œ μ‘°μž‘ 라이브러리λ₯Ό μ‚¬μš©ν•΄μ„œ AppConfig 클래슀λ₯Ό 상속받은 μž„μ˜μ˜ λ‹€λ₯Έ 클래슀λ₯Ό λ§Œλ“€κ³ , κ·Έ λ‹€λ₯Έ 클래슀λ₯Ό μŠ€ν”„λ§ 빈으둜 λ“±λ‘ν•œ 것이닀.
  • κ·Έ μž„μ˜μ˜ λ‹€λ₯Έ ν΄λž˜μŠ€κ°€ λ°”λ‘œ 싱글톀이 보μž₯λ˜λ„λ‘ ν•΄μ€€λ‹€.
  • @Bean이 뢙은 λ©”μ„œλ“œλ§ˆλ‹€ 이미 μŠ€ν”„λ§ 빈이 μ‘΄μž¬ν•˜λ©΄ μ‘΄μž¬ν•˜λŠ” λΉˆμ„ λ°˜ν™˜ν•˜κ³ , μŠ€ν”„λ§ 빈이 μ—†μœΌλ©΄ 생성 ν•΄μ„œ μŠ€ν”„λ§ 빈으둜 λ“±λ‘ν•˜κ³  λ°˜ν™˜ν•˜λŠ” μ½”λ“œκ°€ λ™μ μœΌλ‘œ λ§Œλ“€μ–΄μ§„λ‹€.
  • κ·Έλ ‡λ‹€λ©΄, @Configuration 이 μ—†λ‹€λ©΄ μ–΄λ–»κ²Œ 될까?

  • @Configuration 을 μ£Όμ„μ²˜λ¦¬ν•˜κ³  μ‹€ν–‰ν•œ ν…ŒμŠ€νŠΈ 결과이닀.
  • AppConfig.memberRepository κ°€ 반볡적으둜 μ‹€ν–‰λœ 것을 확인할 수 μžˆλ‹€. 객체가 3개 μƒμ„±λ˜μ—ˆμ§€λ§Œ 싱글톀을 보μž₯ν•˜μ§€ μ•ŠλŠ”λ‹€.
  • @Configuration 을 μ‚¬μš©ν•˜λ©΄ μŠ€ν”„λ§ λΉˆμ€ μ‹±κΈ€ν†€μœΌλ‘œ 보μž₯ν•˜μ§€λ§Œ, μ—†λ‹€λ©΄ μŠ€ν”„λ§ λΉˆμ€ λ“±λ‘λ˜μ§€λ§Œ 싱글톀을 보μž₯ν•˜μ§€ μ•ŠλŠ”λ‹€.
  • μŠ€ν”„λ§ μ»¨ν…Œμ΄λ„ˆλ₯Ό 등둝할 λ•ŒλŠ” 항상 @Configuration을 μ‚¬μš©ν•˜λ„λ‘ ν•˜μž.

ν•™μŠ΅μ— 도움이 λ˜μ—ˆλ˜ 자료

profile
항상 λ°°μš°λŠ” μžμ„Έλ‘œ πŸͺ΄

0개의 λŒ“κΈ€