Spring Core/02. Spring Container와 DI

06. 빈의 스코프

  • -
반응형

이번 포스트에서는 빈의 스코프에 대해 살펴보자.

빈의 스코프(Scope)

 

축 개업!

새롭게 하나의 CoffeeShop을 개업한다고 생각해보자. CoffeeShop에는 Barista가 근무할텐데 이 Barista는 손이 엄청 빠르기 때문에 아무리 많은 주문이 들어와도 순식간에 처리할 수 있다. 즉 맛있게 커피를 만들 수 있는 단 한 명만 필요하다. 그리고 CoffeeShop은 인기가 많아서 엄청나게 많은 Coffee가 팔리고 있다.

이런 CoffeeShop 하나 운영하고 싶네요.

일단 이 일과 관련된 3개의 클래스를 작성해보자.

@Data
public class Coffee {
    private final String client;
    private final String name;
}
public class Barista {
    public Coffee makeCoffee(String client, String name) {
        return new Coffee(client, name);
    }
}
@Data
public class CoffeeShop {
    private final Barista barista; // Coffeeshop의 의존성

    public Coffee orderCoffee(String client, String name) {
        return barista.makeCoffee(client, name);
    }
}

 

@Scope

빈의 스코프란 스프링 컨테이너가 빈을 어떻게 생성하고 어떤 생명주기를 가지며 어떻게 접근할지를 결정하는 방식이다. 스코프에 따라 빈 객체가 어떤 범위까지 존재할지가 결정된다.

Spring에서 스코프를 지정하기 위해서는 @Scope를 사용한다.

 

@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Scope {
  @AliasFor("value")
  String scopeName() default "";
}

스프링 빈의 스코프는 singleton, prototype, request, session, application으로 나뉜다. 각각의 스코프는 어떤 특색이 있는지 살펴보자.

 

singleton

스프링의 빈은 기본적으로 스프링 프레임워크가 비지니스 로직을 재사용 하기위해 관리하는 객체들이다. 이런 빈들은 개별적으로 구분될 필요가 없기 때문에 내부적으로 singleton으로 관리된다. 즉 하나만 만들어서 재사용한다. 이때 사용되는 스코프가 singleton 으로 빈의 기본 스코프이다.

CoffeeShop에서 CoffeeShop이나 Barista는 커피를 판매하거나 커피를 만드는 비지니스 로직만 가지고 있으므로 singleton 타입의 빈으로 적절하다. 실제로 우리는 Barista의 이름이나 나이보다는 얼마나 Coffee를 잘 만드는지, 즉 비지니스 로직에만 관심이 많다.

singleton 스코프의 빈을 멀티스레드 환경에서 사용할 때는 주의가 필요하다. 왜냐하면 이 스코프의 빈은 하나의 객체만 생성되기 때문에 여러 스레드가 동시에 이 객체를 공유하면 데이터 충돌이 발생할 수 있다. 이런 문제를 방지하기 위해 singleton 스코프의 빈들은 무상태(stateless)로 설계하는 것이 바람직하다.

 

prototype

singleton 스코프과 달리 prototype 스코프의 빈은 매번 새로운 객체를 생성한다. 즉 재사용이 목적이 아닌 각각 독립적인 상태를 유지해야 하는 경우에 적합하다. 예를 들어 A 손님의 아메리카노와 B 손님의 아메리카노는 서로 다른 Coffee이므로 각각 독립적으로 생성되어야 하며 재사용 될 수 없다. (음식물 재사용은 안됩니다.!)

그렇다면 왜 이런 객체들을 빈으로 관리해야 할까? 이유는 스프링의 기능을 활용하기 위해서이다. 빈으로 등록되면, 다른 빈을 주입받거나 보안, 로깅 등의 공통 기능을 AOP를 이용해서 적용할 수 있다.

따라서 그럴 필요가 없다면 굳이 재사용되지 않는 객체를 빈으로 만들 필요는 없다. 1회성 객체를 container가 관리하기에는 매번 새로운 객체를 만드는 비용이나 메모리 사용량 증가 등으로 성능상 문제가 될 수도 있다.

 

scope의 지정

빈의 스코프를 지정하기 위해서는 묵시적 방식 또는 명시적 방식 모두 @Scope를 사용한다. 

// 묵시적 빈 등록 시 scope 지정 : 클래스 레벨에 선언
@Component                    
@Scope("singleton")           // 빈의 기본 scope로 생략 가능
public class CoffeeShop {
  ...
}

// 명시적 빈 등록 시 scope 지정 : 메서드 레벨에 선언
@Bean(name = "mylPhone") 
@Profile("dev")
@Scope("prototype")
public LPhone lPhone() {
  return new LPhone();
}

앞서 만들었던 클래스를 빈으로 관리해보자. 앞서 설명했던대로 CoffeeShop과 Barista는 singleton 스코프, Coffee는 prototype 스코프를 사용한다.

Coffee가 빈이 되었기 때문에 객체 생성시 주의가 필요하다. 스프링에서는 빈을 스프링 컨테이너가 관리해야 한다. 따라서 현재 Coffee를 new 해서 사용하고 있는 Barista는 수정되어야 한다. Barista가 Coffee를 스프링 컨테이너 즉 ApplicationContext에게 요청하도록 코드를 수정해보자.

@Component
@RequiredArgsConstructor
public class Barista {
  private final ApplicationContext ctx;  

  public Coffee makeCoffee(String name) {
    return ctx.getBean(Coffee.class, name);  
  }
}

이제 Barista는 singleton scope이고 Coffee는 prototype scope이다. 단위테스트를 통해 빈 객체의 동일성을 확인해보자.

 

request, session, application

request, session, application 스코프는 웹 환경에서 사용되는 스코프로 각각 request, session, application이 유지될 동안 존재하는 빈을 만들어야 할 때 사용된다. 

스코프 생성과 소멸 용도
request HTTP 요청이 들어올 때 생성하고 요청이 끝나면 소멸 각 요청마다 독립적인 빈이 필요한 경우
session HTTP 세션이 시작될 때 생성하고 세션이 종료되면 소멸 사용자(session)별로 독립적인 빈이 필요한 경우
application 웹 애플리케이션이 시작될 때 생성하고 애플리케이션이 종료될 때 소멸 애플리케이션 전체에서 공유해야하는 빈이 필요한 경우

대부분 각 영역에서 유지해야 할 상태를 유지하기 위해 사용되는 빈들로 사용하는 이유는 prototype가 유사하다.

반응형

'Spring Core > 02. Spring Container와 DI' 카테고리의 다른 글

07. 빈의 생명주기  (0) 2024.02.23
05. Profile  (0) 2020.06.17
04. DI 처리 - 묵시적 DI  (0) 2020.06.17
03. DI 처리 - 명시적 DI  (0) 2020.06.16
02. Dependency와 Dependency Injection  (0) 2020.06.15
Contents

포스팅 주소를 복사했습니다

이 글이 도움이 되었다면 공감 부탁드립니다.