티스토리 뷰

인프런에서 '백기선의 스프링 프레임워크 핵심기술' 이라는 강의를 꾸준히(?) 수강하고 있다.

오늘은 AOP에 대한 강의를 들었고, 정의하려고 한다. 이전에 AOP 관련되서 글을 정리한적이 있는데, 다시 봤는데! 나름 괜찮다!

AOP 정의 및 용어에 대한 설명은 내가 작성한 [AOP] 의 글을 참고하면 된다. 추가적으로 AOP를 적용할 수 있는 방법 3가지를 정리하겠다.
Weaving 이라는 단어는 Target(로직을 적용할 대상) 객체에 Aspect (로직)를 적용해서 Proxy 객체를 생성하는 과정이라고 할 수 있다.  
그리고 Weaving을 하는 방식에는 아래와 같이 나눌 수 있으며, 각각 적용법과 장, 단점은 아래와 같다.
    • Compile-Time Weaving
      • 바이트 코드를 만드는 과정에서 Aspect를 추가하는 방식
      • 빠른 실행 속도
      • 복잡한 설정
      • 컴파일 시에 영향을 주는 plugin (예 : Lombok) 과 충돌 가능

    • Run- Time Weaving 
      • Bean 객체에 Aspect를 추가하는 방식
      • 바이트 코드를 변경하지 않기 때문에 플러그인과 충돌 가능성이 없음
      • 스프링의 기본설정이기 때문에 쉬운 설정
      • 스프링 AOP 같은 경우, 제한적인 기능만을 제공 (예 : 메소드를 실행하기 전, 후의 JoinPoint 적용 가능)

    • Load-Time Weaving
      • 클래스 로딩시 Aspect를 추가하는 방식
      • 바이트 코드를 변경하지 않기 때문에 플러그인과 충돌 가능성이 없음
      • Java의 Aspect J를 사용하기 때문에 많은 기능 제공
      • 복잡한 설정


그러면, AOP를 프록시 패턴을 이용해서 직접 구현을 해보겠다. 위에 글을 보았다면, 프록시 패턴을 이해할 수 있을 것이지만, 그림은 아래와 같다.




package spring.aop.service.aop;

public interface PengsooService {

    void pengHi() throws InterruptedException;

    void pengBye();

}
package spring.aop.service.aop;

import org.springframework.stereotype.Service;

@Service
public class PengsooServiceImpl implements PengsooService {

    @Override
    public void pengHi() throws InterruptedException {
        Thread.sleep(2000);
        System.out.println("PENG HI");
    }

    @Override
    public void pengBye() {
        System.out.println("PENG BYE");
    }

}
package spring.aop.service.aop;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Primary;
import org.springframework.stereotype.Service;

@Primary
@Service
public class PengSooProxy implements PengsooService {

    @Autowired
    private PengsooService pengsooService;

    @Override
    public void pengHi() throws InterruptedException {
        long begin = System.currentTimeMillis();

        pengsooService.pengHi();

        long finish = System.currentTimeMillis();
        System.out.println("time : " + (finish - begin));
    }

    @Override
    public void pengBye() {
        pengsooService.pengBye();
    }
}

코드와 그림을 매칭하게 되면, 아래와 같다.

    • PengsooService : Subject Interface

    • PengSooServiceImpl : Real Subject Class

    • PengSooProxy : Proxy

이 코드에서 가장 중요한 것은 @Primary 어노테이션이 존재하기 때문에 Autowired 를 할 경우, 프록시가 주입이 된다는 사실이다.

하지만, 지금과 같이 AOP를 구현하게 되면 항상 프록시 객체를 생성해야 한다는 불편함이 존재할 것이고, 중복코드가 많다는 단점도 있다.

그래서 조금더 쉽게 사용할 수 있도록 스프링에서 지원하는 기능이 있다. 그것은 아래와 같다!


@Component
@Aspect
public class PengsooAspect {

    // @Around("execution(* spring.aop.service.aop.PengsooService.*(..))") // 기존 코드 수정(X), 중복 (X), 복잡한 설정, 예외처리 불가
    @Around("@annotation(Perflogging)") // 기존 코드 수정 (O), 중복 (X), 예외처리 가능
    public Object logPerf(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
        long begin = System.currentTimeMillis();

        Object retVal = proceedingJoinPoint.proceed();

        long finish = System.currentTimeMillis();

        System.out.println("TIME : " + (finish - begin));

        return retVal;
    }
}

package spring.aop.service.pattern;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Retention(RetentionPolicy.CLASS) // 어노테이션 정보 보관 관련 설정
@Target(ElementType.METHOD)
public @interface Perflogging {

}


위와 같이 스프링에서 제공하는 AOP를 활용하게 되면 굉장히 간단하게 구현할 수 있다느 장점을 가지고 있다.

그리고 기존 코드에 대한 수정없이 적용할 수 있다는 것이 가장 큰 장점이다. 위에 코드를 보면 JoinPoint를 적용할 때, execution 또는 annotation을 이용할 수 있다. 각각 장단점이 있다. execution 같은 경우에는 비즈니스 로직에 어떤 수정도 하지 않아도 된다. (어노테이션을 추가하거나 기타 등등)

그리고, 프록시 객체를 직접 생성하는 경우와 달리 중복코드가 전혀 없다! 하지만, 예외처리가 불가능하다는 단점을 가지고 있다.

그리고 annotaion을 이용할 경우에는 굉장히 설정이 간단하고, 적용 대상에 대한 예외처리가 쉽다는 장점을 가지고 있다. 개인적으로 이 방법을 선호한다.

그러나, 기존 비즈니스 로직에 어노테이션을 추가해야 한다는 코드 수정이라는 단점을 가지고 있다.


오늘은 AOP에 대해 정리를 해봤는데, 아래 github를 들어가면 상세코드를 볼 수 있다!


공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
«   2024/04   »
1 2 3 4 5 6
7 8 9 10 11 12 13
14 15 16 17 18 19 20
21 22 23 24 25 26 27
28 29 30
글 보관함