인터셉터와 AOP

Spring에서 AOP 와 인터셉터를 활용한 HTTP 요청 전후 로직 처리
HootJem's avatar
Aug 26, 2024
인터셉터와 AOP
 
택배를 배달하는 상황을 생각해 봅시다. 택배가 목적지에 도착한 후에 열어봤더니 잘못된 물건이 들어있다면, 다시 되돌아가야 하는 불편함이 생깁니다. 그런데 만약 입구에서 택배를 미리 확인할 수 있다면 이런 불편을 줄일 수 있겠죠? 이를 개발에서는 AOP와 인터셉터로 해결할 수 있습니다.
 
AOP 나 인터셉터는
디스펙쳐 서블릿 뒤에 위치하여 controller 에 진입하기 전 공통적으로 처리할 로직을 수행합니다.
notion image
 
예시와 함께 알아보겠습니다.
1. 아침먹기(젓가락) 손씻기 샌드위치 먹기 양치하기 2. 점심먹기(젓가락) 손씻기 삼겹살 먹기 양치하기 3. 저녁먹기(젓가락) 손씻기 김치찌개먹기 양치하기
위 코드에서 매번 식사 전에 손을 씻고, 식사 후에 양치하는 패턴이 반복됩니다. 이때, AOP를 사용하면 이러한 반복적인 코드를 줄일 수 있습니다.
  • 의존성 주입
implementation 'org.springframework.boot:spring-boot-starter-aop' implementation 'org.springframework.boot:spring-boot-starter-validation'

1. AOP 활용

AOP를 사용해 손씻기와 양치하기를 @Before@After로 처리하면, 다음과 같이 코드가 간결해집니다.
1. 아침먹기(젓가락) 샌드위치 먹기 2. 점심먹기(젓가락) 삼겹살 먹기 3. 저녁먹기(젓가락) 김치찌개먹기
이제, AOP를 통해 식사 전후로 자동으로 손씻기와 양치하기가 처리되므로, 핵심 로직인 "무엇을 먹는지"에만 집중할 수 있습니다.
 

1.1 코드

@Hello @GetMapping("/") public String list(HttpServletRequest request) { List<Board> boardList = boardService.게시글목록보기(); request.setAttribute("models", boardList); return "board/list"; }
notion image
@Around("@annotation(shop.mtcoding.blog.core.Hello)") //@Before("@annotation(org.springframework.web.bind.annotation.GetMapping)") //발동 조건을 어노테이션으로 하는게 간단 public Object hello1(ProceedingJoinPoint jp) throws Throwable { System.out.println("aop hello1 before호출"); Object proccecd = jp.proceed(); System.out.println("aop hello2 before호출"); return proccecd; }
AOP 어노테이션으론 Around, Before, After 이 있습니다.
  • @Before: 메서드 실행 전에 지정한 로직을 실행합니다.
  • @After: 메서드 실행 후에 지정한 로직을 실행합니다.
  • @Around: 메서드 실행 전후 모두에 지정한 로직을 실행합니다.
지금은 Around 가 실행되어 실행 전 , 후 로 호출이 되고 있음을 알 수 있습니다.(출력 문구는 Before 이지만..) 어떤 것을 기준으로 실행될 지 어노테이션, 표현식 등 방법이 있지만 어노테이션을 사용하여 지정 하였습니다. GetMapping 을 기준으로 하고싶을 때 import 를 제외한 패키지 명을 작성해야 합니다.
 
@Hello 어노테이션은 예제를 위해 생성한 어노테이션입니다.
package shop.mtcoding.blog.core; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; @Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) public @interface Hello { }
임의로 만든 어노테이션을 활용하여도 원하는 위치에서 발동하도록 만들 수 있습니다.

2. 인터셉터 활용

인터셉터도 비슷한 방식으로 활용할 수 있습니다. preHandle에서 손을 씻고, afterHandle에서 양치를 할 수 있습니다. 인터셉터는 AOP와는 달리 메서드의 매개변수를 직접 조작할 수 없다는 점에서 차이가 있습니다.(aop 가능, interceptor 불가)
 

AOP 와 인터셉터의 차이점

AOP: 메서드 매개변수를 조작하거나 조건을 확인할 수 있습니다. 아래처럼 젓가락이 더러운 경우 예외를 던질 수 있습니다.
1. 아침먹기(젓가락) if(젓가락 == 더러움) throw new Exception(); 샌드위치 먹기 2. 점심먹기(젓가락) 삼겹살 먹기 3. 저녁먹기(젓가락) 김치찌개먹기
인터셉터: 요청 전후에 공통적인 작업을 처리할 수 있지만, 메서드 매개변수에 직접 접근하거나 조작하는 것은 어렵습니다. 주로 웹 요청 처리 과정에서 사용됩니다.
저는 게시물을 수정, 삭제 업데이트 등 할 때 sessoin 을 반복적으로 사용하고 있어 인터셉터를 사용해서 로직을 구성해 보았습니다.
public class LoginInterceptor implements HandlerInterceptor { @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { HttpSession session = request.getSession(); User sessionUser = (User) session.getAttribute("sessionUser"); if (sessionUser == null) { throw new Exception401("인증되지 않았어요"); } return true;//false 면 컨트롤러 진입안됨 } }
인터셉터는 HandlerInterceptor 를 상속받아 구성되어야 하고 이 로그인인터셉터가 어디서 사용될지는 Config 클래스를 추가하여 설정합니다.
@Configuration //IoC 에 저장됨 public class WebConfig implements WebMvcConfigurer { @Override public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(new LoginInterceptor()) .addPathPatterns("/api/**"); } }
.addPathPatterns("/api/**"); 이는 특정 경로에 대해서만 인터셉터가 동작하도록 설정한 것입니다. 위 코드는 /api 로 시작하는 모든 요청에 동작합니다.
이때, URL 구조를 잘 설계해 두면, 이후에 인터셉터를 재설정하거나 코드 변경을 최소화할 수 있습니다.
Share article

[HootJem] 개발 기록 블로그