개발/Spring

Spring @Scope 어노테이션과 리소스 관리

피터JK 2025. 2. 11. 16:11
728x90

Spring에서 @Scope 어노테이션을 사용하면 빈(Bean)의 생성 및 라이프사이클을 관리할 수 있습니다. 스코프를 적절히 설정하면 애플리케이션의 성능과 리소스 활용을 최적화할 수 있습니다.

1. @Scope 어노테이션이란?

@Scope 어노테이션은 Spring 빈의 스코프(생명주기)를 지정하는 데 사용됩니다. 기본적으로 모든 빈은 싱글톤(Singleton) 스코프를 가지지만, 특정 요구사항에 따라 다른 스코프를 적용할 수 있습니다.

1.1 @Scope 기본 사용법

@Component
@Scope("singleton")  // 기본값 (생략 가능)
public class SingletonBean {
    public SingletonBean() {
        System.out.println("SingletonBean 인스턴스 생성됨");
    }
}

위 코드에서 @Scope("singleton")은 해당 빈이 애플리케이션 전체에서 하나의 인스턴스만 유지됨을 의미합니다.

2. 싱글톤(Singleton) vs 프로토타입(Prototype) 스코프

2.1 싱글톤 스코프

특징:

  • Spring 컨테이너가 단 한 개의 인스턴스만 생성하고 이를 모든 요청에서 공유합니다.
  • 애플리케이션이 종료될 때까지 유지됩니다.
  • 메모리를 효율적으로 사용할 수 있지만, 상태를 가지면 동시성 문제가 발생할 수 있습니다.
@Component
@Scope("singleton")
public class SingletonBean {
    public SingletonBean() {
        System.out.println("SingletonBean 인스턴스 생성됨");
    }
}

사용 코드

@Autowired
private SingletonBean singletonBean1;

@Autowired
private SingletonBean singletonBean2;

public void checkSingleton() {
    System.out.println(singletonBean1 == singletonBean2); // true
}

2.2 프로토타입 스코프

특징:

  • 매번 빈을 요청할 때마다 새로운 인스턴스가 생성됩니다.
  • Spring 컨테이너가 라이프사이클을 관리하지 않으며, 수동으로 소멸 처리가 필요합니다.
  • 메모리 사용량이 증가할 가능성이 있습니다.
@Component
@Scope("prototype")
public class PrototypeBean {
    public PrototypeBean() {
        System.out.println("PrototypeBean 인스턴스 생성됨");
    }
}

사용 코드

@Autowired
private PrototypeBean prototypeBean1;

@Autowired
private PrototypeBean prototypeBean2;

public void checkPrototype() {
    System.out.println(prototypeBean1 == prototypeBean2); // false
}

3. 싱글톤과 프로토타입 관계에서 발생하는 문제

3.1 싱글톤 빈이 프로토타입 빈을 포함할 때

싱글톤 빈이 프로토타입 빈을 의존성 주입받으면 프로토타입 빈이 싱글톤 빈이 생성될 때 한 번만 주입되고, 이후에는 같은 인스턴스를 계속 사용하는 문제가 발생합니다.

❌ 잘못된 예제 (항상 같은 프로토타입 빈 사용됨)

@Component
@Scope("singleton")
public class SingletonService {
    private final PrototypeBean prototypeBean;

    @Autowired
    public SingletonService(PrototypeBean prototypeBean) {
        this.prototypeBean = prototypeBean;
    }

    public void usePrototypeBean() {
        System.out.println("PrototypeBean 사용됨: " + prototypeBean);
    }
}

해결 방법

3.2 해결 방법 1: ObjectProvider 사용

Spring의 ObjectProvider를 사용하면 매번 새로운 프로토타입 빈을 가져올 수 있습니다.

@Component
public class SingletonService {
    private final ObjectProvider<PrototypeBean> prototypeBeanProvider;

    @Autowired
    public SingletonService(ObjectProvider<PrototypeBean> prototypeBeanProvider) {
        this.prototypeBeanProvider = prototypeBeanProvider;
    }

    public void usePrototypeBean() {
        PrototypeBean prototypeBean = prototypeBeanProvider.getObject();
        System.out.println("PrototypeBean 사용됨: " + prototypeBean);
    }
}

3.3 해결 방법 2: @Lookup 사용

@Lookup을 사용하면 새로운 프로토타입 빈을 동적으로 주입받을 수 있습니다.

@Component
public abstract class SingletonService {
    public void usePrototypeBean() {
        PrototypeBean prototypeBean = getPrototypeBean();
        System.out.println("PrototypeBean 사용됨: " + prototypeBean);
    }

    @Lookup
    protected abstract PrototypeBean getPrototypeBean();
}

4. 스코프별 리소스 관리 비교

스코프 유형 객체 생성 시점 라이프사이클 관리 리소스 관리 특징

singleton 애플리케이션 시작 시 1회 Spring 컨테이너가 직접 관리 메모리 효율적, 공유 가능, GC 부담 적음
prototype 빈이 요청될 때마다 개발자가 직접 관리 매번 새로운 객체 생성, GC 대상

5. 결론

  • 싱글톤 빈은 애플리케이션 전반에서 공유할 리소스에 적합 (예: 서비스 계층, 캐시)
  • 프로토타입 빈은 매번 새로운 객체를 생성해야 하는 경우 적합 (예: 트랜잭션 처리, 사용자 요청 기반 객체 생성)
  • 싱글톤 내에서 프로토타입 빈을 적절히 사용하려면 ObjectProvider 또는 @Lookup을 활용

적절한 스코프 선택을 통해 성능을 최적화하고, 메모리 효율성을 고려한 애플리케이션을 설계하세요! 🚀

728x90