- 시작하게 된 계기 및 다짐 😮
이번 코드스테이츠의 백엔드 엔지니어링 개발자 부트캠프
에 참여하게 되면서 현직개발자 분들의 빠른 성장을 위한 조언 중 자신만의 블로그를 이용하여 배운 것 들을 정리하는게 많은 도움이 된다 하여 시작하게 되었다.
- 학습 목표 😮
목표 | 결과 |
---|---|
AOP의미 및 필요한 이유 이해 | O |
AOP에서 사용되는 용어들 이해 | O |
타입별 Advice 이해 | O |
- 정리
★ 핵심 기능(Core Concerns) : 업무 로직을 포함하는 기능
★ 부가 기능(Cross-Cutting Concerns) : 핵심 기능을 도와주는 부가적인 기능
[ex. 로깅, 보안, 트랜잭션 등]
★ Aspect : 부가 기능을 정의한 코드인 어드바이스(Advice)와 어드바이스를 어디에 적용할지 결정하는 포인트컷(PointCut)을 합친 개념이다. (Advice + PointCut => aspect)
1. 객체 지향 프로그래밍(OOP)
1). 주요 개념
- 공통된 목적을 띈 데이터와 동작을 묶어 하나의 객체로 정의
- 객체를 활용함으로써 재사용성이 높고 이를 위해 관심사 분리(Serparation of Concerns, SoC)의 디자인 원칙 준수
- 추후, Spring MVC구조는 @Controller, @Service, @Repository와 같이 관심사 별로 계층을 나눠 객체를 관리
- [관심사 분리는 모듈화의 핵심]
2). 문제점
- 트랜잭션, 보안, 로깅등의 애플리케이션에 필수적인 부가 기능들이 다수의 클래스에 존재하게 됨.
- 비지니스 클래스에서 횡단 관심사(부가기능)와 핵심기능이 공존하게 됨
- 메소드 복잡도 증가 -> 코드파악이 어려움
- 불특정 다수 메소드가 반복적 구현 -> 횡단 관심사의 모듈화가 어려움
2. AOP 등장
- 모듈화의 핵심 단위는 관점 ( OOP의 핵심 단위는 클래스)
- 여러 유형과 객체 간의 발생하는 문제(ex. 트랜잭션)의 모듈화를 가능하게 함
- 핵심기능(Core Concerns) 와 부가기능(Cross-Cutting Conrcerns)로 나눌 수있음
[객체의 고유 업무기능] [핵심 기능을 보안하는 기능, 핵심 기능과 함께 사용됨]
- 핵심 기능인 xx로직과 부가기능인 로그 추적 로직이 하나의 객체로 표현됨, 실행시 같이 실행됨
3. AOP가 필요한 이유
- 코드 전체적으로 전부 수정하지 않게 변경 지점을 하나가 될 수 있게 모듈화를 잘 하기 위해서
- 부가 기능은 일반적인 OOP방식으로는 해결이 어렵기 때문에 AOP가 필요
1. AOP의 용어 및 개념
1). 애스팩트(Aspect)
- 여러 객체에 공통적으로 적용되는 공통(부가)기능으로, 어드바이스+포인트컷을 모듈화하여 애플리케이션을 횡단하는 기능
- 여러 어드바이스(부가기능)와 포인트컷(어드바이스가 적용되는 지점)이 함께 존재
2). 조인 포인트(join point) [# 사진]
- 클래스 초기화, 객체 인스턴스화, 메소드 호출, 필드 접근, 예외 발생과 같은 애플리케이션 실행 흐름에서의 특정 포인트를 의미(조인 포인트라함)
- 애플리케이션에 새로운 동작을 추가하기 위해 조인포인트에 관심 코드(aspect code)를 추가
- 횡단 관심은 조인포인트 전/후에 AOP에 의해 자동으로 추가됩니다.
- AOP를 적용할 수 있는 모든 지점
- ★[스프링 AOP는 프록시 방식을 사용하므로 조인 포인트는 항상 메소드 실행 지점으로 제한]
Froxy(대리자) : 클라이언트가 사용하려고 하는 실제 대상인 것처럼 위장해서 클라이언트의 요청을 받아주는 역할
이를 사용하는 클라이언트는 구체 클래스를 알 필요가 없어짐
[Proxy라는 객체가 해야 될 일을 다른 메서드 객체에 의존성 주입을 시킴으로써, 해당 일의 책임을 다른 메서드가 지게하는 방식으로, 외부에 Proxy를 노출시키지 않고 사용가능]
- 어드바이스 적용이 필요한 곳은 애플리케이션 내에 메서드를 갖습니다.
3). 어드바이스(Advice)
- 조인 포인트에서 실행되는 코드로, Aspect를 언제 핵심 코드에 적용할 지를 정의
- 시스템 전체 애스펙트에 API호출을 제공
- 메소드 호출전 각 상세 정보와 모든 메소드를 로그로 남기기 위해 메소드 시작 전의 조인 포인트에 적용
- 부가 기능에 해당
4). 포인트컷(Pointcut)
- 조인 포인트 중에서 어드바이스가 적용될 위치를 선별하는 기능
- AspectJ 표현식을 사용해서 지정
- ★[프록시를 사용하는 스프링 AOP는 메서드 실행 지점만 포인트컷으로 선별]
5). 위빙(Weaving)
- 포인트컷으로 결장한 조인포인트에 어드바이스를 적용하는 것
[Advice를 핵심 코드에 적용하는 것을 의미]
- 부가 기능추가로, AOP적용을 위해 애스펙트 객체에 연결한 상태
[스프링 AOP는 런타임,프록시 방식]
6). AOP 프록시(proxy)
- AOP기능을 구현하기 위해 만든 프록시 객체
- 스프링에서 AOP프록시는 JDK 동적 프록시 or CGLIB 프록시다.
7). 타켓(Target)
- ★[핵심 기능을 담고 있는 모듈 타켓은 부가기능을 부여할 대상이됨]
- Advice를 받는 객체이고 포인트 컷으로 결정됨
8). 어드바이저(Advisor)
- 하나의 어드바이스와 하나의 포인트 컷으로 구성
- 스프링 AOP에서만 사용되는 언어
[extra]
1). JDK DP froxy : 인터페이스 기반의 프록시 생성시
2). CGLIB(default) : 클래스 기반으로 바이트코드를 조작하여 프록시 생성
1. 어드바이스(Advice)
- Aspect를 언제 핵심 코드에 적용할지를 정의
- 부가 기능에 해당하고, 특정 조인 포인트에서 애스펙트에 취해지는 조치
2. Advice 순서
- 어드 바이스는 기본적으로 순서를 보장하지 않는다.
- 이 때, 순서를 지정하고 싶으면 @Aspect 적용 단위로 org.springframework.core.annotation.@Order 애너테이션을 적용
(1) [어드바이스 단위가 아니라 클래스 단위로 적용할 수도 있음]
(2) 하나의 애스펙트에 여러 어드바이스가 존재시, 순서를 보장 받을 수 없음
- 애스팩트를 별도의 클래스로 분리해야 됨
3. Advice 종류
1). Before
- 조인 포인트 실행 이전에 실행됨
- 타켓 메서드가 실행되기 전에 처리해야할 필요가 있는 부가 기능을 호출해서 공통 기능을 실행
- Before Advice 구현 메서드는 리턴타입이 void로, 리턴타입이 있어도 적용 과정에 아무 영향이 없음
- 메서드에서 예외를 발생시킬 경우, 대상 객체의 메서드가 호출되지 않게함
- 메서드 종료 시 자동으로 다음 타겟이 호출됩니다. (예외가 발생하면 다음 코드는 호출되지 않는다.)
[작업흐름은 변경할 수 없다]
[예제 Code]
@Before("hello.aop.order.aop.Pointcuts.orderAndService()")
public void doBefore(JoinPoint joinPoint) {
log.info("[before] {}", joinPoint.getSignature());
}
2). After returning
- 조인 포인트가 정상 완료 후 실행된다.
- 메서드가 예외 없이 실행된 이후에 공통 기능을 실행
[예제 Code]
@AfterReturning(value = "hello.aop.order.aop.Pointcuts.orderAndService()", returning = "result")
public void doReturn(JoinPoint joinPoint, Object result) {
log.info("[return] {} return={}", joinPoint.getSignature(), result);
}
- 메서드 실행이 정상적으로 반환될 때 실행
- returning 속성에 사용된 이름은 어드바이스 메서드의 매개변수 이름과 일치해야 함
- returning 절에 지정된 타입의 값을 반환하는 메서드만 대상을 실행
3). After throwing
- 메서드가 예외를 던지는 경우에 실행
- 메서드 실행 중 예외가 발생한 경우 공통기능을 실행
- throwing 속성에 사용된 이름은 어드바이스 메서드의 매개변수 이름과 일치해야 합니다.
[throwing 절에 지정된 타입과 맞은 예외를 대상으로 실행합니다.]
[예제 Code]
@AfterThrowing(value = "hello.aop.order.aop.Pointcuts.orderAndService()", throwing = "ex")
public void doThrowing(JoinPoint joinPoint, Exception ex) {
log.info("[ex] {} message={}", joinPoint.getSignature(), ex.getMessage());
}
4). After (finally)
- 조인 포인트 동작(정상or예외)와 상관없이 실행 , [예외 동작의 finally]
- 메서드 실행 후 공통 기능을 실행
- 일반적으로, 리소스를 해제하는데 사용한다.
5). Around
- 메서드 호출 전후에 수행하며, 가장 강력한 어드바이스
[조인 포인트 실행 여부 선택, 반환 값 변환, 예외 변환등 가능]
- 메서드 실행 전&후, 예외 발생 시점에 공통 기능을 실행
(1) 조인 포인트 실행 여부 선택 - joinPoint.proceed()
(2) 전달 값 변환 - joinPoint.proceed(args[])
(3) 반환 값 변환
(4) 예외 변환
(5) try ~ catch ~ finally 가 들어가는 구문 처리 가능합니다.
- 어드바이스의 첫 파라미터는 ProceedingJoinPoint를 사용
- proceed()를 통해 대상을 실행
- proceed()를 여러번 실행할 수 있음
[extra]
@Around만 있어도 모든 기능을 수행 가능하다.
- 가장 강력한 어드바이스이며, 대부분의 기능을 제공하지만 타켓 등 고려해야할 사항이 있을 때 정상적으로 작동되지 않는 경우가 있음
1). @Before, @After와 같은 어드바이스는 기능은 적지만 원하는대로 작동 및 코드도 단순
2). 각 에너테이션만 봐도 타켓 실행 전에 어떤 일을 하는지 명학하게 이해됨
3). 좋은 설계는 @Around만 사용해서 모두 해결하는것 보다는 제약을 가지더라도 실수를 미연에 방지
4). 제약을 두면 문제 자체가 발생하지 않게하며, 역할이 분명해진다.
- 피드백 😮
AOP를 실제 적용하기위해 필요한 개념과 각 용어에 대하여 학습하였다. 기본적으로 Spring은 메서드를 시작하기 전/후 시점을 조인포인트로 제한하여 이 중 원하는 곳을 포인터컷을 설정하여 어드바이스를 적용시킨다.
이 어드바이스는 조인포인트의 시작 전/후/예외 등을 각 Advice 종류에 의하여 결정할 수 있으며, Around를 이용하여 대부분의 모든 경우를 해결 할 수 있다.
- 앞으로 해야 될 것 😮