스프링
스프링 빈의 스코프와 생명주기
yoon4360
2025. 4. 1. 22:14
스프링을 사용하다 보면 종종 궁금해진다.
“이 빈은 왜 한 번만 생성되지?”
“매번 새로운 인스턴스를 받고 싶은데 어떻게 하지?”
“공유되는 빈을 여러 스레드에서 써도 괜찮을까?”
이런 궁금증은 모두 스프링 빈의 스코프(scope)와 생명주기 개념과 관련 있다.
이번 글에서는 싱글톤, 프로토타입, @Scope, @Lazy 등 빈의 생성 방식과 사용 시 주의할 점을
정리해보겠다.
스프링 빈 스코프란?
빈은 스프링 컨테이너가 생성하고 관리하는 객체다.
빈 스코프는 스프링이 빈 인스턴스를 언제, 얼마나 자주 생성할지를 결정하는 설정이다.
대표적인 두가지 스코프는 다음과 같다.
- singleton: 하나의 인스턴스를 앱 전역에서 공유 (기본값)
- prototype: 요청할 때마다 새로운 인스턴스 생성
싱글톤 스코프 @Scope("singleton")
특징
- 스프링의 기본 스코프이다. (별도 설정 없이 자동 적용)
- 컨텍스트에 등록된 후 하나의 인스턴스를 앱 전역에서 공유한다.
- @Component, @Service, @Repository, @Bean 등 모두 사용 가능하다.
장점
- 메모리 효율이 좋다.
- 의존성 주입이 간단하다.
주의점
- 여러 컴포넌트에서 동시에 접근시 동시성 이슈가 발생할 수 있다.
- 만약 싱글톤 빈이 상태를 가지면 경쟁 조건이 발생할 수 있다.
@Component
public class SharedCounter {
private int count = 0;
public void increase() {
count++; // 여러 스레드에서 동시 접근 시 위험
}
}
그렇다면 동기화하면 해결될까?
가능은 하지만 성능저하가 심각해진다.
락이 걸려 동시에 한 스레드만 접근 가능해지고, CPU의 자원이 낭비 된다. 그리고 데드락의 위험도 있다.
따라서 가능하면 싱글톤 빈은 상태를 갖지 않게 설계하는 것이 좋다.
프로토타입 스코프 @Scope("prototype")
특징
- 스프링은 요청이 있을 때마다 새 인스턴스를 생성한다.
- 스프링 컨테이너는 객체 생성만 하고, 이후 생명주기는 직접 관리하지 않는다.
- 동시성 이슈가 없다. → 각 스레드는 자신만의 인스턴스를 사용한다.
사용 시점
- 요청마다 다른 데이터나 상태를 저장해야 할 때
- 가변 데이터를 다뤄야 할 때
주의점
- 싱글톤 빈에 주입할 때 주의해야한다.
@Component
public class MyService {
// 이건 한 번만 주입되고 계속 같은 인스턴스 사용됨
private final TempObject tempObject;
public MyService(TempObject tempObject) {
this.tempObject = tempObject;
}
}
TempObject가 프로토타입 스코프라도, 싱글톤에서는 한번 주입되면 같은 인스턴스만 사용되기에 처음 한번만 생성된다.
이 경우 ObjectProvider, ApplicationContext.getBean() 을 사용하면 된다.
지연로딩 @Lazy
@Component
@Lazy
public class HeavyService {
public HeavyService() {
System.out.println("무거운 서비스 생성됨");
}
}
- @Lazy를 붙이면 빈을 실제로 사용할 때까지 생성하지 않는다.
- 앱 시작 시점에 모든 빈을 미리 만들 필요가 없을 때 유용하다.
- 메모리 낭비 방지, 시작 속도 향상 가능하다.
사용 시점
- 모놀로식 앱에서 모든 기능을 초기에 쓰지 않을 경우
- 무거운 로직을 가진 컴포넌트
싱글톤 vs 프로토타입
스코프 | singleton | prototype |
기본값 | ✅ | ❌ |
인스턴스 수 | 하나 | 요청할 때마다 새로 생성 |
상태 관리 | 불변 상태 권장 | 가변 상태 사용 가능 |
동시성 문제 | 주의 필요 | 없음 |
메모리 사용 | 효율적 | 많아질 수 있음 |
주입 시 주의 | 없음 | 싱글톤에 주입 시 한 번만 생성됨 |
즉시 vs 지연 인스턴스 생성
구분 | 즉시 생성 | 지연생성(@Lazy) |
생성 시점 | 앱 시작 시 바로 생성 | 사용할 때 생성 |
메모리 사용 | 전체 빈을 모두 로딩 | 필요한 빈만 로딩 |
성능 | 초기 로딩 시간이 길 수 있음 | 필요할 때만 비용 발생 |
에러 감지 | 앱 실행 시 바로 발견 | 사용 시점까지 지연됨 |
마무리
- 스프링 빈은 무조건 싱글톤이면 좋은 게 아니다.
- 빈이 상태를 가지면 동시성 문제를 반드시 고려해야 한다.
- 가변 상태가 필요한 경우엔 @Scope("prototype")으로 관리하면 된다.
- 앱 초기 속도나 메모리 최적화가 필요하다면 @Lazy로 지연 생성을 활용하자.