Spring: Part 4. Bean 생명주기(Lifecycle)

2026. 7. 29. 15:02·🍃SpringBoot

 Part 4. Bean 생명주기(Lifecycle)

스프링(Spring) 프레임워크는 Bean을 생성하고 의존성을 주입하여 관리합니다. 그렇다면 Bean은 생성된 이후부터 애플리케이션이 종료될 때까지 어떤 과정을 거칠까요? 스프링은 단지 객체를 생성하는 데서 끝나지 않고 생성, 초기화, 의존성 연결, 소멸까지의 전체 과정을 자동으로 안전하게 관리합니다. 이 과정을 'Bean 생명주기(Bean Lifecycle)'라고 합니다.

4.1 Bean 생명주기(Bean Lifecycle)란?

스프링 컨테이너가 Bean 객체를 생성한 시점부터 애플리케이션 종료로 인해 소멸될 때까지 관리하는 전체 흐름을 의미합니다.

기본 생명주기 흐름

  1. Bean 생성 (Create)
  2. 의존성 주입 (Dependency Injection)
  3. 초기화 (Initialization)
  4. 애플리케이션에서 사용 (Use)
  5. 소멸 (Destroy)

2. 생명주기 단계별 상세 설명

1. Bean 생성 (Create)

애플리케이션이 시작되면 Component Scan이나 @Bean 설정을 통해 객체를 생성합니다.
이 시점에는 객체만 만들어졌을 뿐, 의존성이 연결된 상태는 아닙니다.

2.의존성 주입 (Dependency Injection)

객체 생성이 완료되면 필요한 의존성을 주입합니다.

  • 진행 순서 예시: MemberRepository 생성 ➔ MemberService 생성 ➔ 의존성 연결

3. 초기화 (Initialization)

의존성 주입이 끝난 후, 외부 시스템 연결이나 데이터 준비 등 준비 작업을 진행합니다.

  • 대표적인 초기화 작업: DB 연결, 메시지 브로커 연결, 캐시 초기화, 외부 API 클라이언트 준비 등
  • 핵심: 초기화 단계까지 완전히 완료되어야 Bean을 정상적으로 사용할 수 있습니다.

4. Bean 사용 (Use)

초기화가 끝난 Bean은 애플리케이션 요청을 처리하는 데 사용됩니다.

  • 호출 흐름: Client ➔ Controller ➔ Service ➔ Repository
  • Singleton Bean의 경우 동일한 객체를 계속 재사용합니다.

5. 소멸 (Destroy)

애플리케이션 종료 시 안전하게 자원을 해제하고 정리를 수행합니다.

  • 주요 소멸 작업: DB Connection 종료, Thread Pool 종료, Socket 종료, Cache Flush 등

3. 객체 생성과 초기화를 분리해야 하는 이유

생성자 내부에서 외부 서버와 통신하거나 무거운 초기화 작업을 진행하는 것은 좋지 않은 설계입니다.

// ❌ 좋지 않은 예시: 생성자와 초기화 작업이 섞인 경우
public MemberService() {
    connectDatabase();
    loadCache();
    connectMessageBroker();
}

분리해야 하는 이유

  • 생성 단계: 객체를 만드는 단순한 작업만 수행합니다.
  • 초기화 단계: 의존성 주입이 완료된 후 안전하게 외부 자원 연결 등의 작업을 수행합니다.

생성과 초기화를 명확히 분리하면 객체 생성이 단순해지고, 테스트 및 유지보수가 매우 쉬워집니다.

4. 실무에서의 Bean 생명주기 활용

대부분의 일반적인 Bean은 별도의 초기화 과정 없이 생성되지만, 외부 자원을 다루는 객체는 생명주기 관리가 필수적입니다.

  • 관리가 중요한 대상: DB Connection Pool, Redis Client, Kafka Producer/Consumer, Scheduler, Thread Pool, 파일 시스템 연결 등
  • 이유: 자원을 생성한 뒤 해제하지 않으면 메모리 누수나 연결 누수가 발생할 수 있고, 초기화되지 않은 상태로 사용하면 오류가 발생할 수 있기 때문입니다.

핵심 질문

Q1. Spring Bean의 생명주기란 무엇인가요?

Spring 컨테이너가 Bean을 생성하고 의존성을 주입한 뒤 초기화를 수행하고, 애플리케이션 종료 시 Bean을 소멸시키는 전체 과정을 의미합니다.

Q2. Bean의 일반적인 생명주기 순서는 어떻게 되나요?

Bean 생성 → 의존성 주입 → 초기화 → 사용 → 소멸 순서로 진행됩니다.

