들어가며

  • 이번 포스트부터는 빈 스코프에 대해 알아보고 여러가지 스코프에 대해서 알아보도록 한다.

 

 

빈 스코프의 개념과 스코프의 종류

빈 스코프의 개념

  • 지금까지 우리는 스프링 컨테이너가 스프링 빈을 싱글톤으로 관리하는 것만 파악하였고, 자동으로 DI와 빈을 탐색하는 컴포넌트 스캔을 수행하는 것 까지 배웠다.
  • 하지만 빈 자체의 생명 주기 즉 어느 시점에서 생성되어서 어느 시점에 소멸되는지에 대해서는 이전 포스트였던 빈 생명관리에서 처음 알아보았을 것이다.
  • 지금까지 우리가 실험해보고 확인했던 모든 스프링 빈은 스프링 컨테이너가 올라갈 때 부터 생성되기 시작해 스프링 컨테이너가 종료되면 같이 소멸되는 스프링 컨테이너와 유지되는 시간이 동일한 스프링 빈만 따져보았다.
  • 이는 스프링 컨테이너에서 스프링 빈을 생성시킬때 디폴트로 싱글톤 스코프 상태로 생성시키기 때문이다.
  • 이렇게 스프링 빈이 존재 가능한 범위(또는 시간)을 빈 스코프(Bean Scope)라고 한다.

빈 스코프의 종류

  • 스프링에서는 빈 스코프를 바로 위에서 보았던 싱글톤 스코프 이외에도 여러 스코프를 지원한다. 지원하는 스코프들은 다음과 같다.
    • 싱글톤 스코프: 가장 기본적인 스코프로 스프링 컨테이너의 시작부터 끝까지 유지되는 가장 넓은 스코프이다.
    • 프로토타입 스코프: 해당 스코프에 해당하는 스프링 빈은 스프링 컨테이너가 빈의 생성과 의존관계 주입까지만 관여하고 더 이상 관여하지 않는 매우 짧은 범위의 스코프이다.
    • 웹 관련 스코프
      • request : 웹 요청(request)가 들어오고 나갈때 까지 유지되는 스코프이다. 즉 해당 스코프에 있는 스프링 빈은 웹 요청이 들어올 때 생성되며 처리가 끝나고 나갈때 삭제된다.
      • session : 웹 세션(session)이 생성되고 종료될 때 까지 유지되는 스코프이다.
      • application : 웹의 서블릿 컨텍스트와 같은 범위로 유지되는 스코프이다.

빈 스코프의 설정

  • 빈 스코프 설정은 @Scope 어노테이션을 이용하여 설정할 수 있다. 해당 어노테이션이 없거나, 아무것도 설정하지 않을 경우에 자동으로 싱글톤 스코프로 관리된다.
// 컴포넌트 스캔을 사용한 빈에서 스코프 설정

@Scope("prototype")
@Component
public class AutoManagementBean{

}

// 수동으로 빈을 등록할 때의 스코프 설정

@Configuration
public class BeanConfig{
    @Scope("prototype")
    @Bean
    public ManualBean manualBean(){
        return new ManualBean();
    }
}

 

 

 

프로토타입 스코프(Prototype Scope)

  • 예전 기억을 떠올려 보자. 스프링 컨테이너는 생성한 빈을 싱글톤으로 관리한다고 하였다. 즉 여러 클라이언트에서 해당 스프링 빈이 필요하다고 한다면 스프링 컨테이너는 모두 동일한 스프링 빈을 반환한다.
  • 프로토타입 스코프로 지정된 스프링 빈을 스프링 컨테이너에 조회하도록 하면 스프링 컨테이너는 항상 새로운 인스턴스를 생성하여 반환한다. 즉 매번 다른 객체를 반환한다.
    • 위에서 설명했던 스프링 컨테이너는 의존관계 주입과 빈의 생성만 담당하고 나머지는 관리하지 않는다는 것을 생각하자. 만약 프로토타입 스코프에 있는 빈을 요청하여 받았다면 해당 빈의 관리는 이제 요청한 메서드 또는 객체에서 맡아야 한다.
    • 이는 이전에 빈의 생명주기에서 보았던 @PostConstruct, @PreDestory로 확인해볼 수 있는데 스프링 컨테이너에서 빈 생성과 의존관계 주입이 끝나면 관리하지 않기 때문에 컨테이너가 종료되기 전에 해당 빈에 대해 콜백을 해주는 @PreDestory가 동작하지 않는다.
    • 따라서 해당 스프링 빈의 관리는 온전히 클라이언트가 담당해야 한다. 이는 객체의 소멸에 대해서도 클라이언트가 직접 담당해야 한다는 것을 의미한다

 

 

 

