Programming/Spring

[SpringBoot] 관점 지향 프로그래밍 AOP의 개념과 사용법

코딩하는 포메라니안 2023. 3. 22. 23:37

🐣 AOP란?

AOP(Aspect Oriented Programming)는 관점 지향 프로그래밍이라는 의미이다.

이때 말하는 관점은 아래와 같이 두 분류로 나눌 수 있다.

1. 여러 메서드에 공통으로 사용하는 기능 : 주로 로깅이나 성능 측정과 같은 부가적인 로직이 이에 해당한다.

2. 특정 메소드에서만 사용하는 핵심 기능 : 어플이 제공하는 핵심 비즈니스 로직

 

그렇다면, "AOP를 사용했을 때 어떤 이점"이 있을까

여러 메서드에 공통적으로 사용하는 코드를 분리함으로써 코드 중복을 줄일 수 있다. 또한, 이 코드에서 수정 사항이 생기면 모든 파일을 살펴볼 필요 없이 분리된 코드만 수정하면 되기 때문에, OOP원칙에 더 맞는 코드가 된다.

또한, 비즈니스 로직에는 부가적인 코드가 섞여있지 않으므로 가독성이 좋아진다.

 

🐣 AOP 용어

Target : 부가 기능이 부여되는 대상

Advice : 부가 기능

*동작 시점 = Before, AfterReturning, AfterThrowing, After, Around

Join Point : Advice가 삽입될 수 있는 위치이자 포인트 컷 후보(메서드, 필드, 객체, 생성자 등) ex) 메서드

Point cut : 어떤 클래스의 어느 조인포인트를 사용할 지 ex) com.hello.test에 있는 모든 메서드로 설정!

 

Java에서는 Aspectj 구현체가 다양하게 지원하지만, Spring AOP에서는 메서드가 실행될 때만으로 한정하고 있다.

🐣 AOP 구현 방법

1. 컴파일 시

test.java => test.class로 컴파일하는 시점에 aspect(부가 기능)들을 끼워 넣어 준다.

2. 클래스 로드 시

클래스 로더가 클래스를 메모리에 올릴 때, aspect(부가 기능)들을 끼워 넣어 준다.

3. 런타임 시(프록시 패턴) - Spring택

타겟 클래스를 프록시로 감싸서, 요청이 올 때 부가 기능이 적용되도록 한다.

Spring은 IoC와 DI를 제공하고 있는데, Target이 되는 클래스는 Proxy객체를 빈으로 관리한다.

*프록시 패턴에 대해서는 ' 2023.03.21 - [CS/디자인패턴] - Proxy Pattern | 프록시 패턴'에서 자세히 볼 수 있다. 

 

🐣 Spring에서 사용해보기

예시로, 트랜잭션 적용이 대략적으로 이렇게 작동하고 있다고 생각하며 작성했다.

@Aspect
@Component
public class TransactionLogic {
    @Around("within(com.test.hello.service.*)") //(Point Cut)
    public void setTransaction(ProceedingJoinPoint joinPoint) {
        try{
            System.out.println("begin");
            joinPoint.proceed();
            System.out.println("commit");
        }catch(Throwable t){
            System.out.println("rollback");
        }

    }
}

com.test.hello.service패키지 내에 있는 모든 클래스의 메서드들을 Point Cut으로 지정했으며, 메서드 앞뒤로 로직을 수행하므로 @Around를 사용했다.

이렇게 지정한 후, 테스트 코드에서 지정한 패키지 내에 있는 메서드 중 하나를 실행해보았다.

package com.test.hello.service;

import org.springframework.stereotype.Service;

@Service
public class TestService {
    public void A(){
        System.out.println("TestService.A");
    }
}
@SpringBootTest
public class AOPexecutionTest {

    @Autowired
    TestService testService;

    @Test
    void execution(){
        testService.A();
    }

}

출력

트랜잭션 전처리
TestService.A
commit