Q3. 객체 생성과 초기화를 분리하는 이유는 무엇인가요?

객체 생성을 단순하게 유지하고, 의존성이 모두 준비된 이후 필요한 초기화 작업을 수행하기 위해서입니다. 이를 통해 객체의 안정성과 테스트 용이성을 높일 수 있습니다.

Q4. 생명주기 관리가 중요한 Bean의 예를 들어보세요.

데이터베이스 연결 객체, Redis Client, Kafka Client, Thread Pool, Scheduler와 같이 외부 자원을 사용하는 Bean은 생성 후 초기화와 종료 시 자원 해제가 필요하므로 생명주기 관리가 중요합니다.

4.2 @PostConstruct와 @PreDestroy를 활용한 생명주기 콜백

이전 글에서 Spring Bean의 생성, 의존성 주입, 초기화, 사용, 소멸로 이어지는 전체 생명주기(Lifecycle)를 알아보았습니다. 그렇다면 초기화 작업과 종료 작업은 실제로 코드의 어느 위치에서 수행해야 할까요? 스프링은 특정 시점에 필요한 작업을 자동으로 수행해 주는 '생명주기 콜백(Lifecycle Callback)'을 제공합니다. 대표적인 방법이 바로 자바 표준 어노테이션인 `@PostConstruct`와 `@PreDestroy`입니다.

1. 생명주기 콜백(Lifecycle Callback)이란?

스프링 컨테이너가 Bean의 생명주기 중 특정 시점에 도달했을 때 개발자가 지정한 메서드를 자동으로 호출해 주는 기능입니다.

전체 실행 흐름

  1. 객체 생성
  2. 의존성 주입 완료
  3. @PostConstruct 호출 (초기화 작업)
  4. Bean 사용 (서비스 수행)
  5. @PreDestroy 호출 (종료 및 자원 해제)
  6. Bean 소멸

2. @PostConstruct (초기화 콜백)

Bean이 생성되고 의존성 주입(DI)이 완전히 완료된 직후 한 번 호출되는 메서드에 붙입니다.

@Component
public class NetworkClient {

    @PostConstruct
    public void init() {
        System.out.println("초기화 작업을 수행합니다.");
    }
}
생성자 대신 @PostConstruct를 사용하는 이유
생성자가 실행되는 시점에는 아직 필요한 의존성이 주입되지 않은 상태입니다. 반면 `@PostConstruct`는 의존성 주입이 끝난 후 실행되므로, 주입된 다른 Bean들을 안전하게 참조하여 초기화 작업을 진행할 수 있습니다.

주요 사용 예시

  • 데이터베이스 및 외부 API 클라이언트 초기화
  • 메시지 브로커(Kafka 등) 연결
  • 캐시 데이터 사전 적재(Cache Loading)
  • 주요 설정 정보 검증

3. @PreDestroy (소멸 콜백)

스프링 컨테이너가 종료되어 Bean이 소멸되기 바로 직전에 한 번 호출되는 메서드에 붙입니다.

@Component
public class NetworkClient {

    @PreDestroy
    public void close() {
        System.out.println("연결을 안전하게 종료합니다.");
    }
}

주요 사용 예시

  • 데이터베이스 Connection 및 Socket 종료
  • Thread Pool 종료
  • 파일/로그 Flush
  • Redis/Kafka 연결 종료

4. 실무 사용 시 주의사항 및 특징

메서드 작성 규칙

  1. 매개변수(Parameter)를 갖지 않아야 합니다.
  2. 반환 타입은 `void`를 사용합니다.
  3. 하나의 Bean 클래스 내에서는 각각 하나의 메서드에만 적용하는 것이 권장됩니다.

Bean Scope(스코프)와의 관계

  • Singleton Bean: 애플리케이션 시작 시 @PostConstruct가 1회, 종료 시 @PreDestroy가 1회 호출됩니다.
  • Prototype Bean: 스프링 컨테이너가 Bean의 생성과 의존성 주입까지만 관리하고 소멸에는 관여하지 않습니다. 따라서 @PreDestroy가 자동으로 호출되지 않으므로 주의해야 합니다.

핵심 질문

Q1. @PostConstruct는 언제 호출되나요?

Bean 생성과 의존성 주입이 모두 완료된 이후, Bean이 사용되기 직전에 한 번 호출됩니다.

Q2. @PreDestroy는 언제 호출되나요?

Spring 컨테이너가 Bean을 소멸시키기 직전에 한 번 호출됩니다.

Q3. 생성자 대신 @PostConstruct를 사용하는 이유는 무엇인가요?

생성자가 실행되는 시점에는 의존성 주입이 완료되지 않았을 수 있습니다. @PostConstruct는 의존성 주입 이후 호출되므로 다른 Bean을 안전하게 사용할 수 있습니다.

