이번 포스팅은 스프링 프레임워크에서 중요한 기능 중 하나인 인터셉터(Intercepter)와 필터 (Filter)에 대해서 알아보겠습니다.
필터와 인터셉터 이 두 가지는 비슷하면서도 전혀 다른 역할을 하는 클라이언트의 요청을 처리하는 과정에서 특정 작업을 수행하고자 할 때 사용되는 강력한 도구들입니다.
필터(Filter)의 역할과 특징?
필터는 스프링 프레임워크에서 요청(Request)과 응답(Response)을 가로채버린 후, 특정 작업을 처리하는 컴포넌트입니다.
필터(Filter)는 디스패처 서블릿(Dispatacher Servlet)에 요청이 전달되기 전 / 후에 url 패턴에 맞는 모든 요청에 대하여 전역적인 작업을 처리하는 데 사용합니다.
여기서 디스패처 서블릿은 스프링 컨테이너(Spring Container)의
가장 앞단에 존재하는 프론트 컨트롤러(Front Controller)입니다.
프론트 컨트롤러(Front Controller)는 웹 애플리케이션의 진입점으로서 클라이언트로부터 들어오는 모든 요청을 처리하는 중앙집중적인 컨트롤러를 의미합니다.
그래서 디스패처 서블릿은 스프링에서 프론트 컨트롤러의 역할을 수행한다고 이해하시면 됩니다.
또한 이 디스패처 서블릿은 클라이언트로부터 들어오는 모든 요청을 가로채서 처리합니다.
(디스패처 서블릿에 대해서는 추후 포스팅 예정입니다.)
그래서 필터는 스프링의 영역 밖에서 처리가 된다고 이해하시면 됩니다.
필터의 역할
주로 보안, 로깅, 인코딩 변환, 세션 관리 등의 전역적인 작업을 처리하는 데 사용합니다.
서블릿(Servlet) 기반의 웹 애플리케이션에서도 사용되지만,
스프링에서는 서블릿 필터보다 더 많은 기능과 유연성을 제공합니다.
또한 필터(Filter)는 일반적으로 서블릿 컨테이너에서 관리되며, 여러 개의 필터를 체인으로 연결하여 사용할 수 있습니다.
필터의 특징
- 필터는 서블릿 컨테이너 내부에서 동작하며, 웹 애플리케이션 전역에서 적용되어 중복되는 작업을 방지합니다.
- 여러 개의 필터를 체인으로 연결할 수 있으며, 각 필터는 순서에 따라 순차적으로 실행됩니다.
- 클라이언트 요청 전처리와 서브릿 응답 후처리를 수행하며, 요청과 응답에 대한 변형이 가능합니다.
- 필터는 서블릿에 의존하지 않고 독립적으로 동작하기 때문에, 프레임워크나 서블릿 컨테이너와 상관없이 사용 가능합니다.
필터(Filter)의 구현과 생명주기
필터를 구현하려면 javax.servlet.Filter 인터페이스를 구현해야 하고, 다음의 세 가지 메서드를 제공합니다.
1. init()
필터를 초기화하는 메서드입니다. 필터 인스턴스(Instance) 생성 시 단 한 번만 호출합니다.
필요에 따라 초기화 작업을 수행할 수 있습니다.
void init(FilteringConfig filterConfig) throws ServletException {
// 초기화 작업
}
예시
init() 메서드를 통해서, 어떻게 사용할 수 있는지 알아보겠습니다.
FilterConfig에 MyFilter 클래스를 등록한 후 addInitParameter("name", "Code:J")라고 초기화 매개변수를 설정해 줄 수 있습니다.
이렇게 하면 init() 메서드에서 해당 초기화 매개변수의 값을 사용할 수 있습니다.
이처럼 초기화 매개변수를 사용하여 필터의 동작을 유연하게 변경할 수 있습니다.
이를 통해 서버 애플리케이션의 동작을 설정 파일이나 코드 변경 없이도 다양하게 조정할 수 있습니다.
주로 필터 초기화에 필요한 설정 작업들을 수행합니다.
필터가 필요한 설정 값을 읽어서 출력하거나, 외부 리소스를 초기화하는 등의 작업을 할 수 있습니다.
위의 예시는 filterConfig.getFilterName() 메서드로 필터의 이름을 가져오고,
filterConfig.getInitParameter("paramName")을 이용하여 초기화 매개변수에 접근할 수도 있습니다.
2. doFilter()
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
// 전처리 작업
// ...
chain.doFilter(request, response); // 다음 필터 또는 서블릿 호출
// 후처리 작업
// ...
}
예시
위의 예시는 간단한 필터를 구현하였습니다.
이는 클라이언트 요청이 들어올 때마다 파라미터 name의 값을 대문자로 변환하여 다음 필터 또는 서블릿으로 전달해 줍니다.
http://localhost:8080/example? name=codeJ라고 url에 만들어 보겠습니다.
따라서 '/myInfo? name=CodeJ'로 요청이 들어올 경우, 다음 서블릿으로 전달되는 파라미터는 'CODEJ'로 변경됩니다.
이와 같이 필터를 활용하면 요청에 대한 공통적인 전, 후처리 작업을 수행할 수 있습니다.
3. destroy()
public void destroy() {
// 필터 해제 시 처리할 작업 (옵션)
}
예시
destroy() 메서드는 일반적으로 서블릿 컨테이너가 종료될 때 또는 웹 애플리케이션이 다시 로드될 때 호출되기 때문에,
로그를 콘솔에 출력해도 실제로 눈으로 확인하기는 어렵습니다.
왜냐하면 서블릿 컨테이너가 종료되는 경우, 애플리케이션 서버가 종료되거나 재시작되어 로그를 콘솔에 표시하는 것이 중단될 수 있기 때문입니다.
만약 정말로 보고 싶다면, 로그를 파일에 기록하거나 외부 로그 뷰어를 사용한다면 로그를 볼 수 있습니다.
이렇게 필터의 생명 주기가 끝났을 때 호출되는 메서드입니다.
필터에서 사용한 리소스를 해제하는 등의 정리 작업을 수행할 수 있습니다.
위의 예시에서는 init() 메서드에서 tempFile라는 임시 파일 리소스를 할당하고,
destory() 메서드에서 필터가 소멸될 때 해당 tempFile을 삭제하여 리소스를 해제할 수도 있습니다.
리소스를 사용한 후 반드시 해제하는 것이 중요합니다.
이를 통해 메모리 누수 같은 문제를 방지하고, 애플리케이션의 안정성과 성능을 유지할 수 있습니다.
인터셉터(Interceptor)란?
인터셉터는 스프링 프레임워크에서 제공하는 기능으로, 위의 클라이언트의 요청과 디스패처 서블릿 사이의 가 아닌,
클라이언트의 요청과 컨트롤러(핸들러) 사이에 위치하여 요청을 가로채고,
요청 전, 후에 추가적인 작업을 수행할 수 있도록 도와주는 역할을 합니다.
일반적으로 웹 애플리케이션은 사용자의 요청을 받아 처리하는 Controller(컨트롤러)가 있습니다.
이러한 컨트롤러에는 사용자가 요청한 기능을 처리하는 비즈니스 로직이 포함되어 있습니다.
하지만 이렇게 구현된 컨트롤러 만으로는 공통적인 작업을 처리하기 어려운 경우가 있습니다.
만약 50개의 컨트롤러에 같은 로직이 들어가야 한다면, 50개의 컨트롤러에 같은 로직을 넣어주어야 합니다.
하지만 개발자는 같은 코드를 여러 번 사용하기 싫어합니다.
그래서 나온 것이 바로 인터셉터입니다.
예를 들어, 모든 요청에 대해 로그인 상태를 체크하거나 특정 페이지에 대한 권한을 확인해야 한다면,
이런 작업을 모든 컨트롤러의 메서드에 중복해서 구현해야 하는데, 이것은 너무 비효율적인 일입니다.
인터셉터는 이러한 공통적인 작업을 처리하기 위해 사용됩니다.
크게 나눠보면
컨트롤러를 호출하기 전 (PreHandle)
컨트롤러가 처리한 후 (PostHandle)
뷰가 렌더링 된 후 (afterCompletion)
에 동작하는 콜백 메서드를 제공합니다. 우리는 이러한 콜백 메서드를 구현하여 전역적으로 사용해야 하는 작업을 일괄로 처리할 수 있습니다.
인터셉터 구현하기
인터셉터를 구현하려면 org.springframework.web.servlet.HandlerInterceptor 인터페이스를 구현해주어야 합니다.
이 인터페이스는 필터와 마찬가지로 세 가지의 메서드를 제공합니다.
1. preHandle
컨트롤러의 메서드가 실행되기 전에 호출되는 메서드입니다.
요청 전에 추가적인 처리를 하고, true를 반환하면 다음 단계로 진행되고,
반대로 false를 반환하면 컨트롤러를 실행하지 않고 처리를 종료합니다.
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
// 전처리 작업 수행
// 예: 로그인 체크, 권한 확인 등
return true; // true를 반환하면 다음 단계로 진행, false를 반환하면 컨트롤러 실행하지 않음
}
2. postHandle
컨트롤러의 메서드가 실행된 후, 뷰가 렌더링 되기 전에 호출되는 메서드입니다.
컨트롤러의 실행 이후 추가적인 작업을 처리할 수 있습니다.
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
ModelAndView modelAndView) throws Exception {
// 후처리 작업 수행
// 예: 뷰 모델 데이터 수정 등
}
3. afterCompletion
뷰의 렌더링이 완료된 후에 호출되는 메서드입니다.
인터셉터에서 수행한 작업의 마무리를 처리하는 곳에 사용됩니다.
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)
throws Exception {
// 뷰 렌더링 완료 후 작업 수행
// 예: 리소스 정리, 로깅 등
}
인터셉터 등록하기
인터셉터를 사용하기 위해서는 스프링 설정 파일에서 인터셉터를 등록해야 합니다.
XML 설정을 사용하는 경우
<mvc:interceptors>
<mvc:interceptor>
<mvc:mapping path="/*" /> <!-- 인터셉터를 적용할 URL 패턴 -->
<bean class="com.example.MyInterceptor" /> <!-- 인터셉터 클래스 등록 -->
</mvc:interceptor>
</mvc:interceptors>
Java Config를 사용하는 경우
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@Configuration
public class WebConfig implements WebMvcConfigurer {
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new MyInterceptor())
.addPathPatterns("/*"); // 인터셉터를 적용할 URL 패턴
}
}
인터셉터를 사용하는 예시
인터셉터 클래스를 생성합니다.
인터셉터를 등록하는 설정 클래스를 생성합니다.
로그인 체크가 필요한 컨트롤러를 생성합니다.
뷰(HTML)를 생성합니다.
위의 예시에서는 인터셉터(LoginInterceptor)를 사용하여 클라이언트로부터 '/secured/**' 패턴의 URL 요청이 있을 때 로그인 여부를 체크하도록 구현하였습니다. 만약 사용자가 로그인하지 않은 채로 페이지 요청을 한다면,
인터셉터에서 로그인 체크를 수행하여 로그인되지 않았으므로 '/login' 페이지로 리다이렉트 합니다.
이렇게 인터셉터를 사용하면 간단하게 로그인 체크와 같은 공통 작업을 처리할 수 있습니다.
이렇게 공통 작업을 인터셉터에서 처리하면 중복 코드를 줄이고, 유지보수성을 높일 수 있습니다.
실제 프로젝트에서는 인터셉터를 이용하여 다양한 공통 작업을 처리할 수 있으며, 필요에 따라 다양한 로직을 추가할 수 있습니다.
'JAVA' 카테고리의 다른 글
[JS] let 과 const 차이점 with. var 키워드의 문제점 (0) | 2023.08.02 |
---|---|
[Spring] 스프링 제어의 역전(IoC) 와 의존성 주입(DI) 완벽 이해하기 Feat.빈(Bean) (4) | 2023.08.01 |
[JAVA] java 개발자라면 한번 쯤은 겪을 ConcurrentModificationException (1) | 2023.07.30 |
[JAVA] 스레드(Thread), 스레드의 동시성, 멀티 스레드(Multi-Thread) (5) | 2023.07.30 |
[JAVA] 일반 for문과 향상된 for문의 차이와 진실 Feat.Iterator (5) | 2023.07.29 |