35강을 듣다 보면 이런 생각이 든다.
“Rq는 싱글톤이어도 된다면서?”
“컨트롤러도 하나인데 요청이 동시에 들어오면 섞이는 거 아니야?”
“getActor() 같은 메서드가 여러 번 동시에 실행되면 위험한 거 아냐?”
이 글에서는 이 질문들을 초보자 기준으로 하나씩 정리한다.
먼저 결론을 정확하게 적어두자.
Rq는 ‘조건부로’ 싱글톤이어도 된다.
그리고 지금 구조에서는 동시 요청이 와도 섞이지 않는다.
단, 왜 괜찮은지 이유를 이해하지 않으면
“되는 것처럼 보이는데 사실 위험한 코드”를 만들기 쉽다.
Spring 서버에서 가장 많이 오해하는 지점이다.
@RestController
public class ApiV1PostController {
}
컨트롤러가 하나라는 말은,
가 아니다.
즉,
컨트롤러 객체 1개
├─ Thread-1 → write()
├─ Thread-2 → delete()
├─ Thread-3 → modify()
👉 객체 수와 동시 실행은 전혀 다른 개념이다.
요청이 3개 동시에 들어오면,
같은 메서드 코드가 스레드별로 동시에 실행된다.
public Member getActor() {
String headerAuthorization = req.getHeader("Authorization");
if (headerAuthorization == null || headerAuthorization.isBlank())
throw new ServiceException("401-1", "Authorization 헤더가 존재하지 않습니다.");
if (!headerAuthorization.startsWith("Bearer "))
throw new ServiceException("401-2", "Authorization 헤더가 Bearer 형식이 아닙니다.");
String apiKey = headerAuthorization.substring("Bearer ".length()).trim();
Member member = memberService
.findByApiKey(apiKey)
.orElseThrow(() -> new ServiceException("401-3", "API 키가 유효하지 않습니다."));
return member;
}
👉 코드는 같지만 실행 흐름은 완전히 분리된다.

String headerAuthorization
String apiKey
Member member
이 변수들은:
👉 지역변수는 동시 실행돼도 섞일 수 없다.
private final HttpServletRequest req;
이 req는 진짜 request 객체가 아니다.
즉,
👉 같은 req 필드를 써도 실제 데이터는 분리된다.
딱 한 가지 경우다.
요청 데이터를 필드(전역 변수)에 저장할 때
❌ 위험한 예시:
@Component
public class Rq {
private String apiKey; // ❌ 공유 필드
public Member getActor() {
this.apiKey = req.getHeader("Authorization");
...
}
}
이러면:
👉 이 경우에는 @RequestScope가 필요하다.
@Component
@RequestScope
public class Rq {
private Member actor; // 요청별 상태
}
@Component
public class Rq {
private final HttpServletRequest req;
public Member getActor() {
return ...
}
}
👉 이 경우 싱글톤이어도 안전하다.
같은 메서드가 동시에 실행돼도
요청 데이터가 지역변수와 ThreadLocal 기반 request에서만 사용되면
동시 요청은 절대 섞이지 않는다.
이 강의의 진짜 핵심은 이 문장이다.
“싱글톤이 위험한 게 아니라,
싱글톤에 요청 상태를 저장하는 게 위험하다.”