Q4. Prototype Bean에서도 @PreDestroy가 자동으로 호출되나요?

아닙니다. Prototype Bean은 생성만 Spring이 관리하며, 소멸은 관리하지 않으므로 @PreDestroy가 자동으로 호출되지 않습니다.

4.3  Spring Bean Scope 개념 및 종류 완벽 정리

Spring Bean은 생성부터 소멸까지 일련의 생명주기(Lifecycle)를 거칩니다. 하지만 모든 Bean이 똑같은 방식으로, 동일한 시점에 생성되어 유지되는 것은 아닙니다. 어떤 Bean은 애플리케이션 시작 시 딱 하나만 생성되어 계속 공유되는 반면, 어떤 Bean은 요청이 올 때마다 새로 생성됩니다. 이처럼 Spring 컨테이너가 Bean을 얼마 동안 유지하고 몇 개나 생성하여 관리할지 결정하는 범위를 'Bean Scope(스프링 빈 스코프)'라고 합니다.

1. Bean Scope가 필요한 이유

"모든 객체를 하나만 만들어 공유하면 편하지 않을까?"라고 생각할 수 있습니다. |
하지만 객체의 성격에 따라 필요한 관리 방식이 다릅니다.

싱글톤 방식이 적합한 객체

  • 종류: Service, Repository, Controller 등
  • 특징: 여러 사용자가 동시에 접근해서 공유하여 사용해도 상태가 변경되지 않는 객체입니다.

개별 관리가 필요한 객체

  • 종류: 로그인 사용자 정보, HTTP 요청 데이터, 장바구니 등
  • 특징: 사용자마다 서로 다른 상태 데이터값을 가져야 합니다.

만약 사용자 정보를 담는 Bean을 하나만 생성해 공유하면 서로 다른 사용자의 데이터가 섞이는 치명적인 문제가 발생합니다. 반대로 모든 Bean을 매번 새로 생성하면 메모리 사용량 증가와 성능 저하가 발생합니다. 따라서 용도에 맞는 Scope 설정이 필수적입니다.

2. Spring이 제공하는 주요 Scope 종류

스프링은 다양한 환경과 목적에 맞춰 다음과 같은 Scope를 제공합니다.

Scope 설명 생성 시점
Singleton 컨테이너당 단 하나의 Bean만 생성하여 공유 (기본값) 애플리케이션 시작 시
Prototype Bean을 요청할 때마다 매번 새로운 객체를 생성 Bean 요청 시
Request HTTP 요청 하나가 들어오고 나갈 때까지 유지되는 Scope HTTP 요청 시
Session HTTP Session과 생명주기를 같이하는 Scope Session 생성 시
Application ServletContext와 생명주기를 같이하는 Scope ServletContext 생성 시
참고: Request, Session, Application Scope는 주로 웹 애플리케이션 환경(Web Scope)에서 사용됩니다.

3. Scope별 생명주기의 차이

Scope에 따라 Bean의 생명주기도 크게 달라집니다.

1) Singleton Scope

  • 흐름: 애플리케이션 시작 ➔ Bean 생성 ➔ 사용 ➔ 애플리케이션 종료 ➔ Bean 소멸
  • 특징: 애플리케이션의 시작부터 종료까지 컨테이너와 생명주기를 함께합니다.

2) Prototype Scope

  • 흐름: Bean 요청 ➔ Bean 생성 및 주입 ➔ 사용 ➔ Spring 관리 종료
  • 특징: 스프링 컨테이너는 Bean을 생성하고 의존성을 주입하여 반환까지만 담당합니다. 이후 소멸 과정 등은 스프링이 관리하지 않습니다.

4. Scope 설정 방법 및 주의사항

Scope 지정 방법

@Scope 어노테이션을 사용하여 지정할 수 있습니다.

// Prototype Scope 지정
@Component
@Scope("prototype")
public class PrototypeBean {
}

// Singleton Scope (기본값이므로 실무에서는 명시적 선언을 생략합니다)
@Component
@Scope("singleton")
public class MemberService {
}

Scope 선택 시 주의할 점

  1. Singleton Bean 내부 상태 저장 금지 (무상태 유지)
    Singleton Bean에 사용자별 상태 데이터를 저장하면, 여러 사용자가 하나의 객체를 공유하므로 데이터 오염 및 동시성 문제가 발생합니다.
  2. Prototype Bean 과도한 사용 자제
    요청마다 객체를 생성하므로 남발할 경우 객체 생성 비용 및 메모리 부담이 커집니다.

핵심 질문

