새로운 내용을 공부할 때
새로운 내용의 공부를 시작할 때 용어의 정의를 이해하지 못하거나 정확하게 알지 못한다면 그 용어가 포함된 문장을 이해하지 못합니다.
작은 단어 하나가 내용을 이해하지 못하게 하기 때문에 용어를 정확하게 이해하는 것이 중요합니다.
필터 사용법
목표 D-day : 39 일 오늘은 필터를 사용하기 위한 방법을 학습해보려고합니다.
필터 사용 방법
- 필터 등록 하기
- 필터 적용 범위 지정
- 필터와 전처리, 후처리 방법
필터 등록 하기
스프링 부트에서 필터를 등록하는 방법은
- web.xml 사용하기
@WebFilter
애노테이션 추가- 필터를 빈으로 등록하기(
@Bean
,@ComponentScan
등) FilterResigterBean
를 스프링 빈으로 등록
스프링 부트로 편하게 등록하는 것이 목적이므로 2~4번으로 정리해보겠습니다.
@WebFilter
@WebFilter
를 사용해서 필터 적용하는 방법은 설정 클래스 위에 @ServletComponentScan
을 추가하면 등록이 가능합니다
@SpringBootApplication
@ServletComponentScan
public class SendboxApplication {
public static void main(String[] args) {
SpringApplication.run(SendboxApplication.class, args);
}
}
스프링 부트에 사용할 필터에 @WebFilter
를 추가하고 Filter
를 구현합니다.
@WebFilter(urlPatterns = "/*")
@Order(2)
@Slf4j
public class LoginFilter implements Filter {
@Override
public void doFilter(final ServletRequest servletRequest, final ServletResponse servletResponse,
final FilterChain filterChain) throws
IOException,
ServletException {
HttpServletRequest request = (HttpServletRequest)servletRequest;
HttpServletResponse response = (HttpServletResponse)servletResponse;
// HTTP 요청에 대한 로직 수행
log.info("Request URI: {}", request.getRequestURI());
log.info("Request Method: {}", request.getMethod());
// 다음 필터로 요청을 전달
filterChain.doFilter(servletRequest, servletResponse);
// HTTP 응답에 대한 로직 수행
log.info("Response Status: {}", response.getStatus());
}
}
@WebFilter
를 사용하는 경우 체이닝 순서를 @Order(int order)
를 사용하여 지정할 수 있습니다.
다만, 필터가 여러 개라면 체이닝 순서를 파일 하나로 관리하는 것보다 한 눈에 파악하기 어렵다는 단점이 있습니다.
#### FilterResigterBean
@Configuration(proxyBeanMethods = false)
public class FilterConfiguration {
@Bean
public FilterRegistrationBean<Filter> loginFilter() {
FilterRegistrationBean<Filter> registrationBean = new FilterRegistrationBean<>();
registrationBean.setFilter(new LoginFilter());
registrationBean.addUrlPatterns("/*");
registrationBean.setOrder(1);
return registrationBean;
}
@Bean
public FilterRegistrationBean<Filter> loggingFilter() {
FilterRegistrationBean<Filter> registrationBean = new FilterRegistrationBean<>();
registrationBean.setFilter(new LoginFilter());
registrationBean.addUrlPatterns("/*");
registrationBean.setOrder(2);
return registrationBean;
}
}
이렇게 필터가 여러 개라면 이 방식으로 사용하면 필터 설정 파일로 작성하여 체이닝 순서를 한 눈에 확인할 수 있습니다.
필터 범위 지정하기
필터 범위를 지정하는 방법은 총 3가지 입니다.
- urlPatterns: 특정 URL 패턴에 따라 필터를 적용하는 방법.
- dispatcherTypes: 요청을 처리하는 디스패처 타입에 따라 필터를 적용하는 방법. 예를 들어,
REQUEST
,FORWARD
,INCLUDE
,ERROR
등 - servletNames: 특정 서블릿 이름에 따라 필터를 적용하는 방법.
3번은 스프링 부트를 사용할 경우 프론트 컨트롤러인 디스패처 서블릿 하나만 등록되므로 1,2번만 정리하겠습니다.
urlParttern
필터를 특정 URL 패턴에만 적용하는 것이 목적인 경우에 사용합니다.
인증, 로깅, 인가 등 특정 리소스에 접근한 필터를 적용할 때 사용합니다.
- 인증, 인가가 필요한 리소스에 적용하는 경우 :
/admin/*
,/api/delete/
등 으로 인증된 사용자만 접근할 수 있도록 제한합니다. - 로깅 목적인 필터 적용:
/api/*
로 시작하는 URL에만 필터를 적용하여, 특정 API 호출에 대한 로깅을 추가하고 싶을 때 사용 - 리스소 파일 필터링 제외:
/static/
과 같이 정적 파일 경로에 대해서는 로깅이나 인증이 필요없으므로, 경로에서 제외할 수 있습니다.
DispatcherTypes
요청을 처리하는 디스패처 타입에 따라 필터를 선택적으로 적용할 수 있습니다, 물론 여러 개 선택도 가능합니다.
기본 적으로 사용자의 요청만 되어있습니다.
- REQUEST : 클라이언트의 직접 요청에만 필터를 적용하고 싶을 때 사용합니다.
- FORWARD : 서블릿에서 포워딩으로 사용될 때 특정 데이터를 가공하거나 , 뷰 렌더링 직전에 헤더를 추가할 때 사용합니다.
- ERROR : 에러가 발생한 요청에만 필터를 적용하여 , 특정 로깅 처리나 사용자 메세지 추가가 필요한 경우에 사용합니다.
필터 전처리, 후처리
필터는 사용자의 요청이나 응답에서 불필요한 데이터를 여과하는 장치로 생각됩니다.
HttpServletRequestWrapper
,HttpServletResponseWrapper
을 사용하면 다음과 같은 장점이 있습니다.
불변객체
만약 사용자의 요청에 데이터 파라미터를 수정하거나 응답 파라미터를 개발자가 직접 수정하게 되면 불변이 깨지게 됩니다.
공식 문서에도 작성되어있습니다
This class implements the Wrapper or Decorator pattern. Methods default to calling through to the wrapped request object.
만약 개발자가 중간에 원본 요청 객체와 응답 객체를 수정하는 경우 다른 로직에도 영향을 받을 수 있습니다.
서블릿 컨테이너는 HttpServletRequestWrapper
,HttpServletResponseWrapper
을 제공하여 요청, 응답 객체를 래핑한 객체를 반환하고 필요한 정보를 수정해서 불변 객체를 유지할 수 있도록 도와줩니다.
테스트 편하다
필터는 doFilter
메서드 내에 요청 로직, 체이닝 호출, 응답 로직이 하나에 들어있습니다.
필터를 테스트 준비하려면 3가지가 필요합니다.
public void doFilter(final ServletRequest servletRequest,
final ServletResponse servletResponse,
final FilterChain filterChain)
요청 로직만 테스트하기 위해서 불필요한 파라미터를 준비해야하지만
public class AuthenticatedRequestWrapper extends HttpServletRequestWrapper {
public AuthenticatedRequestWrapper(HttpServletRequest request) {
super(request);
}
// 세션에서 인증 여부 확인
public boolean isAuthenticated() {
HttpSession session = this.getSession(false);
return session != null && session.getAttribute("user") != null;
}
}
사용자 인증을 하는 로직일 경우에 단위 테스트로 처리할 수 있으며 필터는 필요한 로직에 대해 위임만 하면 됩니다.
재사용 가능
urlParttern
으로 요청에 대한 처리는 다르지만 응답은 동일한 로직을 실행해야한다면 각각 구현해야합니다.
HttpServletResponseWrapper
을 사용하면 응답 변환에 대한 로직을 재사용 가능하며, 유지보수도 편해집니다.
댓글남기기