본문 바로가기
Spring

AOP를 적용해 중복되는 로그인 검증 기능 분리하기.

by jinukix 2021. 12. 15.

저희 프로젝트의 대부분의 기능은 로그인된 상태로 진행할 수 있습니다. 그래서 각 기능에는 다음과 같이 로그인 검증 기능이 포함되어있었습니다. 

로그인 검증이 포함된 기능

이는 매우 비효율적이고, 유지보수 측면에서 좋은 코드가 아닙니다. 만약 로그인 로직을 수정이라도 해야 되는 날엔 모든 기능을 찾아가 수정 작업을 해야 하는 끔찍한 일이 벌어지고 맙니다.

이러한 문제를 해결하기 위해 Spring AOP를 적용하기로 했습니다. AOP는 관점 지향 프로그래밍으로 핵심적인 기능, 부가적인 기능의 관점 구분하고 부가적인 기능을 모듈화 해서 관리하는 방법입니다. 로그인 검증 로직을 따로 모듈화 하여 최종적으로는 LoginCheck 어노테이션을 붙임으로써 로그인 검증 자동으로 처리할 수 있게 만드는 것을 목표로 진행했습니다.

먼저 LoginCheck 어노테이션을 작성했습니다.

@LoginCheck

그리고 LoginCheck가 붙어있는 메서드가 실행되기 전 로그인 검증이 일어날 수 있도록 Aspect 클래스를 빈으로 등록했습니다. 만약 세션에 아이디가 없다면 RequiredLoginException (401) 런타임 예외가 발생하게 됩니다.

LoginCheckAspect
LoginService-getCurrentUser

리팩토링 결과입니다. 하지만 중복되는 코드가 하나 남아있습니다. 세션에 저장되어 있는 userId를 가지고 오는 부분입니다. 코드가 한 줄이긴 하지만 마찬가지로 이 한 줄의 로직이 수정된다면 모든 기능을 찾아가 수정해야 하는 사태가 발생합니다. 

위 문제를 해결하기 위해 컨트롤러 메서드 파라미터에서 @RequestBody, @PathVariable와 같이 커스텀 어노테이션을 만들어 처리해보겠습니다.

먼저 CurrentUserId 어노테이션을 작성하겠습니다.

그렇다면 @RequestBody, @PathVariable와 같은 것들은 어떻게 동작하는 걸까요? 바로 HandlerMethodArgumentResolver 인터페이스를 통해 동작하게 됩니다. HandlerMethodArgumentResolver은 컨트롤러 메서드에 특정 조건에 맞는 파라미터가 있는 경우 원하는 값을 바인딩해주는 인터페이스입니다.

이제 HandlerMethodArgumentResolver을 작성해봅시다.

CurrentUserIdResolver

HandlerMethodArgumentResolver 인터페이스에서 두 가지 메서드를 구현했습니다.
1. supportsParameter 메서드는 현재 파라미터를 Resolver가 처리할 수 있는지를 확인합니다. 
2. resolverArgument 메서드는 실제로 바인딩할 객체를 리턴합니다.

이제 마지막으로 작성한 HandlerMethodArgumentResolver를 스프링에 등록하겠습니다.

최종 리팩토링 결과를 확인하겠습니다.

초기 코드.
최종 리팩토링.

이전 코드와 비교했을 때 로그인 검증에 대한 로직과 비지니스 로직이 완벽히 분리되어 있음을 확인할 수 있습니다. 이로써 조금 더 유지보수 측면에서 뛰어난 코드를 유지할 수 있게 되었습니다.

'Spring' 카테고리의 다른 글

jacoco 코드 커버리지 측정시. @lombok.Data 제외하기.  (0) 2021.11.30
JDK Dynamic Proxy vs CGLIB  (0) 2021.11.23
@Transactional  (0) 2021.11.23
JPA - 영속성 컨텍스트  (0) 2021.10.01
MySQL - 파티셔닝  (0) 2021.10.01

댓글