Q1. Bean Scope란 무엇인가요?

Spring 컨테이너가 Bean을 생성하고 유지하며 소멸시키는 범위를 정의하는 정책입니다.

Q2. Spring의 기본 Bean Scope는 무엇인가요?

Singleton Scope이다. 별도의 설정이 없으면 모든 Bean은 Singleton으로 등록됩니다.

Q3. Spring에서 자주 사용하는 Bean Scope에는 어떤 것이 있는가?

Singleton, Prototype, Request, Session, Application Scope가 대표적입니다.

Q4. Scope를 잘못 선택하면 어떤 문제가 발생할 수 있나요?

Singleton에 사용자별 상태를 저장하면 데이터 공유 문제가 발생할 수 있으며, Prototype을 과도하게 사용하면 객체 생성 비용과 메모리 사용량이 증가할 수 있습니다.

4.4 Singleton Scope

앞에서 Bean Scope는 Spring 컨테이너가 Bean을 생성하고 관리하는 범위를 의미한다는 것을 살펴보았습니다. Spring에서 가장 많이 사용하는 Scope는 Singleton Scope입니다. 실제로 Spring Boot 애플리케이션에서 Service, Repository, Controller, Configuration 등 대부분의 Bean은 Singleton으로 관리됩니다. 이번 장에서는 Singleton Scope의 동작 원리와 장점, 그리고 사용할 때 주의해야 할 사항을 알아봅니다.

1. Singleton Scope란?

Singleton Scope는 Spring 컨테이너당 하나의 Bean 인스턴스만 생성하는 Scope입니다.
즉, 동일한 Bean을 여러 번 요청하더라도 항상 같은 객체를 반환합니다.

  • 모든 클라이언트는 동일한 MemberService 객체를 공유하여 사용합니다.
  • Spring에서 별도의 Scope를 지정하지 않으면 모든 Bean은 Singleton Scope로 등록됩니다.

2. Singleton Bean 생성 과정 및 특징

Singleton Bean 생성 과정

애플리케이션이 시작되면 Spring 컨테이너는 Singleton Bean을 생성하고 내부 저장소(Cache)에 보관합니다.

  1. Application Start
  2. Spring Container 생성
  3. Singleton Bean 생성
  4. Bean 저장 (Singleton Cache)
  5. 애플리케이션 실행

이후에는 Bean을 새로 생성하지 않고, 이미 생성된 객체를 재사용하여 반환합니다.

같은 Bean을 여러 번 요청한 경우

MemberService service1 = context.getBean(MemberService.class);
MemberService service2 = context.getBean(MemberService.class);

System.out.println(service1 == service2); // true

컨테이너에서 여러 번 조회해도 주소 값이 같은 동일한 객체임을 확인할 수 있습니다.

3. Singleton Scope를 사용하는 이유와 장점

요청이 들어올 때마다 객체를 새로 생성하면 객체 생성 비용과 메모리 사용량이 지속적으로 증가합니다.
반면 Singleton Scope는 한 번만 생성하고 계속 재사용하므로 다음과 같은 강력한 장점을 가집니다.

  • 메모리 사용량 감소: 객체를 단 하나만 생성하여 공유하므로 메모리를 매우 효율적으로 사용합니다.
  • 객체 생성 비용 감소 및 성능 향상: 생성 비용이 큰 객체를 반복해서 만들지 않고 이미 생성된 객체를 재사용하므로 요청 처리 속도가 향상됩니다.
  • Spring의 설계 철학과 부합: Service나 Repository처럼 상태를 가지지 않는 객체는 여러 사용자가 동시에 공유해도 문제가 없으므로 Singleton 구조에 가장 적합합니다.

4.가장 중요한 주의사항: 무상태(Stateless) 설계

Singleton Bean은 여러 사용자(Thread)가 동시에 공유하는 객체입니다.
따라서 절대 내부 상태를 저장하는 Stateful 구조로 설계해서는 안 됩니다.

❌ Stateful Bean의 문제점 (잘못된 예시)

@Service
public class OrderService {

    private String currentUser; // ⚠️ 필드에 상태를 저장!

    public void order(String user) {
        currentUser = user;
    }
}
  • 사용자 A가 주문을 진행하여 `currentUser = "UserA"`가 됩니다.
  • 동시 요청에 의해 사용자 B가 주문을 진행하면 `currentUser = "UserB"`로 값이 변경됩니다.
  • 이때 사용자 A의 정보가 덮어씌워져 데이터 오염 및 심각한 동시성 장애가 발생합니다.

⭕ Stateless Bean 설계 (올바른 예시)

@Service
public class OrderService {

