Spring AOP를 Kotlin으로 리팩터링해보기

서민정·2023년 8월 18일

https://tech.kakaopay.com/post/overcome-spring-aop-with-kotlin/ 에 대한 Review

최근 AOP를 활용하여 애노테이션을 하나 만든 것이 있다.
@ReadBatch 라는 애노테이션이었는데, 해당 애노테이션이 붙은 경우에는 ThreadLocal에 string value인 batch를 set하고, 실제 트랜잭션이 실행될 때 해당 쓰레드의 쓰레드로컬에 batch가 세팅되어있다면 batch db로 쿼리를 라우팅하는 용도였다.

AOP

AOP는 Aspect Oriented Programming으로 특정 메서드가 실행되기 전, 후에 중복되어 실행되어야하는 부분을 재사용할 수 있도록 분리해내는 프로그래밍 방법이다.

가장 간단한 예시가 @Transactional 애노테이션을 들 수 있다.
우리는 db에 cud 쿼리를 수행하기 전 트랜잭션을 열고, 마지막에 트랜잭션을 닫아주는 부분을 매번 작성해야한다. 하지만 해당 애노테이션만 메서드에 명시해준다면 해당 메서드가 실행되기 전/후로 트랜잭션을 열고 닫는 행위를 수행해준다.

이렇게 트랜잭션을 열고 닫는 로직, 즉 특정 메서드의 전후에 삽입되는 로직을 Advice, 로직이 삽입될 메서드를 JoinPoint라고 부른다.

Trailing Lambda with Kotlin

위 링크한 pay tech 블로그 글을 통해 알게된 것이 trailing lambda라는 것이다. 이 문법은 마지막에 오는 함수 형태의 인자를 람다 식으로 변환하여 넘겨준다.

블로그의 예시를 가져와보자면 다음과 같다.

val result = delegate({1+3})

// using trailing lambda
val result = delegate {1 + 3}

이 Trailing Lambda를 활용하여 AOP를 간단히 구현할 수 있다.
만약 내가 위에서 언급한 @ReadBatch를 Trailing Lambda를 활용하면 아래와 같이 구현할 수 있을 것이다.

fun <T> readBatch(function: () -> T): T {
	// before method
    RoutingDataSource.context.set(RoutingDataSource.BATCH)
    
    val result = function.invoke()
    
    // after method
    RoutingDataSource.context.remove()
    
    return result
}

그리고 애노테이션 대신 다음과 같이 적용할 수 있다.

class SomeService {
	fun getSomething() = readBatch {
    	// logic
    }
}

Spring AOP를 사용하게 된다면 프록시로 동작하기 때문에 클래스 내부 함수 호출 시 AOP가 적용되지 않는다. 하지만 위 Trailing Lambda를 활용하면 함수형 프로그래밍 기법을 사용하기 때문에 내부 함수 호출 또한 AOP가 동작하도록 할 수 있다.

profile
Server Engineer

1개의 댓글

comment-user-thumbnail
2023년 8월 18일

많은 도움이 되었습니다, 감사합니다.

답글 달기