AOP 로 적용 @Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) public @interface HostOnly { } @Aspect @Component public class HostVerifier { private final AuthenticationContext authenticationContext; public HostVerifier(final AuthenticationContext authenticationContext) { this.authenticationContext = authenticationContext; } @Before("@annotation(com.woowacourse.gongcheck.application.HostOnly)") public void checkHost() { final Authority authority = authenticationContext.getAuthority(); if (!authority.equals(Authority.HOST)) { throw new UnauthorizedException("호스트만 입장 가능합니다."); } } } 문제점 @Valid 가 먼저 터짐 왜? AOP proxy 가 호출되기 전에 ArgumentResolver (JacksonMapper) 가 먼저 동작 인가가 먼저 터져야 함 Interceptor 로 적용 참고 @Override public boolean preHandle(final HttpServletRequest request, final HttpServletResponse response, final Object handler) throws Exception { if (CorsUtils.isPreFlightRequest(request)) { return true; } String token = AuthorizationTokenExtractor.extractToken(request) .orElseThrow(() -> new UnauthorizedException("헤더에 토큰 값이 정상적으로 존재하지 않습니다.")); String subject = jwtTokenProvider.extractSubject(token); authenticationContext.setPrincipal(subject); Authority authority = jwtTokenProvider.extractAuthority(token); if (HandlerMethod.class.isAssignableFrom(handler.getClass())) { authorize((HandlerMethod) handler, authority); } return HandlerInterceptor.super.preHandle(request, response, handler); } private void authorize(HandlerMethod handlerMethod, Authority authority) { if (handlerMethod.getMethodAnnotation(HostOnly.class) != null) { if (!authority.isHost()) { throw new UnauthorizedException("호스트만 입장 가능합니다."); } } } Documentation Mocking 처리 when(jwtTokenProvider.extractAuthority(anyString())).thenReturn(Authority.HOST);