객체 지향 → 관점 지향
클래스간의 관계에 집중하는게 아니라, 결국 각 기능이 하고 싶은게 뭔지에 대해 집중하자.
프로그래밍을 하다보면 공통적인 기능이 많이 발생한다. 이러한 공통 기능을 모든 모듈에 적용하기 위해 상속을 이용한다. 하지만 Java에서는 다중 상속이 불가능하며, 상속을 받아 공통 기능을 부여하기에는 한계가 있다.
예를 들어 우리가 개발한 API의 호출 시간을 측정하고 싶다고 하자. 이를 AOP없이 구현한다면 어떻겠는가? AOP를 적용하지 않는다면 중복 코드가 발생할 소지가 있고, 코드의 변경이 필요하면 여러 코드에 종속적으로 변경이 필요할 것이며, 핵심적인 비지니스 로직에 호출 시간 측정이라는 부수적인 로직이 추가되어 가독성과 효율성이 떨어지는 등의 문제가 발생할 수 있다. 이러한 문제점을 해결하기 위해 AOP(Aspect Oriented Programming, 관점 지향 프로그래밍)이 등장하게 되었다.
AOP는 새로운 프로그래밍 패러다임이 아니라 OOP(Object Oriented Programming, 객체 지향 프로그래밍)를 돕는 보조적인 기술로, 핵심적인 관심 사항(Core Concern)과 공통 관심 사항(Cross-Cutting Concern)으로 분리시키고 각각을 모듈화 하는 것을 의미한다.
아래 그림의 핵심적인 관심 사항인 계좌 이체에 대해서, 공통적으로 필요한 횡단 관심사는 로깅, 보안, 트랜잭션이 있고, 이렇게 모든 기능에서 공통적으로 사용되는 로직을 모듈화 시키는 것이 AOP이다.
더 자세한 설명을 위해 아래 코드를 예시로 들어보자.
class A {
method a() {
AAAA
method a 가 하는 일
BBBB
}
method b() {
AAAA
method b 가 하는 일
BBBB
}
}
class B {
method c() {
AAAA
method c 가 하는일
BBBB
}
}
Java
복사
•
AAAA, BBBB는 여러군데서 중복적으로 사용하는 코드
◦
중복된 코드 → 비효율
◦
AAAA, BBBB 수정 시 여러 군데에 있는 코드 수정해줘야함 → 비효율
•
AOP는 흩어진 관심사를 모아서 모듈화 시킴
◦
여기저기에 흩어져서 반복되는 코드를 흩어진 관심사라고 한다 → 관심사 관점으로 생각하는게 관점지향 프로그래밍
◦
중복되는 코드를 모듈화하고 핵심적인 비즈니스 로직에서 분리하여 재사용
AOP의 장점
•
공통 관심 사항을 핵심 관심사항으로부터 분리시켜 핵심 로직을 깔끔하게 유지할 수 있다.
•
그에 따라 코드의 가독성, 유지보수성 등을 높일 수 있다.
•
각각의 모듈에 수정이 필요하면 다른 모듈의 수정 없이 해당 로직만 변경하면 된다.
•
공통 로직을 적용할 대상을 선택할 수 있다
주요 개념, 어노테이션
1.
횡단 관심사(Cross-Cutting Concerns):
•
애플리케이션의 여러 부분에 공통적으로 적용되는 기능. (로깅, 트랜잭션 관리, 보안 검사 등)
•
이 관심사들은 특정 비즈니스 로직과는 직접 관련이 없으면서도 여러 모듈에 걸쳐 영향을 미친다.
2.
애스펙트(Aspect):
•
횡단 관심사를 모듈화한 것. 쉽게 말해, “어떤 기능을 어떤 시점에 적용할 것인가”를 정의한 것.
•
예를 들어, 로깅 애스펙트는 메서드가 실행될 때마다 로그를 기록하는 로직을 포함할 수 있다.
3.
조인 포인트(Join Point):
•
애스펙트가 적용될 수 있는 프로그램 실행의 특정 지점.(메서드 호출, 예외 발생, 필드 접근 등)
•
스프링 AOP에서는 주로 메서드 실행 시점을 조인 포인트로 사용한다.
4.
포인트컷(Pointcut):
•
하나 이상의 조인 포인트를 선택하는 기준을 정의한 것.
•
예를 들어, 특정 패키지나 클래스에 속하는 메서드에만 애스펙트를 적용하도록 포인트컷을 정의할 수 있다.
5.
어드바이스(Advice):
•
실제로 애스펙트의 코드가 적용되는 동작. 즉, 조인 포인트에서 수행될 작업을 정의한다.
•
어드바이스는 포인트컷에 의해 선택된 조인 포인트에서 실행된다.
•
Before, After, After Returning, After Throwing, Around 등이 있다.
•
어노테이션
◦
@Aspect : AOP를 정의하는 클래스에 할당
◦
@Pointcut : AOP를 적용 시킬 지점 설정
◦
@Before : 메소드 실행하기 이전
◦
@After : 메소드가 성공적으로 실행 후 예외가 발생되더라도 실행
◦
@AfterReturing : 메소드 호출 성공 실행 시
◦
@AfterThrowing : 메소드 호출 실패 예외 발생
◦
@Around : Before/After 모두 제어
// Aspect 정의
@Aspect
@Component
public class LoggingAspect {
// 포인트컷: 모든 서비스 메서드에 적용
@Pointcut("execution(* com.example.service.*.*(..))")
public void serviceMethods() {}
// Before 어드바이스: 메서드 실행 전에 로그 출력
@Before("serviceMethods()")
public void logBefore(JoinPoint joinPoint) {
System.out.println("Before method: " + joinPoint.getSignature().getName());
}
// AfterReturning 어드바이스: 메서드 정상 실행 후 로그 출력
@AfterReturning("serviceMethods()")
public void logAfterReturning(JoinPoint joinPoint) {
System.out.println("After method: " + joinPoint.getSignature().getName());
}
}
Java
복사
Spring의 AOP
Spring의 AOP는 내부적으로 다음과 같이 동작을 하게 된다.
1.
다이내믹 프록시 객체의 생성 요청
2.
포인트컷을 통해 부가 기능 대상 여부 확인
3.
어드바이스로 부가 기능 적용
4.
실제 기능 처리
쉽게, 스프링은 프록시 패턴을 이용해 런타임에 메서드 호출을 가로채고, 필요한 애스팩트를 적용하며 AOP 효과를 낸다. (접근 제어 및 부가기능을 추가하기 위해) 또한 스프링 빈에만 AOP를 적용할 수 있다.
프록시 패턴
•
interface가 존재하고, client는 interface 타입으로 proxy 객체를 사용하게 된다
•
Real Subject는 Subject라는 interface를 구현한 객체. 원래 해야할 일을 가지고 있다
•
Proxy는 Subject를 구현하면서, 원래해야할 일을 가지고 있는 Real Subject를 의존(의존성 주입)해서 생성된 객체. Client의 요청을 처리한다
•
사용하는 이유
◦
원래의 Client와 Real Subject의 코드를 건드리지 않고 부가기능을 추가할 수 있다
•
Spring AOP는 동적으로 Proxy 생성
◦
정적으로 Proxy를 만들었을 때의 단점
▪
Proxy를 만드는데 생기는 비용과 수고가 발생.
▪
Proxy를 여러 클래스, 메소드에 적용시켜야 한다면 매번 프록시 클래스 작성해야함
◦
런타임, 즉 애플리케이션이 동작하는 중에 proxy 객체 생성함으로써 해결