본문 바로가기
Java & Kotlin/Spring

[Spring] AOP

by heekng 2021. 5. 26.
반응형

[Spring] AOP

AOP란 Aspect Oriented Programming의 약자로 관점 지향 프로그래밍을 뜻한다.

관점, 관심사

관점이란 개발에 있어서, 관심사(concern)을 의미한다.

관심사는 개발 시 필요한 것들을 생각하는 일이다.

  • 파라미터가 잘 전달되었는가?
  • 이 로직에서 발생할 수 있는 모든 예외를 처리하자
  • 적절한 권한을 가진 사용자가 작업하고 있는가?

핵심 로직은 아니지만 반복적으로 개발에 필요한 관심사들이다.

AOP

따라서 AOP는 이러한 것들을 모듈로 분리하여 작성하고 핵심 비지니스 로직만을 작성하도록 한다.

 

종단 관심사 : 핵심로직

횡단 관심사 : 주변로직

 

횡단 관심사를 모듈로 구현하는 것이 AOP를 구현하는 것이다.

즉, 반복적으로 나타나는 횡단괌심사를 모듈로 분리한 수 적절한 곳에 로직을 주입하는 것이 AOP이다.

스프링에서는 별도의 복잡한 설정 없이 간편하게 AOP의 기능들을 구현할 수 있다는 것이 중요한 특징 중 하나이다.


AOP를 사용할 수 있을 때

  • @Around : 전 구역
  • @Before : 메소드 시작 직후
  • @After : 메소드 종료 직전
  • @AfterReturning : 메소드 정상종료 후
  • @AfterThrowing : 메소드에서 예외 발생 종료 후

AOP는 위 어노테이션을 이용해서 원하는 로직이 어떠한 타이밍에 실행되게 할 지를 선택할 수 있다.


AOP 사용을 위한 pom.xml 수정

...

<properties>
   <java-version>1.8</java-version>
   <org.springframework-version>5.0.7.RELEASE</org.springframework-version>
   <org.aspectj-version>1.9.0</org.aspectj-version>
   <org.slf4j-version>1.7.25</org.slf4j-version>
</properties>

...

<!-- AspectJ -->
<dependency>
   <groupId>org.aspectj</groupId>
   <artifactId>aspectjrt</artifactId>
   <version>${org.aspectj-version}</version>
</dependency>

<dependency>
   <groupId>org.aspectj</groupId>
   <artifactId>aspectjweaver</artifactId>
   <version>${org.aspectj-version}</version>
</dependency>

위와 같이 aspectj-version을 1.9.0, slf4j-servsion을 1.7.25로 수정

AspectJ <dependency>아래에 aspectjweaver 태그 추가


AOP 사용을 위한 root-context 수정, 프록시

<context:annotation-config></context:annotation-config>
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>

위 태그를 추가한다.

<context:annotation-config> : 이미 등록된 Bean에 대해서만 Annotation을 활성화 한다.

AOP를 구성하는 패키지를 포함하여 사용하는 곳까지 모두 Bean에 등록해야한다.

<aop:aspectj-autoproxy></aop:aspectj-autoproxy> : 자동으로 프록시를 만들어주는 설정

 

프록시: 종단관심사와 횡단관심사가 분리되어있을 때 둘을 연결해줄 것이 필요, 이 역할을 프록시가 한다.


AOP 구성하기

package com.heekng.aop;

import java.util.Arrays;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;

import lombok.extern.log4j.Log4j;

@Aspect //aop다
@Log4j
@Component
public class LogAdvice {
	@Before("execution(* com.heekng.service.SampleService*.*(..))")
	public void logBefore() {
		log.info("=========Before=========");
	}
	
	@After("execution(* com.heekng.service.SampleService*.*(..))")
	public void logAfter() {
		log.info("=========After=========");
		
	}
	
	@AfterReturning("execution(* com.heekng.service.SampleService*.*(..))")
	public void logAfterRetruning() {
		log.info("=========AfterReturning=========");
		
	}
	
	@Before("execution(* com.heekng.service.SampleService*.doAdd(String, String)) && args(str1, str2)")
	public void logBeforeWithParam(String str1, String str2) {
		log.info("str1 : " + str1);
		log.info("str2 : " + str2);
	}
	
	@AfterThrowing(pointcut="execution(* com.koreait.service.SampleService*.*(..))", throwing = "exception")
	public void logException(Exception exception) {
		log.info("Exception.............");
		log.info("exception : " + exception);
	}
	
	@Around("execution(* com.heekng.service.SampleService*.*(..))")
	public Object logTime(ProceedingJoinPoint pjp) {
		long start = System.currentTimeMillis();
		
		log.info("핵심 로직 : " + pjp.getTarget());
		log.info("파라미터 : " + Arrays.toString(pjp.getArgs()));
		
		Object result = null;
		
		try {
			result = pjp.proceed();
		} catch (Throwable e) {
			e.printStackTrace();
		}

		long end = System.currentTimeMillis();
		
		log.info("걸린 시간: " + (end - start));
		
		return result;
	}
}

 

  • @Aspect : 해당 클래스는 aop임을 알린다.
  • AOP가 적용될 패키지 설정은 AOP어노테이션 뒤 소괄호로 지정한다.
    이를 pointcut이라 하며 횡단관심사와 종단관심사의 결합되는 지점을 결정하는 것이다.
  • @Before : 메소드에 들어와 가장 먼저 실행된다.
  • @After : 메소드가 끝나기 직전에 실행된다.
  • @AfterReturning : 메소드가 끝난 다음에 실행된다.
  • @AfterThrowing : (pointcut="[실행될 메소드]", throwing="[exception]")의 형태로 작성한다.
    매개변수로 exception을 받아야한다.
  • @Around : 메소드들이 시작되기 이전에 거쳐가는 aop를 설정한다.
    실행된 메소드의 모든 정보를 가지고 있는 ProceedingJoinPoint 객체를 매개변수로 받아와 메소드에 대한 정보들을 이용하고, return 클래스가 Object클래스인 이유는 내가 관여한 메소드의 리턴 값인 pjp.proceed()를 직접 전달해줘야 하기 때문이다.
    즉 Around어노테이션은 메소드를 감싼 형태로 작동하고, AOP내에서 직접 메소드를 실행시키고 결과값을 전달시킨다.
반응형