Bean Scope 완전 정복
Scope가 뭔가요?
Scope = "이 객체를 얼마나 오래, 어디까지 공유할 것인가"
비유: 카페의 컵
싱글톤 = 카페 전체에서 컵 1개 공유 (모두가 같은 컵 사용)
프로토타입 = 손님마다 새 컵 제공
request = 한 번 주문할 때마다 새 컵
session = 한 손님이 앉아있는 동안 같은 컵 사용
각 Scope 상세 설명
1. Singleton (기본값)
@Service // 기본이 singleton
// @Scope("singleton") 생략 가능
public class OrderService {
private int orderCount = 0; // ⚠️ 위험한 코드 (아래 설명)
}
앱 시작
↓
Spring: "OrderService 딱 1개만 만들게"
↓
컨테이너에 보관
사용자 A 요청 ──┐
사용자 B 요청 ──┼──→ 모두 같은 OrderService 인스턴스 사용
사용자 C 요청 ──┘
앱 종료될 때까지 이 1개가 계속 살아있음
중요한 주의점 - 상태를 가지면 안 됨
@Service
public class OrderService {
// ❌ 절대 이렇게 하면 안 됨
private int orderCount = 0;
public void order() {
orderCount++; // 사용자 A, B, C가 모두 같은 변수를 건드림
// → 동시성 문제 발생 (Race Condition)
}
// ✅ 상태를 메서드 안의 지역변수로만 써야 함
public void order(OrderRequest request) {
int localCount = 0; // 메서드 호출마다 새로 생성되는 지역변수 → 안전
localCount++;
}
}
사용 예: Service, Repository, Controller → 거의 모든 Spring Bean
2. Prototype
@Component
@Scope("prototype")
public class ReportGenerator {
private List<String> logs = new ArrayList<>(); // 상태를 가져도 OK
public void addLog(String log) {
logs.add(log);
}
}
사용자 A가 ReportGenerator 요청 → 새 인스턴스 생성 (인스턴스 #1)
사용자 B가 ReportGenerator 요청 → 새 인스턴스 생성 (인스턴스 #2)
사용자 C가 ReportGenerator 요청 → 새 인스턴스 생성 (인스턴스 #3)
각자 완전히 독립된 객체
핵심 주의점 - Spring이 소멸을 관리 안 함
Singleton: Spring이 생성 ~ 소멸 전부 관리
Prototype: Spring이 생성만 해주고 이후는 모름
→ @PreDestroy 콜백 호출 안 됨
→ 개발자가 직접 정리해야 함
사용 예: 요청마다 독립된 상태가 필요한 객체 (보고서 생성기, 장바구니 등)
3. Request
@Component
@Scope(value = WebApplicationContext.SCOPE_REQUEST,
proxyMode = ScopedProxyMode.TARGET_CLASS) // proxyMode 필수
public class RequestLogger {
private String requestId = UUID.randomUUID().toString();
public String getRequestId() {
return requestId;
}
}
HTTP 요청 1 들어옴 → RequestLogger 인스턴스 #1 생성
요청 처리 중... (Controller → Service → Repository)
이 요청 안에서는 항상 같은 #1 사용
HTTP 요청 1 끝남 → 인스턴스 #1 소멸
HTTP 요청 2 들어옴 → RequestLogger 인스턴스 #2 생성 (새로 생성)
사용 예: 요청 추적 ID, 요청별 로그 수집
4. Session
@Component
@Scope(value = WebApplicationContext.SCOPE_SESSION,
proxyMode = ScopedProxyMode.TARGET_CLASS)
public class ShoppingCart {
private List<Item> items = new ArrayList<>();
public void addItem(Item item) {
items.add(item);
}
}
사용자 A 로그인 → ShoppingCart #1 생성
페이지 이동해도, 여러 요청을 해도 ShoppingCart #1 유지
"상품 담기" → #1에 추가
"결제하기" → #1에서 가져옴
사용자 A 로그아웃 (세션 만료) → ShoppingCart #1 소멸
사용자 B 로그인 → ShoppingCart #2 생성 (완전히 별개)
Request vs Session 차이:
Request: 요청 1번 = 1개 (페이지 새로고침하면 새 객체)
Session: 로그인 ~ 로그아웃 = 1개 (브라우저 닫을 때까지 유지)
면접 핵심 포인트
Q. Singleton Bean이 Prototype Bean을 주입받으면?
@Service // singleton
public class OrderService {
@Autowired
private ReportGenerator reportGenerator; // prototype이어야 하는데...
public void createReport() {
reportGenerator.generate(); // 매번 새 객체를 원하지만...
}
}
문제 발생 과정:
앱 시작 시:
Spring: "OrderService 만들어야지" (singleton이라 1번만)
→ "ReportGenerator도 주입해야지"
→ ReportGenerator 인스턴스 #1 생성 후 OrderService에 주입
→ 이 시점에 끝!
사용자 A가 createReport() 호출 → reportGenerator는 여전히 #1
사용자 B가 createReport() 호출 → reportGenerator는 여전히 #1
사용자 C가 createReport() 호출 → reportGenerator는 여전히 #1
Prototype인데 사실상 Singleton처럼 동작 → 버그!
해결 방법 1: ObjectProvider (권장)
@Service
public class OrderService {
// ReportGenerator 자체를 주입받지 않고
// "꺼내주는 공장"을 주입받음
@Autowired
private ObjectProvider<ReportGenerator> reportGeneratorProvider;
public void createReport() {
// 호출할 때마다 새 인스턴스를 꺼내옴
ReportGenerator generator = reportGeneratorProvider.getObject();
generator.generate();
}
}
사용자 A가 createReport() 호출
→ getObject() 호출 → ReportGenerator #1 새로 생성
사용자 B가 createReport() 호출
→ getObject() 호출 → ReportGenerator #2 새로 생성
사용자 C가 createReport() 호출
→ getObject() 호출 → ReportGenerator #3 새로 생성
✅ 매번 새 객체!
해결 방법 2: @Lookup
@Service
public abstract class OrderService { // abstract로 선언
public void createReport() {
ReportGenerator generator = getReportGenerator(); // 호출마다 새 객체
generator.generate();
}
@Lookup // Spring이 이 메서드를 오버라이딩해서 매번 새 Bean 반환하도록 조작
protected abstract ReportGenerator getReportGenerator();
}
해결 방법 3: ApplicationContext 직접 사용 (비권장)
@Service
public class OrderService {
@Autowired
private ApplicationContext applicationContext; // 컨테이너 자체를 주입
public void createReport() {
// 컨테이너에서 직접 꺼냄
ReportGenerator generator =
applicationContext.getBean(ReportGenerator.class);
generator.generate();
}
}
Spring 컨테이너에 코드가 직접 의존하게 되어 테스트가 어려워지므로 비권장입니다.
세 방법 비교
방법 권장도 이유
| ObjectProvider | ✅ 가장 권장 | Spring 표준, 테스트 용이 |
| @Lookup | △ 상황에 따라 | 클래스가 abstract여야 함 |
| ApplicationContext | ❌ 비권장 | 컨테이너 직접 의존, 테스트 어려움 |
전체 Scope 한 눈에 비교
생존 기간 (짧음 → 김)
Prototype Request Session Singleton
│ │ │ │
│ 요청 1번 로그인~ 앱 실행~
매번 동안 유지 로그아웃 앱 종료
새 객체 동안 유지 동안 유지
사용 예:
독립 상태 요청 추적 장바구니 Service
필요한 객체 로그 세션 데이터 Repository
면접 답변 템플릿
"Singleton이 Prototype을 주입받으면 어떤 문제가 있나요?"
"Singleton Bean은 앱 시작 시 1번만 생성되기 때문에, 주입 시점에 Prototype Bean도 딱 1개만 생성되어 고정됩니다. 이후 Prototype Bean이 필요할 때마다 새 객체가 생성되길 기대하지만, 실제로는 처음 주입된 객체만 계속 재사용되어 사실상 Singleton처럼 동작합니다. 이를 해결하려면 ObjectProvider를 사용해 필요한 시점마다 컨테이너에서 새 인스턴스를 꺼내오도록 해야 합니다."
'Backend > Spring Boot' 카테고리의 다른 글
| Builder Pattern (0) | 2026.06.06 |
|---|---|
| DAO, DTO, VO, Entity (0) | 2026.06.06 |
| SpringBoot 내부동작 (0) | 2026.06.06 |
| [Spring Boot] 공식문서 번역 - RESTful 웹 빌드하기 (5) | 2025.02.04 |
| [Spring Boot] 스프링을 처음할때 보기 좋은 공식문서 순서 (3) | 2025.01.08 |