🧩 BE

커스텀 어노테이션으로 인증정보 쉽게 주입받기

Date
2024/12/18
Category
Web
Tag
Spring Security
Detail

1. 커스텀 어노테이션 생성

우선 컨트롤러 파라미터에 사용할 어노테이션을 정의한다.
@Target(ElementType.PARAMETER) // 파라미터에 사용 @Retention(RetentionPolicy.RUNTIME) // 런타임까지 유지 public @interface CurrentUser { }
Java
복사

2. ArgumentResolver 구현

HandlerMethodArgumentResolver를 구현한다.
Spring Security의 SecurityContextHolder에서 인증 정보를 꺼내와서 컨트롤러 파라미터에 넘겨주는 핵심 로직이다.
@Component public class CurrentUserArgumentResolver implements HandlerMethodArgumentResolver { @Override public boolean supportsParameter(MethodParameter parameter) { // 파라미터에 @CurrentUser가 붙어 있고, 타입이 User(엔티티 또는 DTO)인지 확인 return parameter.hasParameterAnnotation(CurrentUser.class) && parameter.getParameterType().equals(User.class); } @Override public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer, NativeWebRequest webRequest, WebDataBinderFactory binderFactory) { Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); // 인증 정보가 없거나 익명 사용자인 경우 null 반환 if (authentication == null || authentication instanceof AnonymousAuthenticationToken) { return null; } // Principal에 담긴 객체를 반환 (보통 UserDetails를 상속받은 객체) return authentication.getPrincipal(); } }
Java
복사

3. WebConfig에 Resolver 등록

Resolver 동작할 수 있도록 Spring 설정에 추가
@Configuration @RequiredArgsConstructor public class WebConfig implements WebMvcConfigurer { private final CurrentUserArgumentResolver currentUserArgumentResolver; @Override public void addArgumentResolvers(List<HandlerMethodArgumentResolver> resolvers) { resolvers.add(currentUserArgumentResolver); } }
Java
복사

4. 컨트롤러에서 사용

기존 방식
@GetMapping("/me") public ResponseEntity<UserResponse> getMyProfile() { // 1. SecurityContext에서 Authentication 객체를 꺼냄 Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); // 2. Principal 정보를 가져와서 내가 정의한 User 객체로 형변환 // (이때 익명 사용자인지, null인지 체크하는 로직이 추가되어야 함) User user = (User) authentication.getPrincipal(); return ResponseEntity.ok(UserResponse.from(user)); }
Java
복사
커스텀 어노테이션 적용 방식
@RestController @RequestMapping("/api/profile") public class ProfileController { @GetMapping("/me") public ResponseEntity<UserResponse> getMyProfile(@CurrentUser User user) { // 별도의 캐스팅이나 SecurityContext 접근 없이 바로 User 객체 사용 가능 return ResponseEntity.ok(UserResponse.from(user)); } }
Java
복사

Reference