본문 바로가기
IT 개인 공부/Spring

[Spring] 빈 스코프(Scope) 종류

by Libi 2021. 8. 23.
반응형

스프링 컨테이너에서 다양한 빈들을 관리해줌으로써 애플리케이션을 개발할 때 굉장히 편리하게 개발할 수 있는 장점이 있다.

스프링 빈의 생명주기는 다음과 같다.

스프링 컨테이너 생성 -> 스프링 빈 생성 -> 의존관계 주입 -> 초기화 콜백 -> 사용 -> 소멸 전 콜백 -> 스프링 종료

 

빈 스코프란 빈이 스프링 컨테이너에서 존재할 수 있는 범위를 의미한다.

스프링은 다양한 스코프를 지원하는데 싱글톤, 프로토타입, 웹 관련 스코프들에 대해서 알아보도록 하자.

 

 

싱글톤 스코프

 

스프링 컨테이너에 빈을 등록하면 기본적으로 싱글톤 패턴으로 빈을 관리해준다.

애플리케이션 생명주기 동안 해당 빈의 요청이 발생할 때마다 항상 동일한 빈을 제공해줘야 하기 때문에 싱글톤 스코프는 스프링 컨테이너의 시작과 종료, 모든 생명주기를 함께하는 가장 넓은 범위를 가진 스코프이다.

 

그렇다면 정말로 빈이 한 번만 생성되고 동일한 빈을 반환하는지 확인해보자.

class SingletonScopeTest {

    @Test
    void test() {
        //1.스프링 컨테이너 생성
        AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(MyBean.class);

        System.out.println("MyBean = " + ac.getBean(MyBean.class));
        System.out.println("MyBean = " + ac.getBean(MyBean.class));

	//2.스프링 컨테이너 종료 : 스프링 빈의 생명주기에 따라 종료되기 전에 빈들이 먼저 소멸됨
        ac.close();
    }

    @Scope("singleton")
    static class MyBean {

        @PostConstruct
        public void init() {
            System.out.println("싱글톤 빈 초기화");
        }

        @PreDestroy
        public void destroy() {
            System.out.println("싱글톤 빈 소멸");
        }
    }
}

 

MyBean 클래스를 싱글톤 스코프로 지정하여 스프링 컨테이너에 등록 후 빈을 조회해보면 동일한 빈이 조회되는 것을 확인할 수 있다.

또한, 빈 초기화 시 수행되는 @PostConstruct, 빈 소멸 시 수행되는 @PreDestroy가 한 번씩만 수행되는 것을 확인할 수 있다.

 

 

 

프로토타입 스코프

 

매번 요청마다 동일한 빈이 아닌 새로운 빈이 필요한 경우도 있을 것이다. 이때 사용하는 것이 바로 프로토타입 빈이다.

프로토타입 빈은 매번 새로운 빈을 생성해서 반환한다.

왜냐하면 프로토타입 빈은 스프링 컨테이너가 생성과 의존관계 주입, 초기화까지만 관여하고 이후의 프로세스는 관리하지 않기 때문이다.

즉, 사용 시점에서 프로토타입 빈의 모든 책임은 클라이언트가 가지게 된다.

 

그렇다면 정말로 매번 새로운 빈을 생성하여 반환하는지 확인해보자.

class PrototypeScopeTest {

    @Test
    void test() {
        //1.스프링 컨테이너 생성
        AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(MyBean.class);

        System.out.println("MyBean = " + ac.getBean(MyBean.class));
        System.out.println("MyBean = " + ac.getBean(MyBean.class));
        
        //2.스프링 컨테이너 종료 - 스프링 빈의 생명주기에 따라 종료되기 전에 빈들이 먼저 소멸됨
        ac.close();
    }

    @Scope("prototype")
    static class MyBean {

        @PostConstruct
        public void init() {
            System.out.println("프로토타입 빈 초기화");
        }

        @PreDestroy
        public void destroy() {
            System.out.println("프로토타입 빈 소멸");
        }
    }
}

 

MyBean 클래스를 프로토타입 스코프로 지정하여 스프링 컨테이너에 등록 후 빈을 조회해보면 매번 새로운 빈이 조회되는 것을 확인할 수 있다.

또한, 빈 초기화 시 수행되는 @PostConstruct는 두 번의 빈을 조회하기 때문에 두 번 호출되었으며, 빈 소멸 시 수행되는 @PreDestroy는 수행되지 않은 것을 확인할 수 있다.

왜냐하면 프로토타입 빈은 사용시점부터 관리의 책임이 클라이언트에게 있기 때문이다.

 

 

 

웹 관련 스코프

 

마지막으로 웹 애플리케이션에서 동작하는 스코프이다. 웹 스코프는 싱글톤 스코프처럼 스프링 컨테이너가 생성 시점부터 종료 시점까지 관리해준다.

웹 관련 스코프에는 request, session, application, websocket 등의 스코프가 존재하며, 이들의 동작 과정은 비슷하다.

  • Request : HTTP 요청 하나가 들어오고 나갈 때까지 유지되는 스코프, 각각의 HTTP 요청마다 별도의 빈 인스턴스가 생성되고 관리된다.
  • Session : HTTP Session과 동일한 생명주기를 가지는 스코프
  • Application : Servlet Context와 동일한 생명주기를 가지는 스코프
  • Websocket : Web Socket과 동일한 생명주기를 가지는 스코프

 

그렇다면 Request 스코프 역시 잘 동작하는지 확인해보자. 다만, 웹 스코프들은 웹 환경에서만 동작하기 때문에 스프링 부트를 실제로 실행해야 한다.

따라서 먼저 빈으로 등록할 MyBean 클래스를 구현해주자.

MyBean 클래스를 구현할 때 request 스코프를 넣어주고, proxyMode = ScopedProxyMode.TARGET_CLASS 옵션을 넣어주도록 하자.

request 스코프는 실제로 요청이 왔을 때 생성되기 때문에 가짜 프록시 객체를 주입해주기 때문에 테스트가 가능해진다.

@Component
@Scope(value = "request", proxyMode = ScopedProxyMode.TARGET_CLASS)
public class MyBean {

    public void printURL(String requestURL) {
        System.out.println("RequestURL : " + requestURL);
    }

    @PostConstruct
    public void init() {
        System.out.println("Request 빈 초기화");
    }

    @PreDestroy
    public void destroy() {
        System.out.println("Request 빈 소멸");
    }
}

 

다음으로 request를 처리할 MyController를 구현해주자.

@RequiredArgsConstructor
@RestController
public class MyController {

    private final MyBean myBean;

    @GetMapping
    public String request(HttpServletRequest request) {
        String requestURL = request.getRequestURL().toString();
        myBean.printURL(requestURL);
        return myBean.getClass().toString();
    }
}

 

스프링 부트를 실행하여 http://localhost:8080/를 접속하게 되면 MyController의 request() 메서드가 실행되면서 request 빈이 생성, 사용, 소멸되는 것을 확인할 수 있다.

 

또한, CGLIB를 통해 가짜 프록시 객체가 조회되는 것을 확인할 수 있다.

 

 

 

 

[ Reference ]

· https://www.inflearn.com/course/%EC%8A%A4%ED%94%84%EB%A7%81-%ED%95%B5%EC%8B%AC-%EC%9B%90%EB%A6%AC-%EA%B8%B0%EB%B3%B8%ED%8E%B8/dashboard

 

 

반응형

댓글