    public void order(String user) {
        // 필드에 저장하지 않고 메서드 매개변수나 지역 변수로 처리합니다.
        System.out.println(user);
    }
}
  • Singleton Bean은 Thread Safety를 자동으로 보장하지 않습니다.
  • 상태를 필드에 저장하지 않고 매개변수, 지역 변수, ThreadLocal 등을 활용하여 Stateless하게 작성하는 것이 핵심 원칙입니다.

5. 실무에서의 활용

실무에서는 대부분의 Bean을 Singleton으로 관리합니다.

  • 주요 대상: Controller, Service, Repository, Configuration, Utility Bean
  • 특징: 이러한 객체들은 상태를 저장하지 않고 요청을 처리하는 로직만 수행하므로 Singleton으로 관리하기에 가장 적합합니다.

정리

Singleton Scope는 Spring 컨테이너당 하나의 Bean만 생성하여 재사용하는 기본 Scope입니다. 객체 생성 비용을 줄이고 메모리를 효율적으로 사용할 수 있어 대부분의 Spring Bean은 Singleton으로 관리됩니다. 다만 Singleton Bean은 여러 요청이 하나의 객체를 공유하므로 상태를 저장하지 않는 Stateless 객체로 설계해야 합니다. 다음 장에서는 Singleton과 반대로 Bean을 요청할 때마다 새로운 객체를 생성하는 Prototype Scope를 살펴봅니다.

핵심 질문

Q1. Singleton Scope란 무엇인가요?

Spring 컨테이너당 하나의 Bean 인스턴스만 생성하여 모든 요청에서 공유하는 Scope입니다.

Q2. Spring의 기본 Bean Scope는 무엇인가요?

Singleton Scope입니다. 별도의 Scope를 지정하지 않으면 모든 Bean은 Singleton으로 등록됩니다.

Q3. Singleton Bean을 Stateless하게 설계해야 하는 이유는 무엇인가요?

Singleton Bean은 여러 요청과 여러 스레드가 동일한 객체를 공유하므로, 필드에 상태를 저장하면 데이터가 덮어쓰이거나 동시성 문제가 발생할 수 있기 때문입니다.

Q4. Singleton Scope의 장점은 무엇인가요?

객체를 한 번만 생성하여 재사용하므로 메모리 사용량과 객체 생성 비용을 줄일 수 있으며, 애플리케이션의 성능을 향상시킬 수 있습니다.

4.5 Prototype Scope

앞에서 Singleton Scope는 Spring의 기본 Scope이며, 컨테이너당 하나의 Bean만 생성하여 모든 요청에서 공유한다는 것을 살펴보았습니다.그렇다면 매번 새로운 객체가 필요한 경우에는 어떻게 해야 할까요? 예를 들어 요청마다 독립적인 객체가 필요하거나, 상태를 일시적으로 저장하는 객체라면 Singleton보다 다른 Scope가 적합합니다. 이러한 경우 사용하는 것이 Prototype Scope입니다. 이번 장에서는 Prototype Scope의 동작 원리와 Singleton과의 차이점, 그리고 실무에서 사용할 때 주의해야 할 사항을 알아봅니다.

1. Prototype Scope란?

Prototype Scope는 Bean을 요청할 때마다 새로운 객체를 생성하는 Scope입니다. Singleton처럼 하나의 객체를 공유하지 않고, 컨테이너에 Bean을 요청할 때마다 새로운 인스턴스를 생성하여 반환합니다.

  • Bean 요청 ➔ Prototype Bean 생성 ➔ 반환
  • 다시 요청 ➔ 새로운 Prototype Bean 생성 ➔ 반환

Prototype Bean 등록 및 조회

@Scope("prototype") 어노테이션을 사용하여 지정합니다.

@Component
@Scope("prototype")
public class PrototypeBean {
}

컨테이너에서 같은 Bean을 두 번 조회해 보면 매번 새로운 객체가 생성되므로 서로 다른 주소 값을 가집니다.

PrototypeBean bean1 = context.getBean(PrototypeBean.class);
PrototypeBean bean2 = context.getBean(PrototypeBean.class);

System.out.println(bean1 == bean2); // false

2. Singleton과 Prototype 비교

1) 객체 생성 시점의 차이

  • Singleton: 애플리케이션 시작 시(Spring 컨테이너 생성 시) 객체를 생성하고 이후 계속 재사용합니다.
  • Prototype: 애플리케이션 시작 시에는 생성되지 않고, Bean을 요청하는 시점마다 새로 생성됩니다.

