티스토리 뷰
728x90
반응형
기본 개념
- 기본적으로 스프링 빈은 싱글톤(@Singleton) 이라서 애플리케이션 시작 시 한 번 생성되고 컨테이너가 내려갈 때까지 유지됩니다.
- 하지만 웹 애플리케이션에서는 요청마다 다른 값을 유지해야 하는 경우가 많습니다.
(예: 로그인 사용자 정보, 요청 ID, 트랜잭션 관련 데이터 등) - 이럴 때 @RequestScope를 사용하면 요청 단위로 새로운 빈을 생성할 수 있습니다.
사용 예시
- /test API를 두 번 호출하면 매번 새로운 requestId가 생성됩니다.
- 왜냐하면 RequestInfo 빈이 요청마다 새로 만들어지기 때문이에요.
@Component
@RequestScope
class RequestInfo {
val requestId: String = UUID.randomUUID().toString()
}
@RestController
class TestController(
private val requestInfo: RequestInfo
) {
@GetMapping("/test")
fun test(): String {
return "Request ID: ${requestInfo.requestId}"
}
}
동작 방식
- @RequestScope는 내부적으로 Spring AOP의 프록시(proxy) 를 사용합니다.
- 싱글톤 빈 안에서 @RequestScope 빈을 주입받을 수 있는 이유는, 스프링이 실제 객체 대신 프록시 객체를 주입해주고, 요청이 들어올 때 해당 프록시가 알맞은 요청 스코프 객체로 바인딩해주기 때문이에요.
주의할 점
- Web Application Context에서만 동작합니다. (즉, spring-web 환경이 필요)
→ 일반 애플리케이션에서는 사용할 수 없음. - 스레드 세이프(thread-safe) 하지 않습니다. 요청 단위로만 보장되기 때문에, 동시에 여러 요청이 들어오면 각 요청마다 별도의 인스턴스가 생성됩니다.
- @RequestScope(proxyMode = ScopedProxyMode.TARGET_CLASS) 같이 프록시 모드를 지정하는 경우가 많습니다. (@RequestScope 자체가 @Scope(value = WebApplicationContext.SCOPE_REQUEST, proxyMode = ScopedProxyMode.TARGET_CLASS)의 축약형)
왜 사용하는가?
- 싱글톤 빈에 요청 단위 데이터를 저장하면 안 되기 때문
- 스프링 빈은 기본적으로 싱글톤 → 모든 요청에서 공유됨.
- 요청마다 달라지는 데이터를 싱글톤에 저장하면 동시성 문제 발생.
- 따라서 요청마다 새 인스턴스를 주는 @RequestScope가 필요.
- 코드의 책임을 명확히 분리
- "요청 단위 데이터 관리"를 별도 빈으로 분리해서, 컨트롤러/서비스가 비즈니스 로직에 집중할 수 있음.
- AOP, 로깅, 트랜잭션 맥락 관리에 유용
- 요청 단위로 고유한 ID, 사용자 정보, 트레이싱 데이터 등을 묶어 관리 가능.
대표적인 사용 사례
1) 요청 ID 로깅 (트레이싱)
@Component
@RequestScope
class RequestContext {
val requestId: String = UUID.randomUUID().toString()
}
@RestController
class TestController(
private val context: RequestContext
) {
@GetMapping("/ping")
fun ping(): String {
return "Request ID = ${context.requestId}"
}
}
- 요청마다 고유한 requestId가 발급 → 로그에 찍어서 트레이싱 가능.
2) 사용자 인증 정보 보관
@Component
@RequestScope
class UserContext(
val userId: String = SecurityContextHolder.getContext().authentication.name
)
@Service
class OrderService(
private val userContext: UserContext
) {
fun createOrder(): String {
return "User ${userContext.userId} 주문 생성"
}
}
- 컨트롤러마다 매번 SecurityContextHolder에서 사용자 정보를 꺼낼 필요 없음.
- UserContext 빈을 주입받아서 사용 → 깨끗한 코드 유지.
3) 요청 단위 캐싱
@Component
@RequestScope
class RequestCache {
val cache: MutableMap<String, Any> = mutableMapOf()
}
@Service
class ProductService(
private val requestCache: RequestCache
) {
fun getProduct(id: String): Any {
return requestCache.cache.getOrPut(id) {
// DB 호출 or 외부 API 호출
loadProduct(id)
}
}
}
- 같은 요청 내에서 반복적으로 같은 데이터를 조회하면 → 요청 단위 캐시로 성능 최적화 가능.
4) 에러 핸들링/응답 공통 구조
@Component
@RequestScope
class ErrorCollector {
val errors: MutableList<String> = mutableListOf()
}
- 요청 처리 중 발생한 여러 검증 에러를 모아서 컨트롤러 응답에 담을 때 사용.
728x90
반응형
'Spring' 카테고리의 다른 글
| [Spring] DispatcherServlet 동작 흐름 (0) | 2025.05.02 |
|---|---|
| [Spring] 예외 처리 우선 순위 (0) | 2025.05.01 |
| [Spring] @Valid vs. @Validated (0) | 2025.04.29 |
| [Spring Boot] OpenFeign 쿼리스트링 객체 매핑 (0) | 2025.02.24 |
| [Spring] @ModelAttribute (0) | 2025.01.30 |
반응형
300x250