본문 바로가기
개발/Spring

[Spring] AOP, Interceptor, Filter

by KIMECK 2020. 7. 3.
728x90
반응형

자바 웹 개발을 하다보면, 공통적으로 처리해야 할 업무들이 많다.

 

예를들어 로그인 관련(세션체크)처리, 권한체크, XSS(Cross site script)방어, pc와 모바일웹의 분기처리, 로그, 페이지 인코딩 변환 등이 있다. 

하지만 소스들이 많아지고 중복이 많아지면서 관리가 되지 않는다.
그래서, 공통 부분은 빼서 따로 관리하는 것이 좋다.

 

공통 처리를 위한 3가지 

1. Filter

2. Interceptor

3. AOP

| Filter, Interceptor, AOP의 흐름

 

그림과 같이 요청이 들어오면 Filter → Interceptor → AOP → Interceptor → Filter 순으로 거치게 된다.

| Filter (필터)

요청과 응답을 거르고 정제하는 역할을 한다.

EX)

<!-- 한글 처리를 위한 인코딩 필터 -->
<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>

필터 이름은 encoding 값은 UTF-8 인 파라미터를 정의하고 있다.

필터의 URL-PATTERN을 /*로 정의하면 servlet, jsp뿐만 아니라 이미지와 같은 모든 자원의 요청에도 호출 된다.

 

  [ 필터의 실행메서드 ]

init() - 필터 인스턴스 초기화

doFilter() - 전/후 처리

destroy() - 필터 인스턴스 종료

| Interceptor ( 인터셉터 )

요청에 대한 작업 전/후로 가로챈다고 보면 된다.

필터는 스프링 컨텍스트 외부에 존재하여 스프링과 무관한 자원에 대해 동작한다. 

하지만 인터셉터는 스프링의 DistpatcherServlet이 컨트롤러를 호출하기 전, 후로 끼어들기 때문에 스프링 컨텍스트(Context, 영역) 내부에서 Controller(Handler)에 관한 요청과 응답에 대해 처리한다.

 

스프링의 모든 빈 객체에 접근할 수 있다.

인터셉터는 여러 개를 사용할 수 있고 로그인 체크, 권한체크, 프로그램 실행시간 계산작업 로그확인 등의 업무처리

EX)

import org.springframework.web.servlet.handler.HandlerInterceptorAdapter; 

public class ExamInterceptor extends HandlerInterceptorAdapter { 

@Override 
public boolean preHandle(HttpServletRequest request,
						HttpServletResponse response, Object handle) throws Exception {
	return true; 
	} 

@Override 
public void postHandle (HttpServletRequest request, HttpServletResponse response, 
						Object handler, ModelAndView modelAndView) throws Exception {
	super.postHandle(request, response, handler, modelAndView);
	} 
}
<beans:bean id="interceptorForExam" class="com.exam.ExamInterceptor"> </beans:bean>
<mvc:interceptors>
	<mvc:interceptor>
		<mvc:mapping path="/*"/>
		<mvc:exclude-mapping path="/login/**"/>
		<mvc:exclude-mapping path="/add/**"/>
		<beans:ref bean="interceptorForExam"/>
	</mvc:interceptor>
</mvc:interceptors>

 

 preHandle

- Controller 실행 요청 전에 수행되는 메서드

- 클라이언트의 요청을 컨트롤러에 전달 하기 전에 호출

- return 값으로 boolean 값을 전달하는데 false 인 경우 controller를 실행 시키지 않고 요청 종료

 

 XML 인터셉터 설정

- id 가 "interceptorForExam" 인 class > 위 클래스를 참조하여 빈(Bean) 등록

- 인터셉터 선언부로 설정을 작성

  적용할 url , 적용할 인터셉터 빈, 예외처리 등을 구성한다.

  <mvc:mapping>

   /* 모든 url 에 적용 , /login/* 은 login 의 url 로 접근할 경우 해당 인터셉터를 탄다는 의미

  <exclude-mapping> 은 예외 처리 할 url 을 설정하는 부분이다.

  <beans:ref bean > 은 해당 인터셉터를 처리할 빈을 적용하는 부분이다.

 

  [ 인터셉터의 실행메서드 ]

preHandler() - 컨트롤러 메서드가 실행되기 전

postHanler() - 컨트롤러 메서드 실행직 후 view페이지 렌더링 되기 전

afterCompletion() - view페이지가 렌더링 되고 난 후

 

| AOP ( Aspect Oriented Programming )

AOP Aspect Oriented Programming의 약자로 관점 지향 프로그래밍이라고 불린다. 관점 지향은 쉽게 말해 어떤 로직을 기준으로 핵심적인 관점, 부가적인 관점으로 나누어서 보고 그 관점을 기준으로 각각 모듈화하겠다는 것이다. 여기서 모듈화란 어떤 공통된 로직이나 기능을 하나의 단위로 묶는 것을 말한다.

메소드 전후의 지점에 자유롭게 설정이 가능하며 주로 '로깅', '트랜잭션', '에러 처리' 등 비즈니스단의 메서드에서 조금 더 세밀하게 조정하고 싶을 때 사용합니다.

| AOP 주요 개념

 

  • Aspect : 위에서 설명한 흩어진 관심사를 모듈화 한 것. 주로 부가기능을 모듈화함.
  • Target : Aspect를 적용하는 곳 (클래스, 메소드 .. )
  • Advice : 실질적으로 어떤 일을 해야할 지에 대한 것, 실질적인 부가기능을 담은 구현체
  • JointPoint : Advice가 적용될 위치, 끼어들 수 있는 지점. 메서드 진입 지점, 생성자 호출 시점, 필드에서 값을 꺼내올 때 등 다양한 시점에 적용가능
  • PointCut : JointPoint의 상세한 스펙을 정의한 것. 'A란 메서드의 진입 시점에 호출할 것'과 같이 더욱 구체적으로 Advice가 실행될 지점을 정할 수 있음

| Advice 종류

- @Before(이전)

: 어드바이스 타겟 메소드가 호출되기 전에 어드바이스 기능을 수행

- @After(이후)

: 타겟메소드의 결과에 관계없이(즉 성공, 예외 관계없이 )타겟 메소드가 완료되면 어드바이스 기능을 수행

- @AfterReturning(정상적 반환 이후)

: 타겟 메소드가 성공적으로 결과값을 반환 후에 어드바이스 기능을 수행

- @AfterThrowing(예외 발생 이후)

: 타겟 메소드가 수행 중 예외를 던지게 되면 어드바이스 기능을 수행

- @Around (메소드 실행 전후)

: 어드바이스가 타겟 메소드를 감싸서 타겟 메소드 호출전과 후에 어드바이스 기능을 수행

 

| 스프링 AOP 특징

  • 프록시 패턴 기반의 AOP 구현체, 프록시 객체를 쓰는 이유는 접근 제어 및 부가기능을 추가하기 위해서임
  • 스프링 빈에만 AOP를 적용 가능
  • 모든 AOP 기능을 제공하는 것이 아닌 스프링 IoC와 연동하여 엔터프라이즈 애플리케이션에서 가장 흔한 문제(중복코드, 프록시 클래스 작성의 번거로움, 객체들 간 관계 복잡도 증가 ...)에 대한 해결책을 지원하는 것이 목적

| 조인포인트(JoinPoint) 와 포인트컷(Pointcut)

조인포인트(JoinPoint)

 - 클라이언트가 호출하는 모든 비즈니스 메소드, 모든 메소드를 조인포인트로 생각하면 된다.

    -> 이 중에서 포인트컷이 결정 됨.

포인트컷(Pointcut)

- 포인트컷은 필터링된 조인포인트를 의미한다.

 

// exam.xml

<bean id="before" class="io.icednut.spring.exercise.beforeAdvice"/>
<aop:config>
    <aop:pointcut id="allPointcut" expression="execution(* io.icednut.spring.exercise..*Impl.*(..))"/>
    <aop:aspect ref="before">
        <aop:before pointcut-ref="allPointcut" method="beforeLog"/>
    </aop:aspect>
</aop:config>

"allPointcut" 이라는 id의 포인트컷은 Impl로 끝나는 모든 클래스의 모든 메서드를 포인트컷으로 설정했다.

즉, 포인트컷은 <aop:pointcut>로 선언한 id 속성으로 식별 expression 속성으로 필터링되는 메소드를 지정할 수 있다.

또 <aop:aspect> 하위에 설정된 포인트컷은 해당 <aop:aspect>에서만 사용할 수 있다. (여러개 정의 가능)

 

<aop:aspect> 는 해당 관심에 해당하는 포인트컷 메소드와 횡단 관심에 해당하는 어드바이스 메소드를 결합하기 위해 사용. 어떻게 설정하냐에 따라 결과가 달라지므로 AOP에서 가장 중요한 설정이다.

 

- "allPointcut" 의 pointcut expression 경로의 beforeLog 메소드를 비지니스 메소드 실행 전에 동작한다.

| AOP 사용 예

 보안/인증/로그 등 부가기능, 시스템 전반 산재되어 사용하는기능 A / 비즈니스 로직 C

  [ A > /Advice/ > B(pointCut) ] = Aspect  > /Weaving/ > C (Code) 


1. pom.xml 의존성 di 추가 ( spring-aop )

2. @Aspect 어노테이션으로 Aspect 클래스 명시 + @Component 로 스프링 빈으로 등록 

 ( Component 란 '구성요소'로 어노테이션의 경우 작성된 Class를 Bean으로 등록하기 위한 것.

   component-scan > xml에 선언해 등록된 빈을 디폴트로 스캔 빈생성 정의를 만들고 이를 바탕으로 빈은 생성 의존성있는 빈을 주입 ) 

 

3-1. @Around 로 타겟을 감싸 특정 Advice 실행한다. ( 작성한 코드 )

   - Around ( 해당 패키지 경로의 객체 모든 메서드에 해당 Aspect 를 적용하겠다는 의미 )

3-2. OR 특정 어노테이션( ex. @Around("@anotation(A)")으로 해당 Aspect 를 실행함 

3-3. OR 타겟 메서드 Advice 로 실행 지점을 지정할 수 있다.

 

 


출처 및 참고자료:

http://closer27.github.io/backend/2017/08/03/spring-aop/

https://elfinlas.tistory.com/343 [MHLab Blog]

https://goddaehee.tistory.com/154 [갓대희의 작은공간]

https://engkimbs.tistory.com/746 [새로비]

 

728x90
반응형

댓글