2) 생명주기(Lifecycle)의 차이

  • Singleton: 생성 ➔ 의존성 주입 ➔ 초기화 ➔ 사용 ➔ 소멸까지 전체 생명주기를 Spring이 관리합니다.
  • Prototype: 생성 ➔ 의존성 주입 ➔ 초기화까지만 Spring이 관리하며, Bean을 반환한 이후에는 더 이상 Spring이 관리하지 않습니다.

3. Prototype Bean 사용 시 주의사항

1. @PreDestroy가 호출되지 않는 이유

Prototype Bean에서는 @PreDestroy 소멸 콜백 메서드가 자동으로 실행되지 않습니다.

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

    @PreDestroy
    public void destroy() {
        System.out.println("Destroy"); // ❌ 자동으로 호출되지 않습니다!
    }
}

Spring 컨테이너는 Prototype Bean을 생성하고 초기화한 뒤 클라이언트에 반환하면 더 이상 관리 권한을 갖지 않습니다. 따라서 애플리케이션이 종료되더라도 소멸 메서드가 자동으로 실행되지 않으며, 자원 해제가 필요하다면 개발자가 직접 관리해야 합니다.

2. Singleton Bean과 함께 사용할 때 발생하는 문제 (스프링 빈 주입)

실무에서 자주 실수하는 부분 중 하나는 Singleton Bean 내부에서 Prototype Bean을 직접 주입받는 경우입니다.

@Service
@RequiredArgsConstructor
public class MemberService {

    // Singleton Bean 생성 시점에 한 번만 주입됩니다!
    private final PrototypeBean prototypeBean; 
}
  • MemberService는 Singleton이므로 애플리케이션 시작 시 딱 한 번만 생성됩니다.
  • 이 시점에 PrototypeBean도 함께 주입되므로, 이후 MemberService를 사용할 때마다 동일한 PrototypeBean을 계속 사용하게 됩니다.
  • 즉, Prototype으로 설정했더라도 실제로는 Singleton처럼 동작하는 문제가 발생합니다.
해결 방법: ObjectProvider를 통한 지연 조회(DL)
매번 새로운 Prototype Bean을 생성하여 사용하고 싶다면,
주입 대신 필요한 시점에 지연 조회(Dependency Lookup)를 수행해야 합니다.
@Service
@RequiredArgsConstructor
public class MemberService {

    private final ObjectProvider<PrototypeBean> provider;

    public void execute() {
        // getObject()를 호출할 때마다 새로운 Prototype Bean이 생성됩니다.
        PrototypeBean bean = provider.getObject(); 
    }
}

4. 실무에서의 활용

Prototype Scope는 생각보다 실무에서 자주 사용되지 않습니다. 대부분의 Spring Bean은 무상태(Stateless)로 설계되어 Singleton으로 충분하기 때문입니다.

  • 적합한 사용 대상: 상태를 일정 시간 유지해야 하는 작업 객체, 매 요청마다 새로 조합해야 하는 Builder 객체, 일회성 처리 객체, 독립적인 테스트용 객체 등
  • 대부분의 스프링 프로젝트에서는 Singleton을 중심으로 구성하며, 특수한 목적이 있을 때만 제한적으로 Prototype을 활용합니다.

정리

Prototype Scope는 Bean을 요청할 때마다 새로운 객체를 생성하는 Scope입니다. Singleton과 달리 객체를 공유하지 않으며, 생성과 초기화까지만 Spring이 관리하고 이후의 생명주기는 관리하지 않습니다. 따라서 @PreDestroy가 자동으로 호출되지 않으며, 필요한 자원 해제는 개발자가 직접 수행해야 합니다. 또한 Singleton Bean에 Prototype Bean을 일반적인 방식으로 주입하면 한 번만 생성된 객체가 계속 사용되므로, 매번 새로운 객체가 필요하다면 ObjectProvider와 같은 지연 조회 기능을 사용하는 것이 적절합니다. 다음 장에서는 웹 애플리케이션에서 자주 사용하는 Request Scope, Session Scope, Application Scope를 중심으로 Web Scope를 살펴봅니다.

핵심 질문

Q1. Prototype Scope란 무엇인가요?

Bean을 요청할 때마다 새로운 인스턴스를 생성하여 반환하는 Scope입니다.

Q2. Singleton Scope와 Prototype Scope의 가장 큰 차이점은 무엇인가요?

Singleton은 하나의 Bean을 재사용하지만, Prototype은 Bean을 요청할 때마다 새로운 객체를 생성합니다.

Q3. Prototype Bean에서 @PreDestroy가 자동으로 호출되지 않는 이유는 무엇인가요?

Spring은 Prototype Bean의 생성과 초기화까지만 관리하며, Bean을 반환한 이후의 생명주기는 관리하지 않기 때문입니다.

