관리 메뉴

deVlog

[Spring] Bean Scope(빈 μŠ€μ½”ν”„) μ’…λ₯˜ λ³Έλ¬Έ

🌈 Framework/Spring

[Spring] Bean Scope(빈 μŠ€μ½”ν”„) μ’…λ₯˜

은루밍 2024. 12. 4. 15:53

λͺ©μ°¨

     

     

       

    ☺️ Bean Scope λž€?

    λŒ€λΆ€λΆ„μ€ μŠ€ν”„λ§ 빈이 μŠ€ν”„λ§ μ»¨ν…Œμ΄λ„ˆμ˜ μ‹œμž‘κ³Ό ν•¨κ»˜ μƒμ„±λ˜μ–΄μ„œ μŠ€ν”„λ§ μ»¨ν…Œμ΄λ„ˆκ°€ μ’…λ£Œλ  λ•ŒκΉŒμ§€ μœ μ§€λœλ‹€κ³  μ•Œκ³  μžˆμ„ 것이닀.

    이것은 μŠ€ν”„λ§ 빈이 기본적으둜 싱글톀 μŠ€μ½”ν”„λ‘œ μƒμ„±λ˜κΈ° λ•Œλ¬Έμ΄λ‹€.

     

    κ·Έ μ΄μœ λŠ” ν•˜λ‚˜λ§Œ λ§Œλ“€μ–΄λ‘κ³  κ³΅μœ ν•΄μ„œ μ‚¬μš©ν•˜κΈ° μœ„ν•¨μΈλ°, 이 λ•Œλ¬Έμ— Bean에 μƒνƒœλ₯Ό μ €μž₯ν•˜λŠ” μ½”λ“œλ₯Ό μž‘μ„±ν•˜λŠ” 것은 λ™μ‹œμ„± 문제λ₯Ό μœ λ°œν•  수 μžˆλ‹€. λ˜ν•œ μš”κ΅¬μ‚¬ν•­κ³Ό κ΅¬ν˜„ κΈ°λŠ₯ λ“±μ˜ ν•„μš”μ— λ”°λΌμ„œ 비싱글톀이 ν•„μš”ν•œ κ²½μš°λ„ μžˆλ‹€. 이λ₯Ό λͺ…μ‹œμ μœΌλ‘œ κ΅¬λΆ„ν•˜κΈ° μœ„ν•΄μ„œ scope λΌλŠ” ν‚€μ›Œλ“œλ₯Ό μ œκ³΅ν•œλ‹€.

     

    Spring μ—λŠ” λ‹€μŒκ³Ό 같은 Bean Scope 듀이 μ‘΄μž¬ν•œλ‹€.

     

     

     

     πŸ”­ Bean Scope 의 μ’…λ₯˜

    싱글톀(Singleton)

    • Spring μ—μ„œ κΈ°λ³Έ
    • μŠ€ν”„λ§ μ»¨ν…Œμ΄λ„ˆμ˜ μ‹œμž‘κ³Ό μ’…λ£ŒκΉŒμ§€ μœ μ§€λ˜λŠ” κ°€μž₯ 넓은 λ²”μœ„μ˜ μŠ€μ½”ν”„

     

    ν”„λ‘œν† νƒ€μž…(Prototype)

    • μŠ€ν”„λ§ μ»¨ν…Œμ΄λ„ˆλŠ” ν”„λ‘œν† νƒ€μž… 빈의 생성과 μ˜μ‘΄κ΄€κ³„ μ£Όμž…κΉŒμ§€λ§Œ κ΄€μ—¬ν•˜κ³  λ”λŠ” κ΄€λ¦¬ν•˜μ§€ μ•ŠλŠ” 맀우 짧은 λ²”μœ„μ˜ μŠ€μ½”ν”„
    • μš”μ²­μ΄ 였면 항상 μƒˆλ‘œμš΄ μΈμŠ€ν„΄μŠ€λ₯Ό μƒμ„±ν•˜μ—¬ λ°˜ν™˜ν•˜κ³  이후에 κ΄€λ¦¬ν•˜μ§€ μ•ŠλŠ”λ‹€.
    • μ΄ˆκΈ°ν™” λ©”μ„œλ“œκΉŒμ§€λŠ” λΆ€λ₯΄μ§€λ§Œ μ’…λ£Œ λ©”μ‹œμ§€ 호좜이 λ˜μ§€ μ•ŠλŠ”λ‹€.
    • ν”„λ‘œν† νƒ€μž…μ„ 받은 ν΄λΌμ΄μ–ΈνŠΈκ°€ 객체λ₯Ό 관리해야 ν•œλ‹€.


    μ›Ή κ΄€λ ¨ μŠ€μ½”ν”„

    • request
      • μ›Ή μš”μ²­μ΄ λ“€μ–΄μ˜€κ³  λ‚˜κ°ˆ λ•ŒκΉŒμ§€ μœ μ§€λ˜λŠ” μŠ€μ½”ν”„
    • session
      • μ›Ή μ„Έμ…˜μ΄ μƒμ„±λ˜κ³  μ’…λ£Œλ  λ•ŒκΉŒμ§€ μœ μ§€λ˜λŠ” μŠ€μ½”ν”„
    • application
      • μ›Ήμ˜ μ„œλΈ”λ¦Ώ μ»¨ν…μŠ€νŠΈμ™€ 같은 λ²”μœ„λ‘œ μœ μ§€λ˜λŠ” μŠ€μ½”ν”„

     

     

    🎲 Bean Scope μ§€μ • μ˜ˆμ‹œ

    빈 μŠ€μ½”ν”„λŠ” λ‹€μŒκ³Ό 같이 μ§€μ •ν•  수 μžˆλ‹€.

     

    1. μ»΄ν¬λ„ŒνŠΈ μŠ€μΊ” μžλ™ 등둝

    @Scope("prototype")
    @Component
    public class HelloBean {}

     

    2. μˆ˜λ™ 등둝

    @Scope("prototype")
    @Bean
    ProtoTypeBean HelloBean() {
        return new HelloBean();
    }

     

     

     

    ‼️ ν”„λ‘œν† νƒ€μž… μŠ€μ½”ν”„?

    싱글톀 μŠ€μ½”ν”„μ˜ λΉˆμ„ μ‘°νšŒν•˜λ©΄ μŠ€ν”„λ§ μ»¨ν…Œμ΄λ„ˆλŠ” 항상 같은 μΈμŠ€ν„΄μŠ€μ˜ μŠ€ν”„λ§ λΉˆμ„ λ°˜ν™˜ν•œλ‹€.

    λ°˜λ©΄μ— ν”„λ‘œν† νƒ€μž… μŠ€μ½”ν”„λ₯Ό μŠ€ν”„λ§ μ»¨ν…Œμ΄λ„ˆμ—μ„œ μ‘°νšŒν•˜λ©΄ μŠ€ν”„λ§ μ»¨ν…Œμ΄λ„ˆλŠ” 항상 μƒˆλ‘œμš΄ μΈμŠ€ν„΄μŠ€λ₯Ό μƒμ„±ν•΄μ„œ λ°˜ν™˜ν•œλ‹€.

     

     

     

    1. 싱글톀 μŠ€μ½”ν”„μ˜ λΉˆμ„ μŠ€ν”„λ§ μ»¨ν…Œμ΄λ„ˆμ— μš”μ²­ν•œλ‹€.
    2. μŠ€ν”„λ§ μ»¨ν…Œμ΄λ„ˆλŠ” 본인이 κ΄€λ¦¬ν•˜λŠ” μŠ€ν”„λ§ λΉˆμ„ λ°˜ν™˜ν•œλ‹€.
    3. 이후에 μŠ€ν”„λ§ μ»¨ν…Œμ΄λ„ˆμ— 같은 μš”μ²­μ΄ 와도 같은 객체 μΈμŠ€ν„΄μŠ€μ˜ μŠ€ν”„λ§ λΉˆμ„ λ°˜ν™˜ν•œλ‹€.

     

     

     

    1. ν”„λ‘œν† νƒ€μž… μŠ€μ½”ν”„μ˜ λΉˆμ„ μŠ€ν”„λ§ μ»¨ν…Œμ΄λ„ˆμ— μš”μ²­ν•œλ‹€.
    2. μŠ€ν”„λ§ μ»¨ν…Œμ΄λ„ˆλŠ” μš”μ²­ μ‹œμ μ— ν”„λ‘œν† νƒ€μž… λΉˆμ„ μƒμ„±ν•˜κ³ , ν•„μš”ν•œ μ˜μ‘΄κ΄€κ³„λ₯Ό μ£Όμž…ν•œλ‹€.

     

     

    3. μŠ€ν”„λ§ μ»¨ν…Œμ΄λ„ˆλŠ” μƒμ„±ν•œ ν”„λ‘œν† νƒ€μž… λΉˆμ„ ν΄λΌμ΄μ–ΈνŠΈμ— λ°˜ν™˜ν•œλ‹€.
    4. 이후에 μŠ€ν”„λ§ μ»¨ν…Œμ΄λ„ˆμ— 같은 μš”μ²­μ΄ 였면 항상 μƒˆλ‘œμš΄ ν”„λ‘œν† νƒ€μž… λΉˆμ„ μƒμ„±ν•΄μ„œ λ°˜ν™˜ν•œλ‹€.

     

    μ—¬κΈ°μ„œ 핡심은 μŠ€ν”„λ§ μ»¨ν…Œμ΄λ„ˆλŠ” ν”„λ‘œν† νƒ€μž… λΉˆμ„ μƒμ„±ν•˜κ³ , μ˜μ‘΄κ΄€κ³„ μ£Όμž…, μ΄ˆκΈ°ν™”κΉŒμ§€λ§Œ μ²˜λ¦¬ν•œλ‹€λŠ” 것이닀. ν΄λΌμ΄μ–ΈνŠΈμ— λΉˆμ„ λ°˜ν™˜ν•˜κ³ , 이후 μŠ€ν”„λ§ μ»¨ν…Œμ΄λ„ˆλŠ” μƒμ„±λœ ν”„λ‘œν† νƒ€μž… λΉˆμ„ κ΄€λ¦¬ν•˜μ§€ μ•ŠλŠ”λ‹€. ν”„λ‘œν† νƒ€μž… λΉˆμ„ 관리할 μ±…μž„μ€ ν”„λ‘œν† νƒ€μž… λΉˆμ„ 받은 ν΄λΌμ΄μ–ΈνŠΈμ— μžˆλ‹€. κ·Έλž˜μ„œ @PreDestroy μ™€ κ°™μ€ μ’…λ£Œ λ©”μ„œλ“œκ°€ ν˜ΈμΆœλ˜μ§€ μ•ŠλŠ”λ‹€.

     

     

     

    πŸ§ͺ λΉˆ μŠ€μ½”ν”„ ν…ŒμŠ€νŠΈ

    πŸ«› 싱글톀 μŠ€μ½”ν”„ 빈

    import jakarta.annotation.PostConstruct;
    import jakarta.annotation.PreDestroy;
    import org.junit.jupiter.api.Test;
    import org.springframework.context.annotation.AnnotationConfigApplicationContext;
    import org.springframework.context.annotation.Scope;
    
    import static org.assertj.core.api.Assertions.assertThat;
    
    class BeanTest {
    
        @Test
        void singletonBeanFind() {
            AnnotationConfigApplicationContext ac = new
                    AnnotationConfigApplicationContext(SingletonBean.class);
            SingletonBean singletonBean1 = ac.getBean(SingletonBean.class);
            SingletonBean singletonBean2 = ac.getBean(SingletonBean.class);
            System.out.println("singletonBean1 = " + singletonBean1);
            System.out.println("singletonBean2 = " + singletonBean2);
            assertThat(singletonBean1).isSameAs(singletonBean2);
            ac.close(); //μ’…λ£Œ
        }
    
        @Scope("singleton")
        static class SingletonBean {
            @PostConstruct
            public void init() {
                System.out.println("SingletonBean.init");
            }
    
            @PreDestroy
            public void destroy() {
                System.out.println("SingletonBean.destroy");
            }
        }
    }

     

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

    SingletonBean.init
    singletonBean1 = νŒ¨ν‚€μ§€λͺ….BeanTest$SingletonBean@134d26af
    singletonBean2 = νŒ¨ν‚€μ§€λͺ….BeanTest$SingletonBean@134d26af
    SingletonBean.destroy
    • @PostConstruct(빈 μ΄ˆκΈ°ν™”) λ©”μ„œλ“œ μ‹€ν–‰
    • λ™μΌν•œ μΈμŠ€ν„΄μŠ€ 빈 쑰회
    • @PreDestory(μ’…λ£Œ) λ©”μ„œλ“œ μ‹€ν–‰

     

    🫘 ν”„λ‘œν† νƒ€μž… μŠ€μ½”ν”„ 빈 

    import jakarta.annotation.PostConstruct;
    import jakarta.annotation.PreDestroy;
    import org.junit.jupiter.api.Test;
    import org.springframework.context.annotation.AnnotationConfigApplicationContext;
    import org.springframework.context.annotation.Scope;
    import static org.assertj.core.api.Assertions.assertThat;
    
    
    class PrototypeTest {
        @Test
        void prototypeBeanFind() {
            AnnotationConfigApplicationContext ac = new
                    AnnotationConfigApplicationContext(PrototypeBean.class);
            PrototypeBean prototypeBean1 = ac.getBean(PrototypeBean.class);
            PrototypeBean prototypeBean2 = ac.getBean(PrototypeBean.class);
            System.out.println("prototypeBean1 = " + prototypeBean1);
            System.out.println("prototypeBean2 = " + prototypeBean2);
            assertThat(prototypeBean1).isNotSameAs(prototypeBean2);
            ac.close(); //μ’…λ£Œ
        }
    
        @Scope("prototype")
        static class PrototypeBean {
            @PostConstruct
            public void init() {
                System.out.println("PrototypeBean.init");
            }
    
            @PreDestroy
            public void destroy() {
                System.out.println("PrototypeBean.destroy");
            }
        }
    }

     

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

    PrototypeBean.init
    PrototypeBean.init
    prototypeBean1 = νŒ¨ν‚€μ§€.PrototypeTest$PrototypeBean@663411de
    prototypeBean2 = νŒ¨ν‚€μ§€.PrototypeTest$PrototypeBean@63dd899
    • 싱글톀 λΉˆμ€ μŠ€ν”„λ§ μ»¨ν…Œμ΄λ„ˆ 생성 μ‹œμ μ— μ΄ˆκΈ°ν™” λ©”μ„œλ“œκ°€ μ‹€ν–‰ λ˜μ§€λ§Œ, ν”„λ‘œν† νƒ€μž… μŠ€μ½”ν”„μ˜ λΉˆμ€ μŠ€ν”„λ§ μ»¨ν…Œμ΄λ„ˆμ—μ„œ λΉˆμ„ μ‘°νšŒν•  λ•Œ μƒμ„±λ˜κ³ , μ΄ˆκΈ°ν™” λ©”μ„œλ“œλ„ μ‹€ν–‰λœλ‹€.
    • ν”„λ‘œν† νƒ€μž… λΉˆμ„ 2번 μ‘°νšŒν–ˆμœΌλ―€λ‘œ μ™„μ „νžˆ λ‹€λ₯Έ μŠ€ν”„λ§ 빈이 μƒμ„±λ˜κ³ , μ΄ˆκΈ°ν™”λ„ 2번 μ‹€ν–‰λœ 것을 확인할 수 μžˆλ‹€.
    • 싱글톀 λΉˆμ€ μŠ€ν”„λ§ μ»¨ν…Œμ΄λ„ˆκ°€ κ΄€λ¦¬ν•˜κΈ° λ•Œλ¬Έμ— μŠ€ν”„λ§ μ»¨ν…Œμ΄λ„ˆκ°€ μ’…λ£Œλ  λ•Œ 빈의 μ’…λ£Œ λ©”μ„œλ“œκ°€ μ‹€ν–‰λ˜μ§€λ§Œ, ν”„λ‘œν† νƒ€μž… λΉˆμ€ μŠ€ν”„λ§ μ»¨ν…Œμ΄λ„ˆκ°€ 생성과 μ˜μ‘΄κ΄€κ³„ μ£Όμž… 그리고 μ΄ˆκΈ°ν™” κΉŒμ§€λ§Œ κ΄€μ—¬ν•˜κ³ , λ”λŠ” κ΄€λ¦¬ν•˜μ§€ μ•ŠλŠ”λ‹€. λ”°λΌμ„œ ν”„λ‘œν† νƒ€μž… λΉˆμ€ μŠ€ν”„λ§ μ»¨ν…Œμ΄λ„ˆκ°€ μ’…λ£Œλ  λ•Œ @PreDestroy 와 κ°™μ€ μ’…λ£Œ λ©”μ„œλ“œκ°€ μ‹€ν–‰λ˜μ§€ μ•ŠλŠ”λ‹€.

     

    ν”„λ‘œν† νƒ€μž… 빈의 νŠΉμ§•

    • μŠ€ν”„λ§ μ»¨ν…Œμ΄λ„ˆμ— μš”μ²­ν•  λ•Œλ§ˆλ‹€ μƒˆλ‘œ μƒμ„±λœλ‹€.
    • μŠ€ν”„λ§ μ»¨ν…Œμ΄λ„ˆλŠ” ν”„λ‘œν† νƒ€μž… 빈의 생성과 μ˜μ‘΄κ΄€κ³„ μ£Όμž… 그리고 μ΄ˆκΈ°ν™”κΉŒμ§€λ§Œ κ΄€μ—¬ν•œλ‹€.
    • μ’…λ£Œ λ©”μ„œλ“œκ°€ ν˜ΈμΆœλ˜μ§€ μ•ŠλŠ”λ‹€.
    • ν”„λ‘œν† νƒ€μž… λΉˆμ€ ν”„λ‘œν† νƒ€μž… λΉˆμ„ μ‘°νšŒν•œ ν΄λΌμ΄μ–ΈνŠΈκ°€ 관리해야 ν•œλ‹€. μ’…λ£Œ λ©”μ„œλ“œμ— λŒ€ν•œ ν˜ΈμΆœλ„ ν΄λΌμ΄μ–ΈνŠΈκ°€ 직접 ν•΄μ•Όν•œλ‹€.

     

     

    πŸ€” ν”„λ‘œν† νƒ€μž… λΉˆμ€ μ–Έμ œ μ‚¬μš©ν•΄μ•Ό ν• κΉŒ?

    1. 객체λ₯Ό 맀번 μƒˆλ‘œ 생성해야 ν•  λ•Œ

    • μ„œλ‘œ 독립적인 μƒνƒœλ₯Ό κ°€μ§„ 객체가 ν•„μš”ν•  λ•Œ μœ μš©ν•©λ‹ˆλ‹€.
    • e.g. μš”μ²­λ§ˆλ‹€ λ‹€λ₯Έ μƒνƒœλ₯Ό μœ μ§€ν•΄μ•Ό ν•˜λŠ” λΉ„μ¦ˆλ‹ˆμŠ€ 둜직 ν΄λž˜μŠ€λ‚˜ μž„μ‹œ 데이터 μ €μž₯μ†Œ

    2. μƒνƒœλ₯Ό κ°€μ§„ Bean을 λ‹€λ£° λ•Œ

    • singleton μŠ€μ½”ν”„μ™€ 달리, prototype Bean은 μƒνƒœλ₯Ό κ³΅μœ ν•˜μ§€ μ•ŠλŠ”λ‹€.
    • μƒνƒœκ°€ 독립적이어야 ν•˜κ±°λ‚˜ νŠΉμ • λ‘œμ§μ—μ„œ 객체 μƒνƒœκ°€ λ³€ν•˜λ©΄ μ•ˆ λ˜λŠ” κ²½μš°μ— μ ν•©ν•˜λ‹€.
    • e.g. μ—¬λŸ¬ μ“°λ ˆλ“œκ°€ λ™μ‹œμ— μ ‘κ·Όν•˜μ—¬ 데이터 좩돌이 λ°œμƒν•  수 μžˆλŠ” 경우.

    3. 단기간 μ‚¬μš© ν›„ μ†Œλ©Έν•˜λŠ” 객체

    • prototype μŠ€μ½”ν”„λŠ” Spring μ»¨ν…Œμ΄λ„ˆκ°€ μƒμ„±ν•œ μ΄ν›„μ˜ Bean의 라이프사이클을 κ΄€λ¦¬ν•˜μ§€ μ•ŠλŠ”λ‹€.
    • λ”°λΌμ„œ 단기간 μ‚¬μš©λ˜κ³ , μ• ν”Œλ¦¬μΌ€μ΄μ…˜ μ „μ²΄μ—μ„œ 계속 μœ μ§€λ  ν•„μš”κ°€ μ—†λŠ” 경우 μ ν•©ν•˜λ‹€.
    • e.g. μΌνšŒμ„± μž‘μ—…μ„ μˆ˜ν–‰ν•˜λŠ” μ„œλΉ„μŠ€λ‚˜ μœ ν‹Έλ¦¬ν‹° 클래슀

    4. DI (Dependency Injection)μ—μ„œ ν™œμš©

    • νŠΉμ • singleton Bean이 prototype Bean을 μ£Όμž…λ°›μ•„ 맀번 μƒˆλ‘œμš΄ μΈμŠ€ν„΄μŠ€λ₯Ό μ‚¬μš©ν•΄μ•Ό ν•  λ•Œ μœ μš©ν•˜λ‹€.
    • Spring의 @Scope("prototype")κ³Ό ν•¨κ»˜ ObjectProviderλ‚˜ @Lookup을 μ‚¬μš©ν•΄ μƒˆλ‘œμš΄ μΈμŠ€ν„΄μŠ€λ₯Ό μš”μ²­ν•  수 μžˆλ‹€.

     

     

     

    πŸ“— μ°Έκ³ 

     

    '🌈 Framework > Spring' μΉ΄ν…Œκ³ λ¦¬μ˜ λ‹€λ₯Έ κΈ€

    [Spring] μŠ€ν”„λ§ 빈 μŠ€μ½”ν”„ μ™„λ²½ κ°€μ΄λ“œ  (0) 2025.04.04