싱글톤 빈과 프로토타입 빈의 동시 사용 - 문제

  • 하지만 프토토타입 빈의 동작을 이용하여 어떠한 로직을 처리하려고 할 때 싱글톤 빈과 함께 사용하게 되면 문제가 발생할 수 있다.
  • 다음의 두 상황을 보도록 하자.

스프링 컨테이너에 프로토타입 빈 직접 요청

  • 클라이언트가 직접 스프링 컨테이너에 프로토타입 빈을 요청하는 상황을 가정해보자.
  • 스프링 컨테이너는 각 클라이언트에게 새로운 프로토타입 빈 인스턴스를 만들어 반환한다. 각 클라이언트들은 이 인스턴스들에 있는 addCount를 수행하여 Count 변수를 1 올리는 동작을 수행한다.
  • 각 인스턴스들이 별개의 인스턴스이기 때문에 addCount()를 통해 추가된 각 Count의 값은 0x01, 0x02 모두 1이다.

싱글톤 빈에 프로토타입 빈을 주입하여 사용해야 할 때

  • 싱글톤 빈에 프로토타입 빈을 주입하여 사용해야 하는 경우에는 이야기가 조금 달라진다.

  • ClientBean은 싱글톤 스코프에 속한 빈이다. 따라서 스프링 컨테이너가 생성될 때 같이 생성되며 종료될 때 같이 소멸된다.
  • 따라서 스프링 컨테이너가 생성되면 ClientBean이 생성되면서 프로토타입 빈을 요청하게 된다. 컨테이너는 프로토타입 빈을 새로 만든 뒤에 받은 인스턴스를 클라이언트 빈에 반환한다.
  • 따라서 ClientBean은 PrototypeBean을 내부에 주입받은 체로 존재하게 된다.

  • 이때 클라이언트가 ClientBean을 요청한 뒤 ClientBean 내부에 있는 PrototypeBean의 addCount()을 호출했다고 가정하자.
  • ClientBean 객체가 반환된 뒤 ClientBean 객체가 가지고 있는 PrototypeBean 0x01의 addCount()가 호출되어 Count가 1 늘어난다.

  • 그런데 이후에 다른 클라이언트가 ClientBean을 호출하여 PrototypeBean의 addCount를 호출했다고 생각해보자.
  • 우리가 원하는 동작은 ClientBean이 새로운 PrototypeBean 0x02를 주입받은 뒤에 addCount를 통해 PrototypeBean 0x02의 count를 1로 바꾸는 것이다.
  • 하지만 ClientBean이 싱글톤으로 관리되고 있는것이 문제다. ClientBean은 새로 생성되지 않고, 하나의 인스턴스만 존재하고 있기 때문에 PrototypeBean 0x01을 가지고 있는 ClientBean 인스턴스가 반환되게 된다.
  • 따라서 다른 클라이언트에서 addCount를 수행할 경우 PrototypeBean 0x01의 count가 1올라 2로 변경되게 된다.
  • 이것은 우리가 원하는 동작이 아니다. 우리는 ClientBean 인스턴스를 반환받되 그 내부에 있는 프로토타입 빈은 항상 새로운 인스턴스가 주입되어 반환되는 것을 원한다.
  • 다음 포스트에서 이런 문제를 해결하는 방법에 대해 알아볼 것이다.
복사했습니다!