Q4. Singleton Bean에 Prototype Bean을 주입하면 항상 새로운 Prototype Bean이 사용되는가요?

아닙니다. Singleton Bean이 생성되는 시점에 한 번만 Prototype Bean이 주입됩니다. 매번 새로운 Prototype Bean이 필요하다면 ObjectProvider와 같은 지연 조회 방식을 사용해야 합니다.

4.6 Web Scope (Request, Session, Application)

앞에서 Singleton Scope와 Prototype Scope를 살펴보았습니다. Singleton은 애플리케이션 전체에서 하나의 객체를 공유하고, Prototype은 요청할 때마다 새로운 객체를 생성합니다. 하지만 웹 애플리케이션에서는 이 두 Scope만으로 해결하기 어려운 경우가 있습니다. 예를 들어 HTTP 요청마다 다른 객체가 필요하거나, 로그인한 사용자별로 데이터를 관리해야 하는 경우가 있습니다. 이러한 상황을 위해 Spring은 Web Scope를 제공합니다. 이번 장에서는 Request Scope, Session Scope, Application Scope의 개념과 활용 방법을 알아봅니다.

1. Web Scope란?

Web Scope는 웹 환경에서만 사용할 수 있는 Bean Scope입니다.
HTTP 요청과 Session 같은 웹의 생명주기에 맞춰 Bean을 생성하고 관리합니다.

Scope 생성 기준 소멸 시점
Request HTTP 요청마다 요청 종료 시
Session HTTP Session마다 Session 종료 시
Application ServletContext마다 애플리케이션 종료 시

Singleton이나 Prototype과 달리 웹 요청의 흐름에 맞춰 Bean이 관리됩니다.

2. Request Scope

Request Scope는 HTTP 요청마다 새로운 Bean을 생성하는 Scope입니다.

  • `Request A ➔ RequestBean A`
  • `Request B ➔ RequestBean B`
  • 사용자 A와 사용자 B가 동시에 요청을 보내더라도 서로 다른 Bean을 사용합니다.
  • Bean은 요청이 끝나면 함께 소멸됩니다.

Request Scope 등록

@Scope를 이용하거나 Spring에서 제공하는 축약 어노테이션인 @RequestScope를 사용할 수 있습니다.

// 축약 어노테이션 사용 (권장)
@Component
@RequestScope
public class RequestBean {
}

Request Scope의 활용

요청마다 독립적인 데이터를 저장해야 하는 경우에 적합합니다.

  • 주요 사용 예시: 요청 ID(Request ID), 요청 시작 시간, 요청 로그, 사용자 요청 정보
  • 각 요청은 자신만의 RequestLog 객체를 사용하므로 다른 요청과 데이터가 섞이지 않습니다.

3. Session Scope & Application Scope

Session Scope

HTTP Session마다 하나의 Bean을 생성하는 Scope입니다.

  • `User A Session ➔ SessionBean A`
  • `User B Session ➔ SessionBean B`
  • 등록 방법: @SessionScope 어노테이션을 사용합니다.
  • 주요 사용 예시: 로그인한 사용자 정보, 장바구니, 사용자 설정 등 사용자별 상태 관리

Application Scope

ServletContext당 하나의 Bean을 생성하는 Scope입니다.

  • 특징: Singleton과 비슷해 보이지만 Singleton은 Spring Container 기준이고, Application Scope는 ServletContext 기준입니다.
  • 일반적인 Spring Boot 애플리케이션에서는 두 Scope의 차이를 체감할 일이 많지 않습니다.

4. Scope별 생명주기 및 비교

Scope별 생명주기 비교

  • Singleton:   Application 시작  ─────────────► Application 종료
  • Request:     요청 시작        ───────────────► 요청 종료
  • Session:     Session 생성     ──────────────► Session 종료
  • Application: ServletContext 생성 ───────────► ServletContext 종료

Singleton vs Request Scope 비교

Singleton Request
하나의 객체 공유 요청마다 새로운 객체
모든 사용자가 공유 요청마다 독립적인 객체
상태 저장에 부적합 요청 단위 상태 저장에 적합

5. Singleton Bean에서 Request Scope Bean 사용 시 문제점

Singleton Bean은 애플리케이션 시작 시 생성되지만, Request Bean은 Actual HTTP 요청이 들어와야 생성됩니다.

@Service
@RequiredArgsConstructor
public class MemberService {

    // ⚠️ 애플리케이션 시작 시점에는 Request가 존재하지 않아 에러가 발생합니다!
    private final RequestBean requestBean; 
}

