Spring - (12) : 빈 스코프

­이승환·2021년 12월 9일
0

spring

목록 보기
10/26

서론


앞선 포스팅들에서 스프링 프레임워크는 컨테이너 내부에서 빈들을 관리해주는 것을 배웠다. 스프링 컨테이너에서 다양한 빈들을 관리해줌으로써 애플리케이션을 개발할 때 굉장히 편리하게 개발할 수 있는 장점이 있다.

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

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

이번 포스팅에서는 빈 스코프에서 떠들어보려고 한다. 빈 스코프란 빈이 스프링 컨테이너에서 존재할 수 있는 범위를 의미한다.

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

싱글톤 스코프

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

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

아래 테스트를 확인해 보자

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("싱글톤 빈 소멸");
        }
    }
}

결과는 아래와 같다.

init(), destroy() 메서드는 컨테이너에 빈이 생성될때 처음 실행되는 메서드들이다. 빈 초기화 시 수행되는 @PostConstruct, 빈 소멸 시 수행되는 @PreDestroy를 이용했기 때문이다.

또한 주소에서 싱글톤이니 만큼 동일한 주고사 찍히는 것을 확인 할 수있다.
@scope 어노테이션을 통해 명시적으로 singleton임을 적어놓았으나, default 값이므로 생략해도 된다.

프로토타입 스코프

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

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

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

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

테스트 예제는 아래와 같다.

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 를 처리할 컨트롤러이다.

@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 빈이 생성, 사용, 소멸되는 것을 확인할 수 있다.@RestController 로 인해서 json 을 client에 던지게 된다.

profile
Mechanical & Computer Science

0개의 댓글