본문 바로가기
Spring

[Spring] Filter, Interceptor, AOP

by Real Iron 2019. 2. 12.

자바 웹프로그래밍을 구현하다보면 공통적인 업무를 추가해야할 것들이 많다. 공통적인 업무에는 로그인처리(세션체크), pc웹과 모바일웹의 분기, 로그 확인, 페이지 인코딩 변환, 권한체크, XSS(Cross site script)방어 등이 있는데 이러한 공통업무에 관련된 코드를 모든 페이지 마다 작성 해야한다면 중복된 코드가 많아지게 되고 업무량이 상당히 증가할 것이다. 이러한 공통업무를 프로그램 흐름에서 앞, 중간, 뒤에 추가하여 자동으로 처리할 수 있는 방법이 있는데 서블릿에서 지원하는 서블릿 필터, 스프링 프레임워크를 사용하면 쓸 수 있는 인터셉터, AOP가 있다. 앞서 AOP개념을 정리할 때 언급한 것처럼 개발자는 좀더 핵심로직에 집중하고, 부가로직으로부터 자유로워지게 도와주는 역할을 한다.

그렇다면 요청에 흐름에 따라 필터, 인터셉터, AOP의 차이점에 대해 알아보자.


1. Filter, Interceptor, AOP의 흐름

Filter, Interceptor, AOP의 실행순서


사진 및 내용 출처: 심해펭귄의 심해도서관, [Spring] Interceptor, filter, AOP의 차이

위 그림을 보면 필터와 인터셉터의 차이를 구분할 수가 있다. 작업처리를 위해 컨트롤러가 실행되기 전에 사용한다는 점에서 별반 차이가 없어보이지만 흐름을 보면 명확히 호출되는 시점이 다르다. 실행되는 메서드를 기준으로 다시 설명해보자면, 서버를 실행시켜 서블릿이 올라오는 동안 init이 실행되고, 그후 doFilter가 실행된다. 그후 컨트롤러에 들어가기 전에 preHandler가 실행된다. AOP가 실행된 후에 컨트롤러에서 나와 postHandler, after Completion, doFilter순서로대로 진행되고 destroy가 실행된다.


2. Filter, Interceptor, AOP의 개념

01) Filter(필터)

말그대로 요청과 응답을 거른뒤 정제하는 역할을 한다.

서블릿 필터는 DispatcherServlet 이전에 실행이 되는데 필터가 동작하도록 지정된 자원의 앞단에서 요청내용을 변경하거나, 여러가지 체크를 수행할 수 있다. 또한 자원의 처리가 끝난 후 응답내용에 대해서도 변경하는 처리를 할 수가 있다.
필터는 web.xml에 등록하는데 대표적으로 인코딩 변환, 로그인 여부확인, 권한체크, XSS방어 등의 요청에 대한 처리로 사용된다.

<!-- 한글 처리를 위한 인코딩 필터 -->
<filter>
    <filter-name>encoding</filter-name>
    <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
    <init-param>
        <param-name>encoding</param-name>
        <param-value>UTF-8</param-value>
    </init-param>