애플리케이션 시작 시점에는 HTTP 요청이 없으므로 주입 단계에서 오류가 발생합니다. 실무에서는 이러한 문제를 해결하기 위해 Scoped Proxy (proxyMode = ScopedProxyMode.TARGET_CLASS)나 ObjectProvider를 사용합니다.

6. 실무에서의 활용

실무에서는 대부분의 Bean이 Singleton입니다. Web Scope는 다음과 같이 필요한 경우에만 제한적으로 사용합니다.

  • Request Scope: 요청 추적(Request Trace), 요청 로그, Correlation ID
  • Session Scope: 로그인 사용자 정보, 장바구니
  • Application Scope: ServletContext 공유 데이터, 애플리케이션 전역 속성

정리

Web Scope는 HTTP 요청과 Session 같은 웹 환경의 생명주기에 맞춰 Bean을 관리하는 Scope입니다. Request Scope는 요청마다 새로운 Bean을 생성하고, Session Scope는 사용자 Session마다 하나의 Bean을 생성하며, Application Scope는 ServletContext 단위로 Bean을 관리합니다. 대부분의 Spring Bean은 Singleton을 사용하지만, 요청별 또는 사용자별 상태를 안전하게 관리해야 하는 경우에는 Web Scope를 사용하는 것이 적절합니다. 다음 장에서는 지금까지 학습한 내용을 바탕으로 Singleton Bean과 Prototype Bean의 차이점 및 사용 시 주의사항을 종합적으로 정리합니다.

핵심 질문

Q1. Web Scope란 무엇인가?

웹 애플리케이션에서 HTTP 요청과 Session 등의 생명주기에 맞추어 Bean을 생성하고 관리하는 Scope이다.

Q2. Request Scope와 Session Scope의 차이점은 무엇인가?

Request Scope는 HTTP 요청마다 새로운 Bean을 생성하며 요청 종료 시 소멸한다. Session Scope는 사용자 Session마다 하나의 Bean을 생성하며 Session 종료 시 소멸한다.

Q3. Singleton Bean에 Request Scope Bean을 바로 주입하면 어떤 문제가 발생할 수 있는가?

Singleton Bean은 애플리케이션 시작 시 생성되지만 Request Scope Bean은 HTTP 요청이 있어야 생성된다. 따라서 단순한 주입은 생성 시점의 차이로 인해 문제가 발생할 수 있으며, 일반적으로 Scoped Proxy나 ObjectProvider를 사용하여 해결한다.

Q4. Web Scope는 언제 사용하는가?

요청별 로그 관리, 사용자별 로그인 정보, 장바구니, 요청 추적과 같이 요청이나 Session 단위의 상태를 관리해야 하는 경우에 사용한다.

'🍃SpringBoot' 카테고리의 다른 글

Spring: Part 5. Spring Configuration  (0) 2026.07.29
Spring: Part 3. 의존성 주입(DI)과 활용  (0) 2026.07.29
Spring: Part 2. IoC 컨테이너와 Bean  (0) 2026.07.29
Spring: Part 1. Spring Framework의 이해  (0) 2026.07.28
Spring IoC & DI 완전 정복: Chapter 8. Proxy와 Spring AOP  (1) 2026.07.22
'🍃SpringBoot' 카테고리의 다른 글
  • Spring: Part 5. Spring Configuration
  • Spring: Part 3. 의존성 주입(DI)과 활용
  • Spring: Part 2. IoC 컨테이너와 Bean
  • Spring: Part 1. Spring Framework의 이해
limdaeil
limdaeil
limdaeil 님의 블로그 입니다.
  • limdaeil
    limdaeil
    limdaeil
  • 전체
    오늘
    어제
    • 분류 전체보기 (83) N
      • 💭Retrospective (21)
      • 🥕FrontEnd (0)
      • 🐬MySQL (2) N
      • 🐍Python (5)
      • 🍃SpringBoot (33) N
      • ☕Java (1)
      • ♾️Devops (1)
      • 🌎Network (2)
      • 📚Read & 👨‍🏫Course (10)
      • Programmers (1)
      • 🧪Test (7)
  • 블로그 메뉴

    • 홈
    • 태그
    • 방명록
  • 링크

  • 공지사항

  • 인기 글

  • 태그

    jwt
    회고
    redis
    MySQL
    Mockito
    gradle
    spring boot
    Concurrency
    책
    Spring
    junit
    IoC
    Python
    서평단
    DI
    한빛아카데미
    distributed lock
    한빛미디어
    나는리뷰어다
    optimistic rock
  • 최근 댓글

  • 최근 글

  • hELLO· Designed By정상우.v4.10.6
limdaeil
Spring: Part 4. Bean 생명주기(Lifecycle)
상단으로

티스토리툴바