[Spring] 11. Bean Scope란?

[Spring] 11. Bean Scope란?

이전 글에서는 @Qualifier@Primary에 대해 살펴봤습니다. 같은 타입의 Bean이 여러 개 있을 때 어떤 Bean을 주입할지 정하기 위해 사용하는 방법이었습니다.

이번 글에서는 Bean Scope에 대해 정리하겠습니다. Bean Scope는 Spring Bean이 생성되고 유지되는 범위를 의미합니다. 쉽게 말하면, Spring이 만든 Bean 객체를 얼마나 오래 사용하고, 몇 개나 만들 것인지 정하는 개념입니다.

Spring Bean은 기본적으로 Singleton Scope로 생성됩니다. 즉, 하나의 Bean 객체를 만들어서 여러 곳에서 함께 사용합니다.


1. Bean Scope란?

Bean Scope는 Spring Container가 Bean을 어떤 범위로 관리할지 정하는 설정입니다.

Spring은 Bean을 생성하고 Container 안에서 관리합니다. 그런데 모든 Bean을 같은 방식으로 관리하는 것은 아닙니다. 어떤 Bean은 애플리케이션이 실행되는 동안 하나만 만들어서 계속 사용할 수 있고, 어떤 Bean은 요청마다 새로 만들어야 할 수도 있습니다.

Bean Scope
 → Bean 객체를 몇 개 만들 것인가
 → Bean 객체를 얼마나 오래 유지할 것인가
 → Bean 객체를 어떤 범위에서 사용할 것인가

즉, Bean Scope는 Bean의 생존 범위와 사용 범위를 정하는 개념입니다.


2. 대표적인 Bean Scope 종류

Spring에서 사용하는 대표적인 Bean Scope는 다음과 같습니다.

singleton Spring Container 안에서 Bean 객체를 하나만 생성해서 공유합니다.
prototype Bean을 요청할 때마다 새로운 객체를 생성합니다.
request HTTP 요청 하나마다 새로운 Bean을 생성합니다.
session HTTP Session 하나마다 새로운 Bean을 생성합니다.
application ServletContext 범위에서 Bean을 관리합니다.

처음 Spring을 공부할 때는 이 중에서 singletonprototype을 먼저 이해하면 됩니다. 특히 Spring Bean의 기본 Scope는 singleton이라는 점이 중요합니다.


3. Singleton Scope란?

Singleton Scope는 Spring Container 안에서 Bean 객체를 하나만 생성하고, 그 하나의 객체를 여러 곳에서 공유하는 방식입니다.

Spring Container
 └─ boardService Bean 1개

BoardController → boardService 사용
AdminController → boardService 사용
ApiController   → boardService 사용

위 구조에서 BoardService Bean은 하나만 만들어집니다. 그리고 여러 Controller에서 같은 BoardService Bean을 주입받아 사용합니다.

Spring Bean은 기본적으로 Singleton Scope입니다. 따라서 별도의 설정을 하지 않으면 대부분의 Bean은 하나만 생성되어 공유됩니다.

@Service
public class BoardService {
}

위 BoardService는 기본적으로 Singleton Bean으로 등록됩니다.


4. Singleton Bean이 필요한 이유

웹 애플리케이션에서는 Controller, Service, Repository 같은 객체가 자주 사용됩니다. 요청이 들어올 때마다 이런 객체를 계속 새로 만들면 비효율적일 수 있습니다.

예를 들어 게시글 목록 조회 요청이 100번 들어왔다고 해서 BoardService 객체를 100번 만들 필요는 없습니다. BoardService는 보통 상태를 저장하지 않고, 기능을 처리하는 역할만 담당합니다.

요청 1 → BoardService 사용
요청 2 → BoardService 사용
요청 3 → BoardService 사용

BoardService 객체는 하나만 만들어 공유

이렇게 하나의 객체를 공유하면 객체 생성 비용을 줄이고, Container가 객체를 효율적으로 관리할 수 있습니다. 그래서 Spring은 기본적으로 Bean을 Singleton으로 관리합니다.


5. Singleton Pattern과 Spring Singleton의 차이

Singleton이라는 말을 들으면 디자인 패턴의 Singleton Pattern을 떠올릴 수 있습니다. 하지만 Spring Singleton Bean과 일반적인 Singleton Pattern은 완전히 같은 개념은 아닙니다.

Singleton Pattern 클래스 자체에서 객체를 하나만 만들도록 제한하는 디자인 패턴입니다.
Spring Singleton Spring Container 안에서 Bean 객체를 하나만 생성하고 공유하는 방식입니다.

