코드스테이츠 38일차
학습 목표
- AOP의 의미를 이해할 수 있다.
- AOP가 필요한 이유에 대해 이해할 수 있다.
- AOP에서 사용되는 용어들에 대해 이해할 수 있다.
- 타입별 Advice, Pointcu 표현식, JointPoint 등의 의미를 이해할 수 있다.
- 애너테이션을 이용한 AOP에 대해 이해할 수 있다.
AOP가 필요한 이유
먼저 그 전에 객체 지향 프로그래밍(Object Oriented Programming:OOP)에 대해 다시 생각해보자
- 이전과 현재에 많은 프로젝트는 OOP 패러다임을 지향하며 프로그래밍을 하고 있다.
- OOP의 핵심은 공통된 목적을 띈 데이터와 동작을 묶어 하나의 객체로 정의하는 것이다.
- 객체를 적극적으로 활용함으로써 기능을 재사용할 수 있는 것이 큰 장점이다.
- 객체를 잘 활용하기 위해선 관심사 분리 (Separation of Concerns, SoC)의 디자인 원칙을 준수해야한다.
문제점
- 특정 관심사 업무 코드에 트랜잭션, 보안, 로깅 등의 코드가 존재하게 된다.
- 트랜잭션, 보안, 로깅 코드는 업무와는 관련이 없지만 애플리케이션에 필수적인 부가 기능이다.
- 트랜잭션, 보안, 로깅 기능은 불특정 다수의 클래스에서 존재하게 된다.
- 관심사 관점에서는 트랜잭션, 보안, 로깅 코드들을 횡단 관심사(Cross-cutting Concerns: 부가 기능)라고 한다.
- 업무 관련 코드는 핵심 관심사(Core Concerns: 핵심 기능)라고 한다.
- 비즈니스 클래스에 횡단 관심사와 핵심 관심사가 공존하게 된다.
애스펙트(Aspect) 관점은 하나 하나의 기능 → 횡단 관심사(cross-cutting concerns) 관점으로 보는 것이라고 볼 수 있다.
AOP의 등장
OOP만 사용해선 횡단 관심사 코드를 깔끔하게 분리하고 비즈니스 코드에 적용하기 어려웠다.
이러한 한계를 해결하고자 AOP가 등장하게 되었다.
AOP란?
AOP(Aspect-Oriented Programming)는 기존과 다른 프로그램 구조 사고 방식을 제공함으로써 객체 지향 프로그래밍(OOP)의 부족한 부분을 보완한다.
OOP의 모듈화의 핵심 단위는 클래스이고, AOP의 모듈화의 핵심 단위는 관점이라고 볼 수 있다.
Aspect는 여러 유형과 객체 간에 발생하는 문제 (ex. 트랜잭션)의 모듈화를 가능하게한다.
AOP의 핵심 기능과 부가 기능
- 핵심 기능(Core Concerns)
- 객체가 제공하는 고유의 기능(업무 로직 등을 포함)
- 부가 기능(CROSS-CUTTING CONCERNS)
- 핵심 기능을 보조하기 위해 제공되는 기능이다.
- 로그 추적 로직, 보안, 트랜잭션 기능 등이 있다.
- 단독으로 사용되지 않고 핵심 기능과 함께 사용된다.
부가 기능은 보통 여러 클래스에 걸쳐서 함께 사용되며, 이러한 기능이 바로 횡단 관심사이다.
- 부가 기능을 여러 곳에 적용하려면 번거롭고 중복 코드가 생기게 된다.
- 부가 기능에 수정이 필요하게 되면 사용 되는 클래스에 모두 하나씩 차자가면서 수정해야한다.
AOP가 필요한 이유?
소프트웨어 개발에서 변경 지점은 하나가 될 수 있도록 잘 모듈화 되어야 한다.
부가 기능처럼 특정 로직을 애플리케이션 전반에 적용하는 문제는 일반적인 OOP 방식으로는 해결이 어렵기 때문에 핵심 기능과 부가 기능을 분리하는 AOP 방식이 필요한 것이다.
총정리
- 객체 지향 프로그래밍(OOP)는 객체 지향 프로그래밍이다.
- OOP 방식의 프로그래밍을 했을 때 여러 곳에서 공통적으로 사용되는 부가 기능의 중복 코드가 발생하게 된다.
- 중복되는 부가 기능에 수정 및 삭제가 필요하게 되면 사용되는 모든 곳에 수정 및 삭제 동작을 해줘야한다.
- 관심 지향 프로그래밍(AOP)은 OOP 방식의 불필요한 반복을 해결하기 위한 방법이다.
AOP 용어
애스팩트(Aspect)
- 여러 객체에 공통으로 적용되는 기능을 말한다. (공통 기능)
- 어드바이스 + 포인트컷을 모듈화하여 애플리케이션에 포함되는 횡단 기능이다.
- 여러 어드바이스와 포인트컷이 함께 존재한다.
조인 포인트(join point)
- 클래스 초기화, 객체 인스턴스화, 메소드 호출, 필드 접근, 예외 발생과 같은 애플리케이션 실행 흐름에서의 특정 포인트를 의미한다.
- 애플리케이션에 새로운 동작을 추가하기 위해 조인포인트에 관심 코드(aspect code)를 추가할 수 있다.
- 횡단 관심은 조인포인트 전/후에 AOP에 의해 자동으로 추가된다.
- 추성적인 개념이고 AOP를 적용할 수 있는 모든 지점이라 생각하자
- 스프링 AOP는 프록시 방식을 사용하므로 조인 포인트는 항상 메소드 실행 지점으로 제한된다.
- 어드바이스 적용이 필요한 곳은 애플리케이션 내에 메서드를 갖는다.
어드바이스(Advice)
- 조인포인트에서 수행되는 코드를 의미한다.
- Aspect를 언제 핵심 코드에 적용할 지를 정의한다.
- 시스템 전체 애스펙트에 API 호출을 제공한다.
- 위 그림처럼 메소드를 호출하기 전에 각 상세 정보와 모든 메소드를 로그로 남기기 위해 메소드 시작 전의 포인트인 조인포인트 S를 선택한다.
- 부가 기능에 해당된다.
포인트컷(Pointcut)
- 조인 포인트 중에서 어드바이스가 적용될 위치를 선별하는 기능이다.
- AspectJ 표현식을 사용하여 지정한다.
- 프록시를 사용하는 스프링 AOP는 메서드 실행 지점만 포인트컷으로 선별 가능하다.
위빙(Weaving)
- 포인트컷으로 결정한 타겟의 조인 포인트에 어드바이스를 적용하는 것이다.
- 핵심 기능 코드에 영향을 주지 않고 부가 기능을 추가 할 수 있다.
- AOP 적용을 위해 애스펙트 객체에 연결한 상태이다.
AOP 프록시(proxy)
- AOP 기능을 구현하기 위해 만든 프록시 객체이다.
- 스프링에서 AOP 프록시는 JDK 동적 프록시 또는 CGLIB 프록시이다.
타겟 (Target)
- 핵심 기능을 담고 있는 모듈로 타겟은 부가기능을 부여할 대상이 된다.
- Adivce를 받는 객체이고 포인트컷으로 결정된다.
어드바이저(Advisor)
- 하나의 어드바이스(Advice)와 하나의 포인트 컷으로 구성된다.
- 스프링 AOP에서만 사용되는 특별한 용어이다.
어드바이스(Advice)
- Aspect를 언제 핵심 코드에 적용할지를 정의한다.
- 부가 기능에 해당된다.
- 특정 조인 포인트에서 애스팩트에 의해 취해지는 조치이다.
Advice 순서
어드바이스는 기본적으로 순서를 보장하지 않는다.
순서를 지정하고 싶으면 @Aspect 적용 단위로 org.springframework.core.annotation.@Order 애너테이션을 적용해야 한다.
- 어드바이스 단위가 아니라 클래스 단위로 적용할 수 있다.
- 하나의 애스펙트에 여러 어드바이스가 존재하면 순서를 보장 받을 수 없다.
애스펙트를 별도의 클래스로 분리해야 한다.
Advice 종류
Before
- 조인 포인트 실행 이전에 실행한다.
- 타겟 메서드가 실행되기 전에 처리해야할 필요가 있는 부가 기능을 호출 전에 공통 기능을 실행한다.
- Before Advice룰 구현한 메서드는 일반적으로 리턴타입이 void이다.
- 주의점으로 메서드에서 예외를 발생시킬 경우 대상 객체의 메서드가 호출되지 않게 된다는 점이 있다.
- 작업 흐름을 변경할 수 없다.
- 메서드 종료 시 자동으로 다음 타겟이 호출된다. (예외가 발생하면 다음 코드는 호출되지 않는다.)
After returning
- 조인 포인트가 정상 완료 후 실행한다.
- 메서드가 예외 없이 실행된 이후에 공통 기능을 실행한다.
- 메서드 실행이 정상적으로 반환될 때 실행된다.
- returning 속성에 사용된 이름은 어드바이스 메서드의 매개변수 이름과 일치해야 한다.
- returning 절에 지정된 타입의 값을 반환하는 메서드만 대상을 실행한다.
After throwing
- 메서드가 예외를 던지는 경우에 실행한다.
- 메서드를 실행하는 도중 예외가 발생한 경우 공통 기능을 실행한다.
After (finally)
- 조인 포인트의 동작(정상 또는 예외)과는 상관없이 실행한다.
- 메서드 실행 후 공통 기능을 실행힌다.
- 일반적으로 리소스를 해제하는데 사용한다.
Around
- 메서드 호출 전후에 수행하며 가장 강력한 어드바이스이다.
- 메서드 실행 전 & 후, 예외 발생 시점에 공통 기능을 실행한다.
- 어드바이스의 첫 번째 파라미터는 ProceedingJoinPoint를 사용해야 한다.
- proceed()를 통해 대상을 실행한다.
- proceed()를 여러번 실행할 수 있다.
@Around만 있어도 모든 기능 수행이 가능하다.
가장 강력한 어드바이스이며 대부분의 기능을 제공하지만 타겟 등 고려해야할 사항이 있을 때 정상적으로 작동이 되지 않는 경우가 있기도 하다.
- @Before, @After와 같은 어드바이스는 기능은 적지만 원하는대로 작동되고 코드도 단순하다.
- 각 애너테이션만 봐도 타겟 실행 전에 어떤 일을 하는지 명확하게 알 수 있다.
- 좋은 설계는 @Around만 사용해서 모두 해결하는 것보다는 제약을 가지더라도 실수를 미연에 방지하는 것이다.
- 제약을 두면 문제 자체가 발생하지 않게 하며, 역할이 명확해진다.