티스토리 뷰

RetentionPolicy와 AOP에 대한 고찰

관심을 가지게 된 이유

'땅콩' 프로젝트에서 '방 생성'시 쿠키를 발급하는 로직이 있었다.

이때 쿠키 발급 로직이 '방 컨트롤러'에 구현되어 있었다.

OOP 관점에서 책임 분리의 필요성을 느꼈고 세션 관리 로직을 AOP로 리팩터링 하도록 결정하였다.
이 과정에서 AspectJ를 활용하여 AOP를 구현하였다.

@IssueCookie라는 커스텀 애노테이션을 만들어 해당 애노테이션이 있는 경우 비즈니스 로직 호출이 완료된 이후 쿠키를 발급하는 로직이 호출되도록 설정하였다.


의문점 발생

문제 상황

나는 서버가 실행되고 있는 중 '방 생성 API'가 호출될 때, 이를 처리하는 createRoom 메서드에 애노테이션을 보고 쿠키 로직이 호출된다고 생각하였다. 

그래서 런타임까지 애노테이션에 대한 정보가 있어야 한다고 생각했다. 
따라서 해당 커스텀 애노테이션의 RetentionPolicyRUNTIME으로 설정했다.

하지만 CLASS로 바꿔도 기능이 아무 문제 없이 동작했다.
이에 대한 의문이 생겨서 찾아보게 되었다.


RetentionPolicy란?

우선 RetentionPolicy가 정확히 무엇인지에 대해서 찾아보았다.

RetentionPolicy는 애노테이션이 어느 시점까지 유지될지(라이프 사이클)를 결정하는 것이다.
자원을 효율적으로 관리하고 애노테이션의 의도를 전달하기 위해 사용한다.
이를 설정하지 않으면 애노테이션이 불필요하게 유지되거나, 필요한 시점에 접근할 수 없다.

RetentionPolicy 옵션

RetentionPolicy의 라이프 사이클을 결정하는 옵션은 크게 3가지가 있다.

  • SOURCE: 소스 코드까지만 남아있다.
  • CLASS: 클래스 파일(바이트 코드)까지만 남아있다.
  • RUNTIME: 런타임까지 남아있다.

위빙과 RetentionPolicy

RetentionPolicy에 대해 학습하던 중 '위빙'이라는 키워드가 자주 등장하였다.

RetentionPolicy 값은 위빙(Weaving)과 관련이 깊은 설정 값인 것 같다.
위빙(Weaving)은 애플리케이션 코드의 적절한 위치에 Aspect를 적용하는 과정을 말한다.
쉽게 말해, 부가 기능이 애플리케이션 코드에 추가되는 과정이다.
위빙 시점에 따라 다음과 같이 나뉜다.

  • Compile time weaving (CTW): 컴파일 시점
  • Load time weaving (LTW): 클래스 로드 시점
  • Runtime weaving (RTW): 런타임

RetentionPolicy 옵션과 유사한 점이 있다.

  • 컴파일 시 위빙: 애노테이션이 SOURCE까지만 남아 있으면 충분하다.
  • 클래스 로드 시 위빙: 애노테이션이 CLASS까지만 남아 있으면 충분하다.
  • 런타임 위빙: 애노테이션이 RUNTIME까지 남아 있어야 한다.

AOP 개념 다시 살펴보기

앞서 말했듯이 나는 AspectJ로 구현한 AOP가 런타임에 동작한다고 생각했었다.

하지만 이는 잘못된 상식이였다.

AspectJSpring AOP를 혼동하고 있었다. 둘은 전혀 다른 것이다.
이를 이해하기 위해 AOP에 대해 자세히 정리하였다.

AOP란?

AOP는 Aspect-Oriented Programming의 약자로,
애플리케이션의 핵심 기능과 부가 기능을 분리하여 Aspect라는 별도의 모듈로 설계하는 방법을 말한다.

  • 부가 기능 모듈을 Aspect라고 부른다.

 

AOP는 위빙 시점에 따라 정적 AOP 동적 AOP로 나뉜다.

정적 AOP

  • 컴파일 타임 및 로드 타임에 위빙
  • 대표적으로 AspectJ

동적 AOP

  • 런타임에 위빙
  • Spring AOP는 동적 AOP이며 프록시 기반으로 런타임에 동작한다.

 

따라서 내가 사용한 AspectJ를 활용한 AOP는 정적 AOP로 런타임에 위빙이 되는 것이 아니다.


그래서 왜 내 코드는 CLASS일 때 동작할까?

나는 AspectJ로 쿠키 로직을 구현했는데, AspectJ는 컴파일 타임 위빙과 로드 타임 위빙으로 동작한다는 것을 알게 되었다.
따라서 RetentionPolicy를 RUNTIME으로 설정하지 않아도 된다는 점은 이해되었다.

그렇다면 RetentionPolicy를 SOURCE로 설정해도 동작할까?


아쉽게도 그렇지 않았다.

SOURCE는 동작하지 않는 이유

관련 내용에 대해 구글링 하였다. 그러다 다음 글을 보게 되었다.

해당 글에 남겨진 댓글에 의하면 Maven/Gradle로 다운받은 라이브러리(jar 파일)에는 소스 코드가 포함되지 않는다.
따라서 SOURCE 정책을 사용하면 컴파일된 라이브러리에는 애노테이션 정보가 남아있지 않아 동작하지 않는다.

그래서 CLASS 정책을 사용해야 동작한다.


결론

아직 내 역량으로는 100% 이해하지는 못했지만,
AspectJ로 구현한 로직에서는 RetentionPolicy를 CLASS로 설정해야 한다는 점은 명확히 이해했다.
라이브러리에서 활용할 경우에도 SOURCE가 아닌 CLASS 정책으로 설정해야 한다.
뭔가 찜찜한 결론을 낸 것 같아 아쉽지만, 나중에 더 알게 되는 내용이 있다면 추가해보겠다.