일반적인 Singleton Pattern은 클래스 내부에서 생성자를 막고, static 메서드로 객체를 제공하는 구조를 자주 사용합니다.

public class SingletonService {

    private static final SingletonService instance = new SingletonService();

    private SingletonService() {
    }

    public static SingletonService getInstance() {
        return instance;
    }
}

반면 Spring Singleton은 개발자가 이런 코드를 직접 작성하지 않습니다. Spring Container가 Bean을 하나만 만들고 관리합니다.

@Service
public class BoardService {
}

위처럼 작성하면 Spring Container가 BoardService Bean을 하나만 생성해서 관리합니다. 즉, Spring Singleton은 Container가 관리하는 Singleton입니다.


6. Singleton Bean 사용 시 주의할 점

Singleton Bean은 하나의 객체를 여러 요청과 여러 클래스가 함께 사용합니다. 그래서 상태를 저장하는 필드를 조심해야 합니다.

예를 들어 다음 코드는 위험할 수 있습니다.

@Service
public class BoardService {

    private String title;

    public void write(String title) {
        this.title = title;
    }

    public String getTitle() {
        return title;
    }
}

BoardService는 Singleton Bean입니다. 여러 사용자가 동시에 게시글을 작성하면 같은 BoardService 객체의 title 필드를 함께 사용하게 됩니다.

사용자 A → title = "A의 게시글"
사용자 B → title = "B의 게시글"

같은 BoardService 객체의 필드를 공유

이렇게 되면 사용자 A의 데이터와 사용자 B의 데이터가 섞일 위험이 있습니다. 따라서 Singleton Bean에는 사용자별로 달라지는 상태값을 필드에 저장하지 않는 것이 좋습니다.


7. 좋은 Singleton Bean 구조

Service Bean은 보통 상태를 저장하지 않고, 메서드 안에서 필요한 값을 처리하는 방식으로 작성합니다.

@Service
public class BoardService {

    public void write(String title) {
        System.out.println("게시글 제목: " + title);
    }
}

위 코드는 title을 필드에 저장하지 않습니다. 메서드 파라미터로 받은 값을 메서드 안에서 처리합니다.

이런 구조가 Singleton Bean에서 안전합니다. 요청마다 달라지는 값은 필드에 저장하지 말고, 파라미터나 지역 변수로 처리하는 것이 좋습니다.

피해야 할 것
 → 사용자별 데이터를 Singleton Bean 필드에 저장

권장하는 것
 → 요청 데이터는 파라미터, 지역 변수, DTO로 처리

8. Prototype Scope란?

Prototype Scope는 Bean을 요청할 때마다 새로운 객체를 생성하는 방식입니다.

getBean 호출 1 → 새로운 객체 생성
getBean 호출 2 → 새로운 객체 생성
getBean 호출 3 → 새로운 객체 생성

Prototype Scope를 사용하려면 @Scope("prototype")를 사용할 수 있습니다.

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

이 Bean은 요청할 때마다 새 객체가 생성됩니다. 다만 일반적인 Controller, Service, Repository는 대부분 Singleton으로 충분합니다. Prototype Scope는 특별한 상황에서만 사용하는 경우가 많습니다.


9. Singleton과 Prototype 차이

Singleton과 Prototype의 차이를 정리하면 다음과 같습니다.

구분 Singleton Prototype
객체 생성 Container 안에서 하나만 생성됩니다. 요청할 때마다 새로 생성됩니다.
공유 여부 여러 곳에서 같은 객체를 공유합니다. 매번 다른 객체를 사용합니다.
기본값 Spring Bean의 기본 Scope입니다. 직접 설정해야 합니다.
주 사용처 Controller, Service, Repository 요청마다 새 객체가 필요한 특수한 경우

Spring 프로젝트에서는 대부분 Singleton Bean을 사용합니다. Prototype은 반드시 새 객체가 필요한 경우에만 검토하면 됩니다.


10. Web Scope

웹 애플리케이션에서는 HTTP 요청이나 세션 단위로 Bean을 관리할 수도 있습니다. 대표적으로 request Scope와 session Scope가 있습니다.

request HTTP 요청 하나마다 Bean이 생성되고 요청이 끝나면 사라집니다.
session 사용자 Session 하나마다 Bean이 생성되고 Session 동안 유지됩니다.

예를 들어 request Scope는 요청마다 독립적인 데이터를 관리해야 할 때 사용할 수 있습니다. session Scope는 로그인 사용자별 상태를 관리할 때 사용할 수 있습니다.

