삽질이였다.
출처
GenericFilterBean과 OncePerRequestFilter의 차이
https://g4daclom.tistory.com/115
OncePerRequestFilter에 대한 설명
https://junhyunny.github.io/spring-boot/once-per-request-filter/
DispatcherServlet과 SpringContainer
https://articles09.tistory.com/33
Sevlet Class Diagram
https://tinkerbellbass.tistory.com/1
스프링 시큐리티는 Filter를 사용해서 인증, 인가 작업을 해주는 프레임워크
필터의 개념이 부족해서 공부하던 중 필터는 ServletRequest와 ServletResponse를 사용해서 구현한다고 했다.
그런데 일부 스프링시큐리티 구현예제에서는 HttpServletRequest와 HttpServletResponse를 이용하고 있었다.
GenericFilterBean을 상속받은 Filter 클래스는 ServletRequest/Response 객체를 받고, OncePerRequestFilter에서는 HttpServletRequest/Response 객체를 받고 있었다.
HTTP 요청이 DispatcherServlet에 가기 전에 요청을 가로채서 필터 체인을 통해 처리할 수 있는 컴포넌트이다.
응답의 경우는 WAS에 가기 전에 가로채서 처리한다고 한다.
@Override
public void doFilter(final ServletRequest request, final ServletResponse response, final FilterChain chain) throws IOException, ServletException {
// do Filter 실행 전 로직
println("Before")
chain?.doFilter(request, response) ?: throw Exception()
// do Filter 실행 후 로직
println("After")
}
GenericFilterBean=> 모든 서블릿에 일관된 요청을 처리하기 위해 만들어진 것이OncePerRequestFilter이다.
public final void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain) throws ServletException, IOException {
if (request instanceof HttpServletRequest && response instanceof HttpServletResponse) {
HttpServletRequest httpRequest = (HttpServletRequest)request;
HttpServletResponse httpResponse = (HttpServletResponse)response;
String alreadyFilteredAttributeName = this.getAlreadyFilteredAttributeName();
boolean hasAlreadyFilteredAttribute = request.getAttribute(alreadyFilteredAttributeName) != null;
if (!this.skipDispatch(httpRequest) && !this.shouldNotFilter(httpRequest)) {
if (hasAlreadyFilteredAttribute) {
if (DispatcherType.ERROR.equals(request.getDispatcherType())) {
this.doFilterNestedErrorDispatch(httpRequest, httpResponse, filterChain);
return;
}
filterChain.doFilter(request, response);
} else {
request.setAttribute(alreadyFilteredAttributeName, Boolean.TRUE);
try {
this.doFilterInternal(httpRequest, httpResponse, filterChain); //이 메소드를 개발자가 커스텀한다.
} finally {
request.removeAttribute(alreadyFilteredAttributeName);
}
}
} else {
filterChain.doFilter(request, response);
}
} else {
throw new ServletException("OncePerRequestFilter just supports HTTP requests");
}
}
OncePerRequestFilter이 동작하면 서블릿 객체에 alreadyFilteredAttribute라는 값을 true로 넣어둔다.
그래서 다음에 포워딩이 발생해도 전에 서블릿 객체에서 해당 attribute값이 true인지를 확인하여 한번만 동작하는 것을 보장해준다.
만약에 filter가 실행된 적이 없으면 doFilterInternal 메소드를 호출하여 자신의 기능을 수행한다.
HttpServletRequest httpRequest = (HttpServletRequest) request;
HttpServletResponse httpResponse = (HttpServletResponse) response;
doFilter 메소드를 보면 ServletRequest 객체를 HttpServletRequest로 타입 변환을 시키는 것을 볼 수 있다.
ServletRequest를 상속받은게 HttpServletRequest이기 때문에 변환 가능하고, 요청 객체는 생각해보니 Filter 개념과는 별개였다...
쨌든 doFilter 메소드에서 HttpServletRequest로 형변환하고 속성 있는지 없는지 확인하기 때문에 doFilterInternal 은 HttpServletRequest/Response 를 파라미터로 받는 것이였다!!

DispatcherServlet은 전달받은 설정 파일을 이용해서 스프링 컨테이너를 생성한다. 스프링 컨테이너에는 HandlerMapping, HandlerAdapter, Controller, ViewResolver 등이 존재
인프런 강의 헛으로 들었네... 저번에도 이거 헷갈렸었는데 맨날 까먹음 레전드

필터에서 ServletRequest를 받는 이유는 해당 인터페이스를 상속받는 여러 인터페이스들이 있어서인 줄 알았는데 한 개밖에 없다고 한다.
왜 만들엇노
https://tinkerbellbass.tistory.com/1
위의 출처에도 적었지만 해당 블로그 글을 읽어보면, 나중에 Http보다 더 나은 프로토콜이 생겼을 때 유연성을 위해서 GenericServlet 클래스를 두었다고 한다.
OncePerRequestFilter의 doFilter에는 ServletRequest를 받아서 처리하고 있고, 내가 상속받아서 처리하는 건 doFilterInternal 메소드였기 때문에 HttpServletRequest를 받아야 했었다.