스프링 빈 스코프(Bean Scope)

한눈에 보기

빈 스코프는 스프링 컨테이너가 빈을 언제 생성하고 어디까지 공유할지 정하는 규칙이다. 싱글턴과 프로토타입의 동작 차이, 웹 스코프, 스코프 혼용 시 문제와 ObjectProvider, 프록시 해결법을 정리한다.

최종 업데이트 2026.09.17

스프링 빈 스코프(Bean Scope)는 스프링 컨테이너가 빈(Bean) 인스턴스를 언제 생성하고 얼마나 오래 유지하며 어느 범위까지 공유할지를 정하는 규칙이다. 기본값인 싱글턴(Singleton) 스코프는 컨테이너당 인스턴스 하나를 모든 곳에서 공유하고, 프로토타입(Prototype) 스코프는 요청할 때마다 새 인스턴스를 만들며, 웹 환경에서는 request와 session 같은 웹 스코프를 추가로 쓸 수 있다.

스코프가 결정하는 것

스프링 컨테이너는 빈 정의(Bean Definition)를 읽어 객체를 생성하고 의존성을 주입하며 생명주기를 관리한다. 이때 스코프는 두 가지를 결정한다. 첫째, 같은 빈을 여러 곳에서 주입받을 때 같은 인스턴스를 줄 것인지 새 인스턴스를 줄 것인지다. 둘째, 컨테이너가 그 빈의 소멸까지 책임질 것인지다. 스코프는 @Scope 애너테이션(Annotation)으로 지정하며, 지정하지 않으면 싱글턴이다.

스코프 종류

스코프인스턴스 범위비고
singleton스프링 컨테이너당 1개기본값, 컨테이너가 소멸까지 관리
prototype요청할 때마다 새로 생성생성과 주입까지만 관여, 소멸 콜백 미호출
requestHTTP 요청당 1개웹 환경 전용
sessionHTTP 세션당 1개웹 환경 전용
application서블릿 컨텍스트당 1개웹 환경 전용
websocket웹소켓 세션당 1개웹소켓 환경 전용

싱글턴 스코프의 내부 동작

싱글턴 빈은 컨테이너가 refresh되는 시점에 미리 생성되어 내부 싱글턴 저장소에 이름과 함께 보관된다. 이후 어디에서 주입받든 같은 인스턴스를 돌려받는다. 디자인 패턴의 싱글턴 패턴과 달리 클래스 자체에 private 생성자나 static 인스턴스를 두지 않고, 컨테이너가 하나만 만들어 공유한다는 점이 다르다. 이를 싱글턴 레지스트리라고 부르며, 덕분에 테스트에서 인스턴스를 자유롭게 만들 수 있다.

싱글턴 빈은 여러 스레드가 동시에 사용하므로 반드시 무상태(Stateless)로 설계해야 한다. 요청마다 달라지는 값을 필드에 보관하면 스레드 간에 값이 섞이는 동시성 문제가 생긴다. 상태가 필요하면 지역 변수, 메서드 파라미터, 반환 값으로 다뤄야 한다.

@Component
public class OrderService {

    // 위험: 싱글턴 빈의 공유 필드에 요청별 상태를 저장하면 안 된다
    // private int lastOrderAmount;

    public int order(int amount) {
        int fee = calculateFee(amount); // 지역 변수로 처리한다
        return amount + fee;
    }

    private int calculateFee(int amount) {
        return amount / 100;
    }
}

프로토타입 스코프와 생명주기 콜백

프로토타입 빈은 컨테이너에 요청하는 순간 생성되고, 의존성 주입과 @PostConstruct 초기화까지 마친 뒤 클라이언트에게 넘겨진다. 그 이후 컨테이너는 이 인스턴스를 더 이상 추적하지 않는다. 따라서 @PreDestroy 같은 소멸 콜백이 호출되지 않으며, 자원 해제가 필요하면 사용하는 쪽이 직접 정리해야 한다.

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

    @PostConstruct
    public void init() {
        // 요청마다 새 인스턴스에서 호출된다
    }

    @PreDestroy
    public void close() {
        // 프로토타입이므로 컨테이너가 호출해 주지 않는다
    }
}

스코프가 다른 빈을 섞을 때 생기는 문제

가장 자주 나오는 함정은 싱글턴 빈이 프로토타입 빈을 주입받는 경우다. 주입은 싱글턴이 생성되는 시점에 한 번만 일어나므로, 프로토타입 빈도 그 순간 만들어진 인스턴스 하나가 계속 재사용된다. 매번 새 인스턴스를 원한 의도와 어긋나는 것이다. 해결책은 사용 시점에 컨테이너에 조회를 위임하는 것이며, ObjectProvider가 대표적이다.

@Component
public class ReportService {

    private final ObjectProvider<ReportGenerator> generatorProvider;

    public ReportService(ObjectProvider<ReportGenerator> generatorProvider) {
        this.generatorProvider = generatorProvider;
    }

    public void generate() {
        ReportGenerator generator = generatorProvider.getObject(); // 호출 시점마다 새 인스턴스
        // ...
    }
}

request 같은 웹 스코프 빈을 싱글턴에 주입할 때는 프록시(Proxy) 방식을 쓴다. @Scope(value = "request", proxyMode = ScopedProxyMode.TARGET_CLASS)로 선언하면 CGLIB 프록시가 대신 주입되고, 실제 메서드 호출 시점에 현재 요청에 해당하는 진짜 빈을 찾아 위임한다. 요청이 없는 시점에 웹 스코프 빈을 직접 주입하려 하면 컨텍스트 기동 자체가 실패하므로, 프록시나 ObjectProvider로 조회를 지연시키는 것이 핵심이다.

면접에서 이렇게 물어본다

  • 스프링 빈의 기본 스코프는 무엇이고, 싱글턴 빈을 무상태로 설계해야 하는 이유를 설명해 보라.
  • 스프링의 싱글턴 레지스트리와 디자인 패턴의 싱글턴 패턴은 무엇이 다른지 말해 보라.
  • 프로토타입 빈은 왜 @PreDestroy가 호출되지 않는지 컨테이너의 관리 범위와 연결해 설명해 보라.
  • 싱글턴 빈에 프로토타입 빈을 주입하면 어떤 문제가 생기고 어떻게 해결하는지 말해 보라.
  • request 스코프 빈을 싱글턴 빈에서 사용할 때 프록시 모드가 필요한 이유를 설명해 보라.
Spring / JPA 문서 전체 보기

이 개념, 내 코드에서는 어떻게 물어볼까요

데브핏(DevFit)에 채용공고 URL을 붙여 넣고 레포지토리를 연결하면, 내가 쓴 커밋에서 예상 질문과 어필 포인트를 만듭니다. 하루 10회까지 무료입니다. 합불 판정이나 지원자 랭킹은 만들지 않고, 최종 판단은 사람이 합니다.

공고 넣고 시작하기