다만 처음 Spring을 공부할 때는 Web Scope를 깊게 들어가기보다, 이런 Scope도 있다는 정도로 이해하면 충분합니다. 기본은 Singleton입니다.


11. 자주 하는 실수

1) Singleton Bean에 사용자 데이터를 필드로 저장하는 경우

가장 조심해야 하는 실수입니다. Service는 기본적으로 Singleton Bean입니다. 따라서 사용자마다 달라지는 데이터를 Service 필드에 저장하면 위험합니다.

@Service
public class MemberService {

    private Long loginMemberId;

    public void login(Long memberId) {
        this.loginMemberId = memberId;
    }
}

이런 방식은 여러 사용자가 동시에 접근할 때 데이터가 섞일 수 있습니다. 로그인 사용자 정보는 보통 Session, SecurityContext, 요청 객체 등을 통해 다루는 것이 좋습니다.

2) 모든 Bean을 Prototype으로 바꾸려는 경우

Singleton이 공유된다고 해서 무조건 위험한 것은 아닙니다. 상태를 저장하지 않는 Service, Repository는 Singleton으로 사용해도 괜찮습니다.

Controller
Service
Repository
Mapper

대부분 Singleton으로 사용

문제는 Singleton 자체가 아니라, Singleton Bean 안에 사용자별 상태값을 저장하는 방식입니다.

3) DTO를 Bean Scope로 관리하려는 경우

DTO는 보통 요청 데이터나 응답 데이터를 담는 객체입니다. 요청마다 새로 만들어지는 값 객체에 가깝습니다. 따라서 일반적으로 Spring Bean으로 등록해서 Scope를 설정하지 않습니다.

Bean Scope를 주로 생각하는 대상
 → Controller
 → Service
 → Repository
 → Component
 → 설정 객체

보통 Bean Scope로 관리하지 않는 대상
 → DTO
 → VO
 → Entity
 → 단순 요청/응답 객체

12. Scope 흐름 정리

Bean Scope를 한 줄로 정리하면 다음과 같습니다.

Bean Scope는 Spring Bean이 생성되고 유지되는 범위이다.

Spring Bean의 기본 Scope는 Singleton입니다. 따라서 대부분의 Bean은 Spring Container 안에서 하나만 생성되고 여러 곳에서 공유됩니다.

Spring 실행
 → Component Scan
 → Bean 등록
 → Singleton Bean 생성
 → 여러 곳에서 같은 Bean 공유
 → 애플리케이션 종료 시 Bean 종료

Singleton Bean을 사용할 때는 상태값을 조심해야 합니다. 사용자별로 달라지는 데이터는 필드에 저장하지 말고, 메서드 파라미터나 지역 변수, DTO, Session 같은 적절한 위치에서 관리해야 합니다.


정리

Bean Scope는 Spring Bean이 생성되고 유지되는 범위를 의미합니다. Spring Bean은 기본적으로 Singleton Scope로 관리됩니다. Singleton Scope는 Spring Container 안에서 Bean 객체를 하나만 만들고 여러 곳에서 공유하는 방식입니다.

singleton Bean을 하나만 생성해서 공유합니다. Spring Bean의 기본값입니다.
prototype Bean을 요청할 때마다 새로 생성합니다.
request HTTP 요청 하나마다 Bean을 생성합니다.
session HTTP Session 하나마다 Bean을 생성합니다.

Spring에서 Controller, Service, Repository는 대부분 Singleton Bean으로 사용합니다. Singleton Bean을 안전하게 사용하려면 사용자별로 달라지는 상태값을 필드에 저장하지 않는 것이 중요합니다.


연습해보기

아래 코드에서 어떤 점이 위험한지 생각해보세요.

@Service
public class CartService {

    private Long memberId;
    private List<Long> productIds = new ArrayList<>();

    public void addCart(Long memberId, Long productId) {
        this.memberId = memberId;
        this.productIds.add(productId);
    }
}

CartService는 Singleton Bean일 가능성이 높습니다. 따라서 memberId와 productIds를 필드로 저장하면 여러 사용자의 장바구니 데이터가 섞일 수 있습니다. 이런 데이터는 DB, Session, 요청 DTO, 지역 변수 등 적절한 위치에서 관리해야 합니다.

다음 글에서는 Singleton Bean을 조금 더 자세히 다루겠습니다. 특히 Singleton Bean에서 상태값을 잘못 관리하면 어떤 문제가 생기는지 예제로 정리하겠습니다.