dev-sohee 님의 블로그
웹 요청을 처리하는 4가지 기술 : Filter, AOP, Interceptor, Dispatcher Servlet 본문
자바 웹 개발을 하다보면, 공통적으로 처리해야 할 작업들이 많습니다. 예를 들어 로그인 관련(세션 관리), 권한 체크, XSS 방어, 페이지 인코딩 변환 등이 있습니다. 공통업무에 관련된 코드를 모든 페이지 마다 작성한다면 중복된 코드가 많아지게 되고 프로젝트 단위가 커질수록 서버에 부하를 줄 수도 있으며, 소스 관리도 힘들어질 것입니다. 그래서 공통된 부분은 따로 관리하는 것이 좋습니다. 이러한 공통 업무를 프로그램 흐름의 앞, 중간, 뒤에 추가하여 자동으로 처리할 수 있는 방법이 있는데 바로 Filter, AOP, Interceptor, Dispatcher Servlet입니다.
* Dispatcher Servlet
* Filter
* AOP(Aspect-Oriented Programming)
* Interceptor
# Dispatcher Servlet
Spring Framework의 핵심 구성 요소로, 클라이언트의 요청을 처리하고 적절한 뷰를 반환하는 역할을 합니다. MVC 패턴에서 "Controller"의 역할을 수행합니다.
- 동작 과정
1) 클라이언트 요청 수신
2) 요청 URL에 따라 적절한 Handler(컨트롤러) 검색(HandlerMapping 사용)
3) Handler 실행 (HandlerAdapter 사용)
4) 결과를 기반으로 적절한 View 선택
5) View 렌더링 후 응답 반환 - 장점
: Spring MVC는 Dispatcher Servlet 덕분에 web.xml의 역할이 상당히 축소되었습니다. 과거에는 모든 서블릿을 URL 매핑을 위해 web.xml에 모두 등록해주어야 했지만, Dispatcher Servlet이 해당 어플리케이션으로 들어오는 모든 요청을 핸들링해주고 공통 작업을 처리하면서 상당히 편리하게 이용할 수 있게 되었습니다. 개발자가 컨트롤러를 구현해두기만 하면 Dispatcher Servlet이 알아서 적합한 컨트롤러로 위임해주는 구조가 된 것입니다.
# Filter
: Filter는 서블릿 컨테이너에서 요청과 응답을 거른 뒤 정제하는 기능입니다. 즉 요청과 응답이 올바른 양식인지 검사하고 전/후처리를 할 수 있습니다. 공통적인 작업을 여러 서블릿에 대해 일관되게 적용할 때 유용합니다.
필터의 역할을 예시로 들기 위해, 큰 건물의 사무실에 출입하려 한다고 가정합시다. 만약 필터가 없다면, 각 사무실마다 보안 요원이 배치되어 각 사무실에 들어갈 때마다 보안 요원이 신분을 확인할 것입니다. 사무실을 이동할 때마다 같은 절차가 반복되기 때문에, 시간이 많이 걸리고, 보안 요원이 여러 명 필요합니다. 만약 건물 입구에 하나의 보안 검색대(필터)가 있다면, 이곳에서 한 번만 신분을 확인 받고나면 건물 내 모든 사무실에 자유롭게 출입할 수 있습니다.
- 동작 과정
import javax.servlet.Filter; import javax.servlet.FilterChain; import javax.servlet.FilterConfig; import javax.servlet.ServletException; import javax.servlet.ServletRequest; import javax.servlet.ServletResponse; import java.io.IOException; public class MyFilter implements Filter { @Override public void init(FilterConfig filterConfig) throws ServletException { // 서버 시작 시 필터가 초기화되면서 한 번 호출됩니다. // 이 단계에서는 필터가 필요한 리소스를 설정하거나 초기화할 수 있습니다. System.out.println("MyFilter initialized"); } @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { // 전처리 작업 // 클라이언트의 요청이 들어올 때마다 이 메서드가 호출됩니다. System.out.println("Request received at MyFilter"); // 다음 필터 또는 서블릿으로 요청을 전달 chain.doFilter(request, response); // 후처리 작업 System.out.println("Response leaving MyFilter"); } @Override public void destroy() { // 리소스 정리 작업 수행 // 서버가 종료되거나 필터가 제거될 때 호출되며, 이 시점에 필터는 리소스를 정리하거나 해제할 수 있습니다. System.out.println("MyFilter destroyed"); } }
- 장점
: 필터를 사용하면 인증, 로깅, 데이터 압축 같은 공통 작업을 중앙화하여 처리할 수 있습니다. 이를 통해 코드 중복을 줄이고, 유지 보수성을 높이며, 서블릿 간의 관심사 분리를 더욱 명확하게 할 수 있습니다.
# AOP
AOP란 어플리케이션의 여러 부분에 공통적으로 적용되는 기능을 말하며, 대표적으로 로깅, 보안, 트랜잭션 관리 등이 있습니다. 예를 들어, 집안의 안전을 위해 CCTV 시스템을 설치하려고 합니다. 이 CCTV 시스템은 집안의 여러 방과 출입구에 설치되어, 모든 활동을 모니터링하고 기록합니다. 만약 AOP를 사용하지 않는다면, 각 방의 카메라가 직접 녹화 기능을 관리하고 카메라 시스템이 업데이트되면, 모든 방의 카메라 코드를 수정해야 합니다. 그런데 AOP를 사용한다면, 집안 전체에 CCTV 시스템(Aspect)을 설치하여, 모든 방(Object)에서 발생하는 활동을 중앙에서 모니터링합니다. 이제 각 방은 카메라 관리에 대해 신경 쓸 필요가 없습니다. 방들은 본연의 기능(생활 공간)에만 집중하면 됩니다.
- 동작 과정
// 필요한 Spring AOP 어노테이션을 가져옵니다.
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.JoinPoint;
@Aspect // 이 클래스를 Aspect로 지정합니다.
public class LoggingAspect {
// Pointcut: UserService 클래스의 모든 메서드에 적용
@Before("execution(* UserService.*(..))")
public void logBefore(JoinPoint joinPoint) {
// Before Advice: 메서드 실행 전에 로그를 출력
System.out.println("Method execution started: " + joinPoint.getSignature().getName());
}
@After("execution(* UserService.*(..))")
public void logAfter(JoinPoint joinPoint) {
// After Advice: 메서드 실행 후에 로그를 출력
System.out.println("Method execution completed: " + joinPoint.getSignature().getName());
}
@Around("execution(* UserService.*(..))")
public Object logAround(ProceedingJoinPoint joinPoint) throws Throwable {
// Around Advice: 메서드 실행 전후에 로그를 출력
System.out.println("Method execution started: " + joinPoint.getSignature().getName());
// 원래의 메서드를 실행
Object result = joinPoint.proceed();
System.out.println("Method execution completed: " + joinPoint.getSignature().getName());
return result; // 메서드 실행 결과를 반환
}
}
public class UserService {
public void registerUser(String userName) {
// 사용자 등록 로직
System.out.println("Registering user: " + userName);
}
public void deleteUser(String userName) {
// 사용자 삭제 로직
System.out.println("Deleting user: " + userName);
}
}
- 장점
: 정의한 Aspect를 여러 클래스에서 재사용이 가능하고, 코드가 간결해지며 새로운 기능 추가 및 변경 시 기존 코드를 변경하지 않아도 되기 때문에 유지보수성이 향상됩니다.
# Interceptor
인터셉터는 J2EE 표준 스펙인 필터와 달리 Spring이 제공하는 기술로써, 디스패처 서블릿이 컨트롤러를 호출하기 전과 후에 요청과 응답을 참조하거나 가공할 수 있는 기능을 제공합니다. 인터셉트의 동작을 설명 드리기 위해 쉬운 예시로 설명드리겠습니다. 카페에서 고객이 주문을 하면, 바리스타가 커피를 준비하는 과정과 동시에 주문과 관련된 추가 작업(주문 확인, 결제 처리, 고객 만족도 조사 등)을 수행한다고 가정합시다.
- 고객이 커피를 주문합니다.
- 주문 처리: 바리스타가 커피를 만들기 전에, 여러 단계를 거쳐야 합니다.
- 주문을 확인
- 결제를 처리
- 고객의 만족도를 조사
- 커피가 준비되어 고객에게 제공됩니다.
여기서 인터셉터의 역할이 바로 주문 처리 프로세스이고 아래 예제 코드를 통해 동작 방식을 설명드리겠습니다.
- 동작 방식
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.web.servlet.HandlerInterceptor;
public class OrderInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
// 요청이 컨트롤러에 도달하기 전 수행할 작업
System.out.println("주문 확인: 고객의 주문을 확인합니다.");
// true를 반환하여 요청이 계속 처리되도록 합니다.
return true;
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
// 요청이 컨트롤러를 통해 처리된 후, 응답이 생성되기 전 수행할 작업
System.out.println("결제 처리: 고객의 결제를 처리합니다.");
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
// 요청 처리 후, 응답이 클라이언트에 전송된 후 수행할 작업
System.out.println("고객 만족도 조사: 고객의 만족도를 조사합니다.");
}
}
- 장점
: 웹 어플리케이션에서 공통 작업을 효율적으로 관리하고, 비즈니스 로직을 깔끔하게 유지하는 데 유용합니다. 인터셉터를 통해 요청과 응답 처리 과정의 모든 단계를 제어하고, 코드의 재사용성과 유지보수성을 높일 수 있습니다.
오늘 공부한 기술들이 웹 어플리케이션의 구조적이고 효율인 운영을 도와준다는 것을 알았습니다. 이를 통해 기능의 모듈화와 비즈니스 로직의 분리의 중요성도 다시 한번 깨닫게 되었습니다. 특히 웹의 가장 큰 단점인 취약한 보안을 filter를 통해 보완할 수 있다는 것도 반드시 알아두어야 할 내용입니다.
'웹' 카테고리의 다른 글
MSA: 마이크로서비스 아키텍처의 모든 것 (5) | 2024.10.05 |
---|---|
웹 보안의 취약점 2탄 : CORS 방어기법 (0) | 2024.08.27 |
트랜잭션 격리 수준이란? 데이터베이스 일관성의 비법 (0) | 2024.08.24 |
서버 간 세션 불일치 문제, 이렇게 해결한다(Sticky Session, Session Clustering, In-Memory DB) (0) | 2024.08.24 |
웹 보안의 취약점 1탄 : XSS, CSRF 공격 기법 (0) | 2024.08.24 |