티스토리 뷰
Q. 궁금점
(상황) 로그인 기능을 세션으로 구현하였습니다. 사용자가 로그인 후 개인정보 수정 혹은 주문목록으로 세션을 가지고 있는 사용자만 접근이 가능하고자 할때, 저희는 로그인 인증을 "AOP/필터/인터셉터"로 가능합니다. 즉, 공통 관심사는 중복을 발생할 수 있기 때문에 "AOP/필터/인터셉터"를 이용하여 처리하며 각각의 장단점을 알아보고자 합니다.
Q. 질문
"AOP/필터/인터셉터"는 어떤 차이가 있으며 무엇을 사용하는 것이 좋을까?
A. 답변
A1) AOP
장점1) 인터셉터와 필터와 달리 메소드 전후의 지점을 자유롭게 설정가능하며, 인터셉터와 필터가 주소로 밖에 걸러낼 대상을 구분 할수 없는 것에 비해서 AOP는 주소, 파라미터, 어노테이션등 다양한 방법으로 대상을 지정할 수 있다.
장점2) 인터셉터와 필터는 Controller 호출 전이기 때문에 ModelAttribute를 사용하여 개발한다면 Model로 값이 들어가기 전이기 때문에 request,getParameter()함수를 사용해야 하는 반면 AOP는 Controller를 호출하기 전에 끼어드는 방식으로 Around 방식을 사용하면 Model에 값이 들어간 후 이기 때문에, 데이터를 자유롭게 참조할 수 있다. Model은 joinPoint.getArgs( ) 함수를 사용하여 값을 확인이 가능하다.
단점1) 인터셉터와 필터는 HttpServletRequest, HttpServletResponse 객체를 기본적으로 사용할수 있는 반면 AOP는 파라미터에 JoinPoint와 ProceedingJoinPoint를 활용하기 때문에 Controller Method에 인자로 HttpServletRequest를 넣어 줘야 한다는 단점이 있다.
단점2) 공통관심사를 AOP를 이용할 수 있으나 우리는 쿠키나 URL, 헤더등 웹 정보가 필요하기 때문에 HttpServletRequest를 제공하는 인터셉터나, 필터를 사용하는 것이 좋다.
A2) 필터
필터는 서블릿이 지원하는 수문장이다.
HTTP 요청 -> WAS -> 필터 -> 서블릿 -> 컨트롤러
HTTP 요청 -> WAS -> 필터1 -> 필터2 -> 필터3 -> 서블릿 -> 컨트롤러
장점1) 필터를 적용하면 필터가 호출 된 다음에 서블릿이 호출된다. 그래서 모든 고객의 요청 로그를 남기는 요구사항이 있다면 필터를 사용하면 좋다.
장점2) 필터는 체인으로 구성되어, 중간에 필터를 자유롭게 추가할 수 있다.
ex) 로그를 남기는 필터를 먼저 적용하고, 그 다음에 로그인 여부를 체크하는 필터를 만들 수 있다.
장점3) 필터에는 스프링 인터셉터가 제공하지 않는, 아주 강력한 기능이 있는데 chain.doFilter(request, response); 를 호출해서 다음 필터 또는 서블릿을 호출할 때 request , response 를 다른 객체로 바꿀 수 있다.
A3) 인터셉터
스프링 인터셉터도 서블릿 필터와 같이 웹과 관련된 공통 관심 사항을 효과적으로 해결할 수 있는 기술이다. 서블릿 필터가 서블릿이 제공하는 기술이라면, 스프링 인터셉터는 스프링 MVC가 제공하는 기술이다. 둘다 웹과 관련된 공통 관심 사항을 처리하지만, 적용되는 순서와 범위, 그리고 사용방법이 다르다
HTTP 요청 -> WAS -> 필터 -> 서블릿 -> 인터셉터 -> 컨트롤러
HTTP 요청 -> WAS -> 필터 -> 서블릿 -> 인터셉터1 -> 인터셉터2 -> 컨트롤러
스프링 인터셉터는 디스패처 서블릿과 컨트롤러 사이에서 컨트롤러 호출 직전에 호출 된다. 스프링 인터셉터는 스프링 MVC가 제공하는 기능이기 때문에 결국 디스패처 서블릿 이후에 등장하게 된다.
장점1) 스프링 인터셉터에도 URL 패턴을 적용할 수 있는데, 서블릿 필터 URL 패턴과는 다르고, 매우 정밀하게 설정할 수 있다.
장점2) 서블릿 필터의 경우 단순하게 doFilter() 하나만 제공된다. 인터셉터는 컨트롤러 호출 전( preHandle ), 호출 후( postHandle ), 요청 완료 이후( afterCompletion )와 같이 단계적으로 잘 세분화 되어 있다.
장점3) 서블릿 필터의 경우 단순히 request , response 만 제공했지만, 인터셉터는 어떤 컨트롤러( handler )가 호출되는지 호출 정보도 받을 수 있다. 그리고 어떤 modelAndView 가 반환되는지 응답 정보도 받을 수 있다.
ex) Object handler를 통해 modelAndView등 매핑 정보를 얻을 수 있다.
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception
장점4) 스프링의 모든 빈 객체에 접근할 수 있다.
장점5) 인터셉터는 스프링 MVC 구조에 특화된 필터 기능을 제공한다고 이해하면 된다. 스프링 MVC를 사용하고, 특별히 필터를 꼭 사용해야 하는 상황이 아니라면 인터셉터를 사용하는 것이 더 편리하다
인터셉터의 실행메서드
preHandle : 컨트롤러 호출 전에 호출된다. (더 정확히는 핸들러 어댑터 호출 전에 호출된다.)
postHandle : 컨트롤러 호출 후에 호출된다. (더 정확히는 핸들러 어댑터 호출 후에 호출된다.)
afterCompletion : 뷰가 렌더링 된 이후에 호출된다.
preHandle 의 응답값이 true 이면 다음으로 진행하고, false 이면 더는 진행하지 않는다. false 인 경우 나머지 인터셉터는 물론이고, 핸들러 어댑터도 호출되지 않는다. 그림에서 1번에서 끝이 나버린다.
예외가 발생시
preHandle : 컨트롤러 호출 전에 호출된다.
postHandle : 컨트롤러에서 예외가 발생하면 postHandle 은 호출되지 않는다.
afterCompletion : afterCompletion 은 항상 호출된다.
afterCompletion은 예외가 발생해도 호출된다. 예외가 발생하면 postHandle() 는 호출되지 않으므로 예외와 무관하게 공통 처리를 하려면 afterCompletion() 을 사용해야 한다. 예외가 발생하면 afterCompletion() 에 예외 정보( ex )를 포함해서 호출된다. 이 경우 예외( ex )를 파라미터로 받아서 어떤 예외가 발생했는지 로그로 출력할 수 있다.
# Code
1. AOP
@RequiredArgsConstructor
@Aspect // AOP Aspect
@Component
public class LoginAuth {
@Around("@annotation(AopAuth)") // 어노테이션과 Aspect 연결
public Object LoginAroundAuth(ProceedingJoinPoint pjp) throws Throwable {
HttpServletRequest request =((ServletRequestAttributes) RequestContextHolder.currentRequestAttributes()).getRequest();
HttpSession session = request.getSession(false);
UserProfile updateUserProfile = (UserProfile) session.getAttribute("nowId");
Object reval = pjp.proceed();
return reval;
}
@Before("@annotation(AopAuth)")
public void doSomethingBefore() {
System.out.println("메소드 시작전 !");
}
@After("@annotation(AopAuth)")
public void doSomethingAfter() {
System.out.println("메소드 시작후 !");
}
}
2. 필터
@Slf4j
public class LogFilter implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
log.info("log filter init");
}
@Override
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
...
log.info("log filter doFilter");
}
@Override
public void destroy() {
log.info("log filter destroy");
}
}
@Configuration
public class WebConfig implements WebMvcConfigurer {
@Bean
public FilterRegistrationBean logFilter() {
FilterRegistrationBean<Filter> filterRegistrationBean = new
FilterRegistrationBean<>();
filterRegistrationBean.setFilter(new LogFilter());
filterRegistrationBean.setOrder(1);
filterRegistrationBean.addUrlPatterns("/*");
return filterRegistrationBean;
}
}
3. 인터셉터
@Slf4j
public class LogInterceptor implements HandlerInterceptor {
public static final String LOG_ID = "logId";
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse
response, Object handler) throws Exception {
log.info("preHandle [{}]", modelAndView);
return true; //false 진행X
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse
response, Object handler, ModelAndView modelAndView) throws Exception {
log.info("postHandle [{}]", modelAndView);
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse
response, Object handler, Exception ex) throws Exception {
log.info("RESPONSE [{}][{}]", logId, requestURI);
}
}
@Configuration
public class WebConfig implements WebMvcConfigurer {
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new LogInterceptor())
.order(1)
.addPathPatterns("/**")
.excludePathPatterns("/css/**", "/*.ico", "/error");
}
}
Reference
https://www.inflearn.com/course/%EC%8A%A4%ED%94%84%EB%A7%81-mvc-2/dashboard (김영한님 강의)
'질문 & 답변' 카테고리의 다른 글
트러블 슈팅 (feat. Eureka AWS 배포하기) (0) | 2021.10.08 |
---|---|
Agile pull requests (feat. PR관리방법) (0) | 2021.08.01 |
"@Retention @Target "은 어떤 역할을 할까? (feat. 커스텀 어노테이션 인터셉터에 적용하기) (0) | 2021.07.11 |
"DAO / Repository" 의 차이는 무엇일까? (0) | 2021.07.08 |
"Mapper / DAO" 의 차이점은 무엇일까? (0) | 2021.07.08 |