본문 바로가기

Web/Spring

AOP

'관점 지향 프로그래밍'

'관점' 이라는 용어는 개발자들에게는 '관심사(concern)'라는 말로 통용된다.

'관심사'는 개발 시 필요한 고민이나 염두에 두어야 하는 일이라고 생각할 수 있는데, 코드를 작성하면서 염두에 두는 일들은 주로 다음과 같다.

  • 파라미터가 올바르게 들어왔을까?
  • 이 작업을 하는 사용자가 적절한 권한을 가진 사용자인가?
  • 이 작업에서 발생할 수 있는 모든 예외는 어떻게 처리해야 하는가?

위와 같은 고민들은 '핵심로직'은 아니지만, 코드를 온전하게 만들기 위해서 필요한 고민들인데 전통적인 방식에서는 개발자가 반복적으로 이러한 고민을 코드에 반영하게 된다.

AOP가 추구하는 것은 '관심사의 분리(separate concerns)'이다.

AOP는 개발자가 염두에 두어야 하는 일들은 별도의 '관심사'로 분리하고, 핵심 비즈니스 로직만을 작성할 것을 권장한다.

 


AOP 용어들

 

Target : 핵심 비즈니스 로직을 의미하고, 어떠한 관심사들과도 관계를 맺지 않는다. 순수한 코어(core)라고 볼 수 있다.

 

Proxy : Target을 전체적으로 감싸고 있는 부분. 프록시는 내부적으로 타켓을 호출하지만, 중간에 피요한 관심사들을 거쳐서 타켓을 호출하도록 자동 혹은 수동으로 작성된다.

 

JoinPoint : 타켓 객체가 가진 메서드. 조인포인트는 타켓이 가진 여러 메서드(엄밀하게 스프링 AOP에서는 메서드만이 JoinPoint가 된다.) 

 

PointCut : 어떤 메서드에 관심사를 결합할 것인지를 결정해준다. 포인트컷은 관심사와 비즈니스 로직이 결합되는 지점을 결정한다.

 

관심사 → (Aspect, Advice)로 표현.

Aspect : 관심사 자체를 의미하는 추상명사라고 볼 수 있고, Advice는 Aspect를 구현한 코드.

 

Advice : 실제 걱정거리를 분리해 놓은 코드. 스프링 3버전 이후에는 어노테이션으로도 모든 설정이 가능.

 

Advice 동작 위치
PointCut 설정 기준


AOP 실습

 

AOP 기능은 주로 일반적인 Java API를 이용하는 클래스(POJO - Plain Old Java Object)들에 적용한다.

컨트롤러에 적용이 불가능한 것은 아니지만, 컨트롤러의 경우 인터셉터나 필터 등을 이용한다.

 

package org.zerock.aop;

import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;

import lombok.extern.log4j.Log4j;

@Aspect //Aspect를 구현한 것을 알림
@Log4j
@Component //aop와 관련 없지만 bean으로 인식하기 위해 사용
public class LogAdvice {

	//BeforeAdvice를 구현한 메서드
	@Before("execution(* org.zerock.service.SampleService*.*(..))")
	public void logBefore() {
		log.info("==============");
	}
}

execution : 어떤 위치에 Advice를 적용할 것이지를 결정하는 포인트컷 

AOP 설정

root-context에 aop 랑 context 체크

<context:component-scan base-package="org.zerock.service"></context:component-scan>
<context:component-scan base-package="org.zerock.aop"></context:component-scan>
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>

root-context 작성

 

AOP 테스트

package org.zerock.service;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

import lombok.extern.log4j.Log4j;

@RunWith(SpringJUnit4ClassRunner.class)
@Log4j
@ContextConfiguration({"file:src/main/webapp/WEB-INF/spring/root-context.xml"})
public class SampleServiceTest {

	@Autowired
	private SampleService service;
	
	@Test
	public void testClass() {
		log.info(service);
		log.info(service.getClass().getName());
	}
}

프록시 객체가 생성 되는지 체크

 

결과

서비스를 출력했을 때는 기존에 사용하듯이 '샘플서비스Impl'클래스의 인스턴스처럼 보이는 건 toString()의 결과이므로 좀 더 세밀하게 파악하려면 getClass()를 이용해서 파악해야 한다.

결과

doAdd() 를 실행하면 LOgAdvice의 설정이 같이 적용되어 위와 같이 로그가 기록된다.

 

 

agrg를 이용한 파라미터 추척

args
결과

 

@AfterThrowing

테스트 코드에서 고의로 예외를 발생시켰다.

결과

 

@Around, ProceedingJoinPoint

@Around가 적용되는 메서드의 경우에는 리턴 타입이 void가 아닌 타입으로 설정하고, 메서드의 실행 결과 역시 직접 반환하는 형태로 작성해야 한다.

결과

실행 결과를 보면 @Around가 먼저 동작하고, @Before 등이 실행된 후에 메서드가 실행되는데 걸리는 시간이 로그로 기록되는 것을 볼 수 있다.

 

 

 

 

'Web > Spring' 카테고리의 다른 글

스프링 트랜잭션  (0) 2020.06.09
Rest  (0) 2020.05.31
스프링 (Spring)  (0) 2020.05.23