이 글은 “토비의 스프링” 책 내용을 정리한 글입니다.

만약 저작권 관련 문제가 있다면 “gunjuko92@gmail.com”로 메일을 보내주시면, 바로 삭제하도록 하겠습니다.

토비의 스프링 - 프로토타입과 스코프

  • 싱글톤이 아닌 빈은 크게 두가지로 나눌 수 있다.
    • 프로토타입 빈
    • 스코프 빈
    • 싱글통과 프로토타입도 스코드의 한 종류이지만, 두 가지는 나머지 스코프와 성격이 크게 다르게 때문에 따로 구분해서 생각하는 것이 좋다.
  • 스코프 : 빈의 스코프는 빈 오브젝트가 만들어져 존재할 수 있는 범위다.

1. 프로토타입 스코프

  • 싱글톤으로 설정된 빈은 DI, DL시 매번 같은 오브젝트가 사용된다.
  • 프로토타입 스코프는 컨테이너에게 빈을 요청할 때마다 매번 새로운 오브젝트를 생성해준다.
    • getBean() 메소드뿐만 아니라 @Autowired와 같은 DI 선언도 각각 독립적인 빈 요청이므로 매번 새로운 오브젝트가 만들어져서 주입된다.
  • IoC 기본 원칙을 따르지 않는다. 프로토타입 스코프를 갖는 빈은 초기화와 DI까지는 해주지만 빈을 제공하고 나면 컨테이너는 더 이상 빈 오브젝트를 관리하지 않는다. DL이나 DI를 통해 빈 오브젝트를 주입받은 다른 빈이 컨테이너가 제공한 빈 오브젝트를 관리하게 된다.
    • 빈이 제거되기 전에 빈이 사용한 리소스를 정리하기 위해 호출하는 메소드를 이용할 수 없다.
    • 프로토타입 빈은 이 빈을 주입받은 오브젝트나 코드에 종속적이다.
  • 프로토타입 빈을 주입받은 빈이 싱글톤이라면, 이 빈에 주입된 프로토타입 빈도 역시 싱글톤 생명주기를 따른다.

용도

  • 코드에서 new로 오브젝트를 생성하는 것을 대신하기 위해 사용
  • 대부분의 경우 new로 오브젝트를 생성하거나 팩토리를 이용해서 오브젝트를 생성하면 된다. 하지만 컨테이너의 DI 기능을 사용해야 하고 싶은 경우는 프로토타입 스코프 빈을 사용한다.
  • 매번 새로운 오브젝트가 필요하면서 DI를 통해 다른 빈을 사용해야할 경우 프로토타입 빈이 적절한 선택이다.

new 키워드를 대신하기 위해 사용되는 것이 프로토타입의 용도라고 본다면, DI는 프로토타입 빈을 사용하기에 적합한 방법이 아니다. DI가 아닌 DL 방식으로 사용해야한다.

프로토타입 빈의 DL전략

스프링은 DL 방식을 코드에서 사용해야 할 경우를 위해 ApplicationContext를 이용하는 것 외에도 다양한 방법을 제공한다.

  • ApplicationContext, BeanFactory : getBean() 메소드를 호출해서 빈을 가져오는 방법. 간단하지만 스프링 API가 등장한다는 단점이 있음
  • ObjectFactory, ObjectFactoryCreatingFactoryBean : ApplicationContext를 ID 받아서 getBean()을 호출해 원하는 프로토타입 빈을 가져오는 방식으로 동작하는 팩토리를 하나 만들어서 빈으로 등록해두고, 이 팩토리를 Di 받아서 필요할 때 getObject()와 같은 메소드를 호출
    • 직접 인터페이스를 정의하고, 해당 인터페이스의 구현체를 구현하는 방법
    • 스프링이 제공하는 ObjectFactory 인터페이스와 ObjectFactory 인터페이스를 구현한 ObjectFactoryCreatingFactoryBean를 사용하는 방법. ObjectFactoryCreatingFactoryBean는 FactoryBean이기 때문에 실제 빈의 오브젝트는 ObjectFactory 타입이 된다.
    • ObjectFactory는 DL을 이용해 빈을 가져와야 하는 모든 경우에 적용할 수 있다.
  • ServiceLocationFactoryBean : ObjectFactory처럼 스프링이 미리 정의해둔 인터페이스를 사용하지 않아도된다. 대신 직접 정의한 팩토리 인터페이스를 이용해서 ObjectFactory 처럼 사용할 수 있다.
  • 메소드 주입 : 메소드를 통해 DI 받는게 아니라, 메소드 코드 자체를 주입하는 것을 말한다. 메소드 주입은 일정한 규칙을 따르는 추상 메소드를 작성해두면 ApplicationContext와 getBean() 메소드를 사용해서 새로운 프로토타입 빈을 가져오는 기능을 담당하는 메소드를 런타임시에 추가해주는 기술이다.
  • Provider<T> : JSR-330에 추가된 표준 인터페이스이다. ObjectFactory와 거의 유사하다. 하지만 ObjectFactoryCreatingFactoryBean을 이용해 빈을 등록해주지 않아도 되기 때문에 사용이 편리하다.

