택배를 배달하는 상황을 생각해 봅시다. 택배가 목적지에 도착한 후에 열어봤더니 잘못된 물건이 들어있다면, 다시 되돌아가야 하는 불편함이 생깁니다. 그런데 만약 입구에서 택배를 미리 확인할 수 있다면 이런 불편을 줄일 수 있겠죠? 이를 개발에서는 AOP와 인터셉터로 해결할 수 있습니다.
AOP 나 인터셉터는
디스펙쳐 서블릿 뒤에 위치하여 controller 에 진입하기 전 공통적으로 처리할 로직을 수행합니다.

예시와 함께 알아보겠습니다.
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";
}

@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