Post

[강의] 스프링 핵심 원리 - 기본편 09 빈 스코프

스프링 핵심 원리 - 기본편 강의

김영한님의 인프런 강의(스프링 핵심 원리 - 기본편) 을 수강하면서 강의 내용을 일부 발췌해 요약한 글.

섹션 9 빈 스코프

빈 스코프

빈이 존재할 수 있는 범위

  • 싱글톤 : 기본 스코프로 스프링 컨테이너의 시작과 종료까지 유지되는, 가장 넓은 범위의 스코프
  • 프로토타입 : 의존관계 주입 후 초기화 메서드까지 불러주고 더는 관리하지 않음
  • 웹 관련 스코프 : 스프링 웹 관련 기능이 들어가야 사용 가능한 스코프로 request, session, application, websocket 이 있음

스코프 지정은 @Scope("prototype") 과 같은 형태로 컴포넌트 스캔 위에 자동 등록하거나 빈 위에 수동등록하면 된다.

프로토타입 스코프

항상 같은 인스턴스를 반환하는 싱글톤 스코프의 빈과 달리, 프로토타입 스코프는 항상 새로운 인스턴스를 생성해 반환한다.

[싱글톤 스코프] 싱글톤 스코프의 빈은 항상 같은 인스턴스를 반환하며 먼저 생성된다. 스프링 컨테이너가 종료될 때 빈의 종료 메서드가 실행된다.

image

[프로토타입 스코프] 프로토타입 스코프의 빈은 항상 새로운 인스턴스를 반환하며 요청 시 생성된다. 종료 메서드가 실행되지 않는다.

image

스프링 컨테이너는 프로토타입 스코프의 빈을 요청 시에 생성하고 필요한 의존관계를 주입 후 반환한다. 이후엔 더이상 관리하지 않으므로 같은 요청이 오더라도 새로운 빈을 생성해 반환한다. 당연히 종료 메서드가 호출되지 않는다. 종료 메서드를 호출애햐 한다면 prototypeBean.destroy() 처럼 직접 호출해주어야 한다.

싱글톤과 프로토타입 스코프

스프링은 일반적으로 싱글톤 빈을 사용하기 때문에 싱글톤 빈이 프로토타입빈을 사용하게 된다. 그러나 싱글톤에서 프로토타입 빈을 사용하는 경우, 스프링 컨테이너에서 요청을 받을 때마다 인스턴스를 생성하는 것이 아니라 처음 주입받은 인스턴스를 그대로 계속 사용하게 되어 본래 의도와는 벗어나게 되는 경우가 발생할 수 있다.

이를 해결하기 위해서는 여러 방법이 있다.

  • 로직을 호출할 때마다 프로토타입을 요청:

    의존관계를 주입(DI)하는 게 아니라 필요한 의존관계를 찾는 것을 DL(Dependency Lookup)이라고 한다. 그러나 이러한 방식은 스프링에 종속적이게 되므로 좋은 방식은 아니다.

  • ObjectFactory, ObjectProvider

    ObjectProvider는 getObject()를 호출할 때 스프링 컨테이너에서 이를 찾아 반환해준다. ObjectProvider는 ObjectFactory에 부가적인 기능이 추가된 것이라고 보면 된다. 핵심 컨셉은 스프링 컨테이너에서 대신 조회해주는 대리자라고 생각하면 된다. (DL) 단순하므로 코드 만들기는 쉬워지지만 여전히 스프링에 의존한다.

  • JSR-330 Provider 자바 표준으로, 라이브러리를 추가해 사용해 주어야 한다. implementation 'jakarta.inject:jakarta.inject-api:2.0.1' Provider, provider.get()으로 호출. 스프링이 아닌 다른 컨테이너에서도 사용해야 할 때 사용!

  • @Lookup 강의에서는 생략되었으며 이전 기능들로도 충분하다.

웹 관련 스코프

웹 환경에서만 동작하는 스코프로 스프링이 종료시점까지 관리해주므로 종료 메서드가 호출된다.

  • request : HTTP 요청 하나가 들어오고 나갈 때까지 유지되며, 각각의 HTTP 요청마다 별도의 빈 인스턴스가 생성되고 관리됨
  • session : HTTP 세션이 생성되고 종료될 때까지 유지(HTTP 세션과 동일한 생명주기)
  • application : 웹이 서블릿 컨텍스트와 동일한 범위로 유지
  • websocket : 웹 소켓과 동일한 생명주기

스코프와 프록시

request scope를 확인하기 위한 예제를 작성할 때, 요청하기도 전에 스프링 컨테이너가 동작해야 하므로(?) 에러가 발생한다. 이때, ObjectProvider를 사용하면 해결할 수 있으나 코드가 복잡해지는데 스코프 어노테이션 내의 proxyMode를 설정하면 간결하게 작성이 가능하다. 이렇게 하면 CGLIB라는 라이브러리로 가짜 프록시 클래스를 만들어서 주입해두고, 기능을 실제로 호출할 때(실제 요청이 올 때) 진짜 클래스를 찾아서 동작한다. 가짜 프록시 객체는 원본 클래스를 상속받아 만들어졌기 때문에 객체를 사용하는 클라이언트 입장에서는 원본인지 아닌지도 모르게 동일하게 사용할 수 있다.(다형성)

image

결과적으로 Provider와 프록시 모두 진짜 객체 조회를 꼭 필요한 시점까지 지연처리한다는 것이 중요하고 특별한 스코프는 꼭 필요한 곳에서만 최소화하여 사용하는 것이 유지보수에 좋다!

This post is licensed under CC BY 4.0 by the author.