</filter>
<filter-mapping>
    <filter-name>encoding</filter-name>
    <url-pattern>/*</url-pattern>
</filter-mapping>
None

등록한 encoding는 이름은 encoding이고, 값은 UTF-8인 파라미터를 정의하고 있다. 필터를 /* 에 맵핑하여 필터가 servlet, jsp뿐만 아니라 이미지와 같은 모든 자원의 요청에도 호출 된다는 것을 의미한다.

필터의 실행메서드
  • init() - 필터 인스턴스 초기화
  • doFilter() - 전/후 처리
  • destroy() - 필터 인스턴스 종료

02) Interceptor(인터셉터)

요청을 가로챈다(작업 전/후)

앞서 말했던 것처럼 필터와 인터셉터는 호출 시점이 다르다. 필터는 스프링과 무관하게 지정된 자원에 대해 동작한다. 스프링은 DistpatcherServlet으로부터 시작되므로 필터는 스프링 컨텍스트 외부에 존재하게 된다. 하지만 인터셉터는 스프링의 DistpatcherServlet이 컨트롤러를 호출하기 전, 후로 끼어들기 때문에 스프링 컨텍스트 내부에 존재하게된다. 그리고 스프링 내의 모든 객체(bean) 접근이 가능하다. 인터셉터는 여러 개를 사용할 수 있고 로그인 체크, 권한체크, 프로그램 실행시간 계산작업 로그확인, 업로드 파일처리등에 사용된다.

인터셉터의 실행메서드
  • preHandler() - 컨트롤러 메서드가 실행되기 전
  • postHanler() - 컨트롤러 메서드 실행직 후 view페이지 렌더링 되기 전
  • afterCompletion() - view페이지가 렌더링 되고 난 후

03) AOP

관점 지향 프로그래밍

AOP는 Ioc/DI, 서비스 추상화와 더불어 스프링의 3대 기반기술 중에 하나이다. AOP는 스프링의 기술중에서 가장 이해하기 힘든 난해한 용어와 개념을 가진 기술로 악명이 높다. AOP를 바르게 이용하려면 OOP를 대체하려고 하는 것 처럼 보이는 AOP라는 이름 뒤에 감춰진, 그 필연적인 등장배경과 스프링이 도입한 이유, 그 적용을 통해 얻을 수 있는 장점이 무엇인지에 대한 충분한 이해가 필요하다.


AOP는 OOP(Object Oriented Programming, 객체지향프로그래밍)를 보완하는 확정적인 개념

AOP란 OOP를 대신하는 새로운 개념이 아니라, OOP를 더욱 OOP답게 사용할 수 있도록 도와주는 개념이다.

OOP는 객체를 재사용함으로써 반복되는 코드의 양을 굉장히 많이 줄일수가 있었지만 객체의 재사용에도 불구하고 반복되는 코드를 없앨수는 없었다. 예를 들어 로그, 권한 체크, 인증, 예외 처리 등 필수적으로 해야하기 때문에 소스에서 반복될 수 밖에 없는 부분이 존재했다. AOP는 이러한 부분을 해결해주었다. 기능을 비지니스 로직과 공통 모듈로 구분한 후에 개발자의 코드 밖에서 필요한 시점에 비지니스 로직에 삽입하여 실행되도록 한다. 즉, OOP에서는 공통적인 기능을 각 객체의 횡단으로 입력했다면, AOP는 공통적인 기능을 종단간으로 삽입할 수 있도록 한 것이다.

OOPAOPLogic
[사진 및 내용 출처 : 스프링(Spring) 개발 - (16) AOP 설정하기 (부제: Controller에도 AOP 적용하기)][http://addio3305.tistory.com/86]

기존의 OOP로직의 흐름은 계정, 게시판, 계좌이체를 처리할 때마다 똑같이 권한, 트랜잭션, 로깅을 처리해야하기 때문에 모든 로직에 똑같은 코드가 반복적으로 삽입될 수 밖에 없다. 즉, 권한, 로깅, 트랜잭션이라는 관심(Aspect)이 횡단으로 삽입되는 것이다. 하지만 AOP는 이러한 관심을 종단으로 삽입할 수 있도록 해준다. 기존의 OOP로직에서는 각 객체별로 처리했던 것들을 각 관점별로 외부에서 접근하는 것이 AOP의 핵심이다. 개발자는 계정, 게시판, 계좌이체와 같은 기능을 만들고, 공통적인 관심을 처리하는 모듈을 분리해서 개발한 뒤, 필요한 시점에 자동으로 소스코드가 삽입되도록 하는 것이다.


AOP 주요 용어와 개념

Aspect
  • 구현하고자 하는 횡단 관심사(로깅, 트랜잭션, 권한 등)의 기능을 의미한다.
  • 한개 이상의 포인트컷과 어드바이스의 조합으로 만들어진다.
Join Points
  • 관점(Aspect)를 삽입하여 어드바이스가 적용될 수 있는 위치를 의미한다.
  • method를 호출하는 시점, 예외가 발생하는 시점 등과 같이 특정 작업이 실행되는 시점을 의미하기도 한다.
Advice
  • 관점(Aspect)의 구현체로 Join Points에서 실행되어야 하는 코드(실제로 AOP 기능을 구현한 객체)

  • Advice는 Join Point와 결합하여 동작하는 시점에 따라 5개로 구분된다.

  • Advice의 종류

    1. Before Advice : Join Points 전에 실행되는 advice
    2. After returning advice : Join Points에서 성공적으로 리턴 된 후 실행되는 advice
    3. After throwing advice : 예외가 발생하였을 경우 실행되는 advice
    4. After advice : Join Points에서 메서드의 실행결과에 상관없이 무조건 실행되는 advice, 자바의 finally와 비슷한 역할
    5. Around advice : Join Points의 전 과정(전, 후)에 수행되는 advice(가장 광범위하게 사용)
  • Pointcuts
    어드바이스를 적용할 조인 포인트를 선별하는 과정이나 그 기능을 정의한 모듈을 의미한다. 패턴매칭을 이용하여 어떤 조인포인트를 사용할 것인지 결정한다.

  • Target
    어드바이스를 받을 대상, 즉 객체를 의미한다. 비지니스로직을 수행하는 클래스일수도 있지만, 프록시 객체(Object)가 될 수도 있다.


AOP의 설정 방법 (로깅 저장 예제)

pom.xml 에 라이브러리 추가
<dependency>
 <groupId>org.aspectj</groupId>
 <artifactId>aspectjweaver</artifactId>
 <version>1.8.9</version>
</dependency>
None
servlet-context.xml의 Namespace에 aop 추가

AopAddition

servlet-context.xml에 aop 태그 추가
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
None
AOP 기능을 지원할 Advice 클래스 작성
@Component // 스프링에서 관리하는 bean
@Aspect // AOP bean
public class LogAdvice {

 // private : 외부에서 로그를 가로채지 못하도록 하기 위해
 // static final : 로그 내용이 바뀌지 않으므로
 // 로깅툴을 사용하는 이유 : sysout명령어는 IO리소스를 많이 사용하여 시스템이 느려질 수 있다, 로그를 파일로 저장하여 분석할 필요가 있다.
 private static final Logger logger = LoggerFactory.getLogger(LogAdvice.class);

 // PointCut - 실행 시점
 // @Before, @After, @Around
 // 컨트롤러, 서비스, DAO의 모든 method를 실행 전후에 logPrint method가 자동으로 실행된다.
 // .. : 하위의 모든 디렉토리를 의미
 // *(..) : * - 하위의 모든 메서드, (..) - 모든 매개변수
 @Around("execution(* com.example.spring02.controller..*Controller.*(..))"
         + " or execution(* com.example.spring02.service..*Impl.*(..))"
         + " or execution(* com.example.spring02.model..dao.*Impl.*(..))")
 public Object logPrinnt(ProceedingJoinPoint joinPoint) throws Throwable{
     // 실행 시간 체크 : 시작시간
     long start = System.currentTimeMillis();
     // 핵심로직으로 이동
     Object result = joinPoint.proceed();
     // 클래스 이름
     String type = joinPoint.getSignature().getDeclaringTypeName();
     String name = "";
     if (type.indexOf("Controller") > -1) {
         name = "Controller:";
     } else if (type.indexOf("Service") > -1) {
         name = "ServiceImpl:";
     } else if (type.indexOf("DAO") > -1) {
         name = "DAO:";
     }
     // 메서드 이름
     logger.info(name+type+"."+joinPoint.getSignature().getName()+"()");
     // 파라미터 이름
     logger.info(Arrays.toString(joinPoint.getArgs()));
     // 실행 시간 체크 : 종료시간
     long end = System.currentTimeMillis();
     // 실행 시간 체크 : 연산
     long time = end-start;
     logger.info("실행 시간:"+time);
     return result;
 }
}
None

마무리

AOP의 가장 큰 특징이자 장점은 중복되는 코드 제거, 효율적인 유지보수, 높은 생산성, 재활용성의 극대화, 변화 수용의 용이성이 좋다.
개발자는 핵심로직에 보다 더 집중할 수 있도록, 부가로직으로부터 자유롭게 해준다.



3. 정리

구분FilterInterceptorAOP
실행 위치서블릿서블릿메서드
실행 순서123
설정 위치web.xmlxml or javaxml or java
실행 메서드init(), doFilter(), destroy()preHandler(), postHanler(), afterCompletion()포인트 컷으로 @After, @Before, @Around 위치를 지정