티스토리 뷰
로그인을 검사해주는 부가 로직을 어떤 기술을 이용해서 분리할까? Filter & Interceptor & AOP
hamryt 2021. 4. 6. 02:37스프링 프로젝트를 하는 중에 회원가입을 개발하고 다음으로 로그인 개발을 하였습니다. 그 다음으로 유저의 정보를 수정하는 API를 개발하는데 "이 사용자가 해당 정보를 수정할 수 있는 권한이 있는건가?" 라는 사실을 검사해야하기에 저는 수정 권한을 체크하기 위해서 로그인 여부를 체크하였습니다. 그리고 그 다음으로 회원 탈퇴 API를 개발하였는데 또 다시 로그인 권한을 체크 해야했습니다.
이처럼 로그인 권한 검사는 개발자가 어떠한 목적을 가지고 API 개발하였는 가 와는 별개로 부가적인 로직으로 메소드 앞 부분에 반복해서 추가해야하는 상황이 오게 됩니다. 따라서 저는 이 로그인 부분을 따로 떼어놓아야겠다고 생각했습니다. 그리고 반복되는 부가 로직을 분리하는 방법으로는 3가지가 있습니다.
Filter와 Interceptor 그리고 AOP 입니다. 이제 각각을 알아보며 어떠한 기술을 사용하여 로그인 권한 검사를 할지 정해보겠습니다.
Filter
먼저 필터에 대해서 알아보겠습니다.
필터는 요청은 아예 스프링 영역에 들어가기 전 DispatacherServlet 이전에 실행이 되면서 개발자가 지정한 로직에 따라 요청이 필터에 걸러저 스프링에 들어가게 됩니다. 그리고 Controller 이후 자원 처리가 끝난 응답 처리에 대해서도 필터를 통해서 인코딩 변환 처리, XSS(Cross Script Site) 방어 같은 일을 수행할 수 있습니다.
사용하는 메소드
init() : 필터 인스턴스 초기화
doFilter() : 실제 로직 처리
destroy() : 필터 인스턴스 종료
저는 어노테이션 기반으로 검사를 하고 싶었는데, Filter는 스프링 이전에 실행되는 URL기반으로 검사하기 때문에 세밀한 메소드 구분을 하기에는 적절하지 않아보입니다.
Interceptor
이제 Interceptor에 대해서 알아보겠습니다.
그림을 보면 인터셉터는 스프링 영역안에서 실행됨을 알 수 있습니다. DispatcherServlet이 Controller를 호출하기 전에 preHandler라는 메소드로 요청을 인터셉트 한 뒤에 개발자가 원하는 로직을 추가하면 됩니다. 그럼 지금 제 상황에서 보면 요청이 수행되기 전에 로그인 권한을 검사할 수 있습니다. 다른 메소드를 살펴보면 그림을 보면 쉽게 알 수 있듯이 postHandler는 컨트롤러 수행이 끝난 뒤 Dispatcher Servlet에게 응답을 주기 전에 인터셉트 됩니다. 마지막으로 afterCompletion은 응답이 끝나고 view Rendering까지 한 뒤에 실행됩니다 그래서 마지막으로 리소스를 정리할 때 사용한다고 합니다.
사용하는 메소드
preHandler() : DispatcherServlet이 Controller를 호출하고 사이
postHandler() : DispatcherServlet이 Controller의 응답을 받는 사이
afterCompletion() : 응답을 받고 view Rendering까지 한 뒤
로그인 권한 검사는 컨트롤러의 메소드 실행 전에 수행되면 됩니다. 따라서 Interceptor의 preHandler() 메소드를 사용하여 구현하면 적절할거 같습니다. 마지막으로 AOP를 알아보고 비교해서 무엇을 사용할지를 생각해보겠습니다.
AOP
AOP는 주로 Controller 처리 이후 비즈니스 로직 처리에서 사용됩니다. 스프링에서의 구현방법은 2가지가 있는데 JDK 동적 프록시 방법이 있고 CGlib으로 구현하는 방법이 있습니다. 둘 다 프록시 객체를 만들어서 사용하지만 JDK 동적 프록시는 인터페이스를 사용해서 프록시를 만들고 CGlib는 enhancer라는 클래스를 통해서 프록시 객체를 만듭니다. 프록시 객체를 통해서 컨트롤러가 서비스 클래스를 호출하여 요청을 보내는 중간에 처리하는 @Before와 요청이 처리되고 응답을 컨트롤러에 보내는 중간에 처리하는 @After가 있습니다. 그리고 호출 이전, 이후, 예외발생 등 모든 시점에 적용 가능한 @Around도 있습니다. 이렇게 메소드를 중심으로 부가로직을 추가할 수 있는 AOP가 있습니다.
추가적으로 AOP는 서비스 메소드 전후에만 추가되는 것이 아니라 컨트롤러 메소드 전후에도 추가 될수 있습니다.
중요한 개념
JoinPoint : Advice가 적용될 수 있는 위치
Advice : 부가 로직
PointCut : Advice를 적용할 타켓의 메소드를 선별하는 정규 표현식
Advisor : Advice + PointCut
AOP도 컨트롤러 메소드 전후에서 로그인 검사를 진행할 수 있어 저의 상황에서 사용하기 적절해 보입니다.
결론
일단 Filter는 처리되는 컨트롤러 메소드를 구분하지 못하니 로그인 권한 검사에는 적절해보이지 않습니다. 따라서 Interceptor와 aop 둘 중에서 선택하면 될거 같습니다. 둘 다 로그인 권한 검사에는 적절하게 사용되어 보입니다. 기능면에서는 둘 차이가 없으니 리팩토링 관점에서 보겠습니다.
먼저 aop는 메소드에 자유롭게 적용가능해 보입니다. 하나의 클래스안에서 다른 메소드를 호출하는 상황에서도 부가로직을 추가하면서 유연하게 사용할 수 있는게 장점으로 보입니다. 하지만 이것은 트랜잭션을 처리하는 상황에서는 장점이 되겠지만, 로그인 권한 검사는 컨트롤러에서 요청이 들어올 때만 검사하면 되므로 지금의 상황에서는 장점이 되지 않는것 같습니다.
Interceptor는 DispatcherServlet의 요청이 컨트롤러에 들어가기 전에 로직이 수행됩니다. 따라서 로그인 권한 검사에는 충분합니다. 그리고 aop와 같이 어노테이션과 같은 적절한 표시만 해주면 메소드 단위로 쉽게 적용 가능합니다. 또한 Interceptor는 컨트롤러에만 적용가능하다는 제약이 오히려 무분별한 사용을 억제할 수 있다는 장점이 있습니다.
따라서 저는 로그인 권한 검사를 Interceptor를 통해 구현하기로 하였습니다.
@RequiredArgsConstructor
public class LoginValidationInterceptor implements HandlerInterceptor {
private final LoginService loginService;
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
if (handler instanceof HandlerMethod == false) {
return true;
}
HandlerMethod handlerMethod = (HandlerMethod) handler;
LoginValidation loginValidation = handlerMethod.getMethodAnnotation(LoginValidation.class);
if (loginValidation != null) {
loginService.sessionValidate();
}
return true;
}
}
'Project 'HELPARTY'' 카테고리의 다른 글
프로젝트에 적용할 Log 라이브러리 정하기 (0) | 2021.06.07 |
---|---|
프로젝트에서 부가 로직 분리하기 [AOP_AspectJ, HandleMethodArgumentResolver] (0) | 2021.05.07 |
세션 불일치 해결 과정 (0) | 2021.03.12 |
Sticky Session, Session Clustering, Session Storage 분리 (0) | 2021.03.12 |
스케일 아웃 (Scale Out) vs 스케일 업 (Scale Up) (0) | 2021.03.10 |
- Total
- Today
- Yesterday
- scale Out
- scale in
- MySQL
- 성능 향상
- AOP
- devops
- memcached
- Declarative Transaction
- 쿼리 튜닝
- logback
- 유틸클래스
- redis
- UTIL
- 성능 테스트
- log4j2
- nGrinder
- Filter
- AWS
- 로그
- 선언적 트랜잭션
- interceptor
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
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 | 31 |