"리졸버(Resolver)"는 메서드의 파라미터 값을 어떻게 채울지를 결정하는 객체.
🎯 리졸버(Resolver)란?
컨트롤러에서 이런 코드
@GetMapping("/todos")
public ResponseEntity<?> getTodos(@AuthenticationPrincipal AuthUser user) {
...
}
여기서 AuthUser user 값은 Spring이 자동으로 채워줌.
그 역할을 하는 게 HandlerMethodArgumentResolver라는 인터페이스.
즉, "컨트롤러 메서드 파라미터 값을 어떻게 구할까?"를 해결(Resolve)하는 객체라서 리졸버(Resolver) 라고 부름.
✅ Spring Security 기본 Resolver 예시
@AuthenticationPrincipal은 Spring Security에서 제공하는 기본 리졸버가 있어서,
SecurityContextHolder.getContext().getAuthentication().getPrincipal()에서 값을 꺼내서 자동으로 넣어줌.
그래서 Security 기반에서는 무조건 @AuthenticationPrincipal이 가장 안정적으로 작동함.
✅ 내가 만들었던 커스텀 Resolver
@Auth + AuthUserArgumentResolver도 사실 커스텀 리졸버.
public class AuthUserArgumentResolver implements HandlerMethodArgumentResolver
직접 만든 리졸버는
- request.getAttribute("userId") 같은 값을 HttpServletRequest에서 가져왔고
- @Auth가 붙어 있는 파라미터를 보고 값을 만들어 넣어줬음.
✅ 결론 정리
리졸버 종류 | 어디서 값 가져옴 | 예시 |
Spring Security 기본 리졸버 | SecurityContextHolder → Authentication → Principal | @AuthenticationPrincipal |
커스텀 리졸버 | request attribute (request.setAttribute()) | @Auth + AuthUserArgumentResolver |
Spring MVC의 HandlerMethodArgumentResolver 전체 동작 흐름
🛤️ Spring MVC Argument Resolver 전체 흐름
1. 클라이언트가 요청을 보낸다.
- 예를 들면 /todos에 POST 요청 보냄.
2. DispatcherServlet이 요청을 받는다.
- Spring MVC의 모든 요청은 DispatcherServlet이 제일 먼저 받는다.
3. HandlerMapping을 통해 어떤 Controller, 어떤 메서드로 요청을 보낼지 찾는다.
- 예를 들어 TodoController#saveTodo() 같은 메서드를 찾아냄.
4. HandlerMethodArgumentResolver가 작동하기 시작한다.
"컨트롤러 메서드 파라미터에 어떤 값을 넣어줄까?" 를 결정하는 단계야.
Spring은 등록된 여러 ArgumentResolver 목록을 가지고 있는데,
요청에 필요한 파라미터마다 순서대로 supportsParameter()를 호출해서
- 이 파라미터를 내가 처리할 수 있나?
- 못하면 다음 Resolver에게 넘긴다.
✅ supportsParameter()가 true를 리턴하면 → resolveArgument()를 호출해서 실제 값을 생성해낸다.
5. Spring Security가 개입하는 경우
- @AuthenticationPrincipal을 붙이면
- Spring Security의 기본 AuthenticationPrincipalArgumentResolver가 작동함.
그 리졸버가
SecurityContextHolder.getContext().getAuthentication().getPrincipal()
여기서 Principal 객체(= 로그인한 사용자 정보)를 꺼내서 메서드 파라미터로 넘겨줌.
→ 그래서 Spring Security가 알아서 값을 넣어줄 수 있었던 것.
6. 커스텀 Resolver가 개입하는 경우
만약 커스텀 어노테이션 @Auth를 썼으면
- 만든 AuthUserArgumentResolver가 supportsParameter를 통해 잡아채고
- resolveArgument에서 직접 request.getAttribute() 같은 걸로 값을 채워줬어야 함.
🎯 요약
요청 → DispatcherServlet →
HandlerMapping → Controller 메서드 찾음 →
각각의 파라미터마다
1. 여러 ArgumentResolver 순회
2. supportsParameter()로 담당자 찾기
3. resolveArgument()로 실제 값 생성
→ Controller 메서드 실행
🔥 요약
- Resolver = Controller 파라미터를 어떻게 채울지 결정하는 객체
- Spring Security 기본 Resolver = Principal을 SecurityContextHolder에서 꺼내줌
- 커스텀 Resolver = request.getAttribute에서 수동으로 꺼냈음
🌟 1️⃣ Spring MVC + Spring Security 요청 처리 흐름
클라이언트 요청 → DispatcherServlet → FilterChain → Controller
✔️ 1. 클라이언트 요청
POST /todos
Authorization: Bearer eyJhbGci...
✔️ 2. DispatcherServlet이 FilterChain 호출
DispatcherServlet
↓
FilterChainProxy (Spring Security FilterChain)
✔️ 3. Spring Security FilterChain 동작
순서 예시
[일반 Filter]
→ JwtFilter (직접 만든 Filter → SecurityContextHolder.setAuthentication())
→ Security Filters
- SecurityContextPersistenceFilter
- UsernamePasswordAuthenticationFilter
- AuthorizationFilter
...
직접 만든 JwtFilter에서 이 코드가 실행됨:
SecurityContextHolder.getContext().setAuthentication(auth);
즉, SecurityContextHolder에 UsernamePasswordAuthenticationToken(authUser, null, authorities)가 들어감.
이게 이후 인증 정보로 계속 유지됨.
✔️ 4. DispatcherServlet → HandlerMapping → Controller 찾음
DispatcherServlet
↓
HandlerMapping
↓
@Controller 클래스, @RequestMapping 메서드 찾기
ex)
@PostMapping("/todos")
public ResponseEntity<?> saveTodo(@AuthenticationPrincipal AuthUser authUser, ...)
✔️ 5. HandlerMethodArgumentResolver 작동
메서드 파라미터마다
→ ArgumentResolver 리스트 순서대로 supportsParameter() 체크
기본 리스트 예시
- RequestParamMethodArgumentResolver (@RequestParam)
- PathVariableMethodArgumentResolver (@PathVariable)
- AuthenticationPrincipalArgumentResolver (@AuthenticationPrincipal ⭐)
- 커스텀 AuthUserArgumentResolver (❌ 지금은 등록 안 됨 → 패스)
- 기타...
✅ @AuthenticationPrincipal 붙은 파라미터 → AuthenticationPrincipalArgumentResolver에서 처리
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
Object principal = authentication.getPrincipal();
principal (= JwtFilter에서 set했던 AuthUser) 가 자동으로 파라미터에 주입됨. 👉 그래서 @AuthenticationPrincipal은 정상 작동
✔️ 6. 컨트롤러 호출
모든 파라미터가 주입되고 Controller#saveTodo() 호출
🌟 3️⃣ 정리
@Auth + AuthUser @AuthenticationPrincipal
동작 방식 | request attribute 직접 꺼냄 | SecurityContextHolder에서 Principal 꺼냄 |
Resolver | 직접 만든 AuthUserArgumentResolver | Spring Security 기본 AuthenticationPrincipalArgumentResolver |
Security 연동 | ❌ | ✅ 완벽 연동 |
🎯 요약
클라이언트
↓
DispatcherServlet
↓
FilterChainProxy (Security Filter)
↓
JwtFilter → SecurityContextHolder에 Authentication 세팅
↓
HandlerMapping → Controller 찾기
↓
HandlerMethodArgumentResolver → @AuthenticationPrincipal → SecurityContextHolder에서 principal 꺼내서 파라미터 주입
↓
Controller 실행
'코딩 > sparta TIL' 카테고리의 다른 글
TIL 59 : Docker 란 (1) | 2025.05.19 |
---|---|
Redis란 (1) | 2025.05.18 |
TIL 57 : 베이직 기술 면접 망함 - 죄송합니다. 튜터님 준비를 안하긴 했어요. (0) | 2025.05.14 |
Spring Plus 기본 과제 필수는 진행 완료 (0) | 2025.05.12 |
TIL 56 : Spring Security 다시 복습. 배워도배워도 리셋되는 내머리 (0) | 2025.05.12 |