위의 내용은 프로토타입 외에도 Dl 방식의 코드 내에서 필요한 경우라면 언제든 사용 가능하다.

각 방법에 대한 자세한 설명은 책을 참고하길 바란다. 책에서는 ApplicationContext를 직접 사용하는 것은 피하고 Provider가 가장 깔끔한 선택이라고 한다.

2. 스코프

스프링은 싱글톤, 프로토타입 외에 요청, 세션, 글로벌세션, 애플리케이션이라는 4가지 스코프를 기본적으로 제공한다. 이 스코프는 웹 환경에서만 의미가 있다. 4가지 스코프 중에서 application을 제외한 나머지 세가지 스코프는 싱글톤과 다르게 독립적인 상태를 저장해두고 사용하는데 필요하다. 상태를 저장해둘 수 있는 이유는 사용자마다 빈이 만들어지는 덕분이다.

요청 스코프

  • 하나의 웹 요청 안에서 만들어지고 해당 요청이 끝날때 제거된다.
  • 각 요청별로 독립적인 빈이 만들어지기 때문에 빈 오브젝트에 상태값을 저장해도 된다.
  • 요청 스코프 빈의 주요 용도는 애플리케이션 코드에서 생성한 정보를 프레임워크 레벨의 서비스나 인터셉터 등에 전달하는 것이다.

세션 스코프

  • HTTP 세션과 같은 존재 범위를 갖는 빈으로 만들어주는 스코프다.
  • HTTP 세션은 사용자별로 만들어지고 브라우저를 닫거나 세션 타임이 종료될 때까지 유지되기 때문에 로그인 정보나 사용자별 선택옵션 등을 저장해두기에 유용하다. 하지만 세션을 직접 이용하는 건 매우 번거롭고, 웹 환경 정보에 접근할 수 있는 계층에서만 가능한 작업이다.
  • 세션 스코프를 이용하면 HTTP 세션에 저장되는 정보를 모든 계층에서 안전하게 이용할 수 있다.

애플리케이션 스코프

  • 싱글톤 스코프와 비슷한 존재 범위를 갖는다.
  • 웹 애플리케이션과 애플리케이션 컨텍스트의 존재 범위가 다른 경우가 있는 경우 사용한다.

스코프 빈의 사용 방법

  • 하나 이상의 오브젝트가 만들어져야 하기 때문에 싱글톤에 DI 해주는 방법으론 사용할 수 없다.
  • 스코프 빈은 프로토타입 빈과 마찬가지로 Provider나 ObjectFactoryt 같은 DL 방식으로 사용해야 한다.
  • 스코프 빈은 싱글톤에서 일반적인 방법으로 DI 하는것은 불가능하다. 대신 직접 스코프 빈을 DI하지 않고 스코프 빈에 대한 프록시를 DI해주는 방식이 있다. 그러면 마치 평범한 싱글톤 빈을 이용하듯이 스코프 빈을 쓸 수 있다.
    • @Scope 애노테이션의 proxyMode 엘리먼트를 사용해서 프록시 모드를 결정한다.
  • 클라이언트는 스코프 프록시 오브젝트를 실제 스코프 빈처럼 사용하면 프록시에서 현재 스코프에 맞는 실제 빈 오브젝트로 작업을 위임해준다.
  • 스코프 프록시는 각 요청에 연결된 세션 정보를 참고해서 사용자마다 다른 오브젝트를 사용하게 해준다.
  • 스코프 프록시는 실제 스코프 오브젝트를 상속하고 있다.
  • 프록시 방식의 DI를 적용하면 스코프 빈이지만 마치 싱글톤 빈을 사용하듯이 편하게 쓸 수 있다는 장점이 있다. 반면에 주입되는 빈의 스코프를 모르면 코드를 이해하기가 어려울 수도 있다.

커스텀 스코프와 상태를 저장하는 빈 사용하기

  • 스프링이 기본적으로 제공하는 스코프 외에도 임의의 스코프를 만들어 사용할 수 있다.
  • 싱글통 외에 스코프를 사용한다는 건 기본적으로 빈에 상태를 저장해두고 사용한다는 의미다.
  • 스프링에서는 Scope 인터페이스를 구현해서 새로운 스코프를 작성할 수 있다.
  • 빈에 상태를 저장해두는 방식을 선호하지 않는다면 상태 정보를 URL 파라미터, 쿠키, 폼 히든 필드, DB, HTTP 세션등에 분산해 저장해두고 코드로 관리하면 된다.