Spring 과 정적 리소스

now_iz·2021년 8월 6일
0

문제

@Component
@RequiredArgsConstructor
public class AuthInterceptor implements HandlerInterceptor {

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws UnauthorizedException {
        try {
            if (isNeedToAuth((HandlerMethod)handler)) {
                String userIdBySession = getUserIdBySession(request);
                String userIdByPath = getUserIdByPathVariable(request);
                if (!userIdBySession.equals(userIdByPath)) {
                    throw new UnauthorizedException();
                };
            }
            return true;
        } catch (Exception e) {
            throw new UnauthorizedException(e);
        }
    }
    // 기타 메소드 생략
}
@Configuration
@RequiredArgsConstructor
public class WebConfig implements WebMvcConfigurer {

    @Autowired
    private AuthInterceptor authInterceptor;

    @Override
    public void addInterceptors(InterceptorRegistry registry){
        registry.addInterceptor(authInterceptor);
    }
}

대략 @AuthRequired 어노테이션이 붙은 MethodHandler 에 로그인 세션 검증을 추가하는 인터셉터를 만들었다!
그런데 문제는 일반적으로 404 에러가 발생해야 하는 "서비스하지 않는 URL"을 요청했을 때,
ResourceHttpRequestHandlerHandlerMethod 로 타입변환 할 수 없다는 에러가 발생한다.
별다른 설정이 없는 한, 서버 에러를 띄우게 된다!

java.lang.ClassCastException:
class org.springframework.web.servlet.resource.ResourceHttpRequestHandler cannot be cast to class org.springframework.web.method.HandlerMethod (org.springframework.web.servlet.resource.ResourceHttpRequestHandler and org.springframework.web.method.HandlerMethod are in unnamed module of loader 'app')



정적 리소스 요청

스프링 2.X 프레임워크에서 정적 리소스를 요청할 때에도 인터셉터가 호출되기 때문에,
인터셉터에 전달되는 핸들러가 모두 HandlerMethod가 아닐 수 있다는 것이다!

API 로 등록되지 않은 모든 URL을 정적 리소스를 요청하는 URL 로 바꾸는 것은 성능 향상을 위해 2.X 부터 바꾼 것으로 추측해본다.

정적 리소스

  • 클라이언트로부터 요청이 들어왔을 때, 요청에 대한 리소스가 이미 만들어져 있어 그대로 응답하는 경우
  • html, css, js, image 등

ResourceHttpRequestHandler

  • 아까 우리가 HandlerMethod 로 들어올 것이라고 예상했으나, 실제로 받았던 이 핸들러가 바로 '정적 리소스를 처리하는 핸들러' 이다!
  • 브라우저가 최초로 정적리소스를 요청하는 경우에는 응답으로, 200 응답 코드에, Last-modified 헤더를 함께 보낸다.
  • 브라우저는 이 정보를 기억하고 있다가, 다음 요청에서는 If-Modified-Since 헤더를 포함해서 보낸다.
    • 서버는If-Modified-Since 헤더값과 리소스의 변경 시점이 같다면
      • 304 상태코드를 응답하면서 다시 리소스를 응답하지 않는다.
    • 서버는 If-Modified-Since 헤더값과 리소스의 변경 시점이 다르다면
      • 변경된 리소스와 200 상태코드를 응답하게 되면서 브라우저는 다시 응답의 Last-Modified 헤더값을 다음 요청의 If-Modified-Since 헤더값에 포함하여 전송한다.


해결

@Configuration
@RequiredArgsConstructor
public class WebConfig implements WebMvcConfigurer {

    @Autowired
    private AuthInterceptor authInterceptor;

    @Override
    public void addInterceptors(InterceptorRegistry registry){
        registry.addInterceptor(authInterceptor)
        	.addPathPatterns("/rest-api-root/**");;
    }
}

이렇게 addPathPatterns 를 이용해서, REST API 의 루트 URL 을 명시해주면, 정적 요청없이 잘 동작한다!



Reference

profile
👀

0개의 댓글