1-13 인터셉터(Interceptor) 와 AOP(Aspect-Oriented Programming, 관점 지향 프로그래밍)
인터셉터(Interceptor), AOP(Aspect-Oriented Programming, 관점 지향 프로그래밍)
Aug 26, 2024
1.인터셉터(Interceptor)
인터셉터(Interceptor)는 Spring Framework에서 클라이언트의 요청을 처리하는 과정에서 특정 로직을 실행할 수 있게 해주는 기능입니다. 인터셉터는 컨트롤러에 요청이 도달하기 전과 후, 그리고 뷰가 렌더링되기 전후에 개입할 수 있습니다. 이는 주로 인증, 권한 부여, 로깅, 요청 데이터 변환, 공통적인 사전/사후 처리를 구현할 때 사용됩니다.
인터셉터의 주요 메서드
인터셉터는
HandlerInterceptor
인터페이스를 구현하거나 이를 상속한 HandlerInterceptorAdapter
클래스를 확장하여 작성합니다. HandlerInterceptor
인터페이스에는 세 가지 주요 메서드가 있습니다:preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
- 설명: 컨트롤러의 메서드가 호출되기 전에 실행됩니다.
- 반환값:
boolean
타입을 반환하며,true
를 반환하면 요청이 다음 단계(컨트롤러)로 진행되고,false
를 반환하면 요청 처리가 중단됩니다. - 용도: 인증 또는 권한 확인, 요청 로깅, 특정 조건에 따라 요청을 차단하는 등의 작업을 수행할 때 사용됩니다.
postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView)
- 설명: 컨트롤러의 메서드가 실행된 후, 뷰가 렌더링되기 전에 실행됩니다.
- 용도: 컨트롤러가 반환한 데이터나 뷰를 수정하거나 추가 작업을 수행할 때 사용됩니다.
afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)
- 설명: 뷰가 렌더링된 후에 실행됩니다.
- 용도: 리소스 정리, 로깅, 예외 처리 등의 사후 작업을 수행할 때 사용됩니다.
인터셉터 설정 방법
인터셉터를 사용하려면 인터셉터를 정의한 후, 이를 스프링의 설정 클래스에 등록해야 합니다. 등록 시 특정 경로에만 적용되도록 설정할 수 있습니다.
1. 인터셉터 클래스 정의
package shop.mtcoding.blog.core.interceptor;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.servlet.http.HttpSession;
import org.springframework.web.servlet.HandlerInterceptor;
import shop.mtcoding.blog.core.error.ex.Exception401;
import shop.mtcoding.blog.user.User;
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("user");
if (sessionUser == null) {
throw new Exception401("인증되지 않았어요.");
}
return true;//. false면 컨트롤러 진입안됌
}
}
2. 인터셉터 등록
package shop.mtcoding.blog.core.config;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import shop.mtcoding.blog.core.interceptor.LoginInterceptor;
//인터셉터
@Configuration // IoC에 저장됨
public class WebConfig implements WebMvcConfigurer {
//모든 곳에서 말고 특정한 곳에서 사용
// /user, /board 할 때만 뜬다.
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new LoginInterceptor()).addPathPatterns("/api/**");
}
}
인터셉터의 활용
인터셉터는 다음과 같은 다양한 작업에 활용될 수 있습니다:
- 로그인 체크: 사용자가 로그인된 상태인지 확인하고, 로그인되지 않은 경우 로그인 페이지로 리다이렉트하는 기능을 구현할 수 있습니다.
- 권한 검증: 사용자가 특정 리소스에 접근할 권한이 있는지 확인하는 작업을 수행할 수 있습니다.
- 로깅 및 감사: 요청에 대한 로깅 작업을 수행하여 시스템의 사용 내역을 추적할 수 있습니다.
- 공통 작업: 여러 컨트롤러에서 공통적으로 수행해야 하는 작업을 한 곳에서 처리할 수 있습니다.
인터셉터는 필터(Filter)와 유사하지만, 스프링 MVC의 컨트롤러 레벨에서 작동하기 때문에 더 세밀한 요청 처리와 컨트롤러 로직과의 상호작용이 가능합니다.
2.AOP(Aspect-Oriented Programming, 관점 지향 프로그래밍)
AOP(Aspect-Oriented Programming, 관점 지향 프로그래밍)은 애플리케이션의 핵심 비즈니스 로직과는 별도로 공통적으로 적용되어야 하는 기능(로깅, 트랜잭션 관리, 예외 처리 등)을 분리하여 코드의 모듈성을 높이는 프로그래밍 패러다임입니다. AOP를 사용하면 코드의 중복을 줄이고, 특정 기능을 쉽게 유지보수할 수 있습니다. 
AOP의 주요 개념
- Aspect (애스펙트):
- 공통 기능을 정의한 모듈입니다. 예를 들어, 로깅, 트랜잭션 관리, 보안 등이 Aspect가 될 수 있습니다.
- Advice (어드바이스):
- 애스펙트가 적용될 시점과 구체적인 동작을 정의한 것입니다. 어드바이스는 언제(언제 실행될지)와 무엇을(어떤 기능을 실행할지) 수행할지 결정합니다. 어드바이스의 종류는 아래와 같습니다:
- Before: 타겟 메서드가 실행되기 전에 실행됩니다.
- After: 타겟 메서드가 정상적으로 실행된 후 실행됩니다.
- AfterReturning: 타겟 메서드가 성공적으로 종료된 후 실행됩니다.
- AfterThrowing: 타겟 메서드가 예외를 던진 후 실행됩니다.
- Around: 타겟 메서드 실행 전후 또는 타겟 메서드를 대신하여 실행됩니다.
- Join Point (조인 포인트):
- 어드바이스가 실행될 수 있는 시점이나 위치를 말합니다. 보통 메서드 호출 지점이 조인 포인트가 됩니다.
- Pointcut (포인트컷):
- 어드바이스가 적용될 조인 포인트를 정의하는 표현식입니다. 메서드 이름, 패키지, 클래스 등을 기반으로 어드바이스를 적용할 위치를 지정합니다.
- Target (타겟):
- 애스펙트가 적용될 실제 객체를 의미합니다. 즉, 비즈니스 로직을 수행하는 대상입니다.
- Weaving (위빙):
- 애플리케이션 코드와 애스펙트를 결합하는 과정입니다. 위빙은 컴파일, 클래스 로딩, 런타임 시점에 이루어질 수 있습니다.
AOP 적용 예시
Spring에서 AOP를 사용하는 방법을 간단한 예시로 설명하겠습니다.
1.Gradle (build.gradle) 추가하기
implementation 'org.springframework.boot:spring-boot-starter-aop'
2.어노테이션 만들기
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 {
}
3. AOP 만들기
package shop.mtcoding.blog.core.error;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.stereotype.Component;
@Component
@Aspect // AOP 등록
public class GlobalValidationHandler {
@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 proceed = jp.proceed(); // @Hello 어노테이션이 붙은 함수 호출 "user/login-form";
System.out.println("aop hello1 after 호출됨");
System.out.println(proceed);
return proceed;
}
@Before("@annotation(org.springframework.web.bind.annotation.PostMapping) || @annotation(org.springframework.web.bind.annotation.PutMapping)")
public void validCheck(JoinPoint jp) {
Object[] args = jp.getArgs(); // 모든 매개변수를 가지고 온다
for (Object arg : args) {
if (arg instanceof Errors) {
Errors errors = (Errors) arg;
if (errors.hasErrors()) {
for (FieldError error : errors.getFieldErrors()) {
throw new Exception400(error.getDefaultMessage() + ":" + error.getField());
}
}
}
}
}
}
1. 애너테이션 포인트컷
- ‘@Before’ 어노테이션은 해당 메소드가 ‘@Post’,’@Put’ 어노테이션이 붙은 메서드 호출 전에 실행되도록 지정한다.
- ‘@annotation’ 포인트컷 표현식을 사용하여 특정 에너테이션이 붙은 메소드를 선택한다.
2. 메소드 시그니처
- ‘validCheck’ 메소드는 위의 ‘@Before’ 어드바이스가 실행될 때 호출된다.
- ‘JoinPoint’는 가로채려는 메소드에 대한 정보를 제공합니다. 여기에는 메소드의 매개변수나 메소드 자체에 대한 정보가 포함된다.
3. Object[] args = jp.getArgs();
- 가로채는 메소드의 모든 매개변수를 ‘Object’ 배열로 가져온다.
4.’Errors’ 인스턴스 확인
- 이 루프는 매개변수 배열을 순회하면서 각 매개변수가 ‘Errors’ 인스턴스인지 확인합니다.
- ‘Errors’는 Spring의 검증 결과를 담고 있는 객체입니다.
5. 검증 에러 처리
- ‘errors.hasErrors()’가 ‘true’인 경우, 즉 검증 에러가 있는 경우, ‘errors.getFieldErrors()’를 통해 모든 필드 오류를 가져온다.
- ‘FieldError’에 대해 ‘Exception400’ 예외를 던지며, 에러 메시지와 필드명을 포함시킵니다.
4.실행시키기
@Hello
@GetMapping("/login-form")
public String loginForm() {
System.out.println("loginForm 호출됨");
return "user/login-form";
}

AOP의 장점
- 관심사의 분리: 로깅, 보안, 트랜잭션 관리 등 비즈니스 로직과 직접 관련이 없는 공통 관심사를 별도로 관리할 수 있습니다.
- 코드 중복 감소: 공통 기능을 여러 곳에서 중복 작성할 필요가 없으며, 한 곳에서 정의하고 여러 곳에 적용할 수 있습니다.
- 유지보수 용이: 공통 관심사가 한 곳에 모여 있어, 수정이 필요할 때 코드 전체를 수정할 필요 없이 한 곳만 수정하면 됩니다.
AOP의 활용 사례
- 로깅: 모든 메서드 호출에 대해 로그를 남기거나 특정 메서드 호출 전후에 로그를 남기는 작업.
- 트랜잭션 관리: 데이터베이스 트랜잭션을 처리할 때, 트랜잭션 시작 및 종료를 자동으로 처리.
- 보안: 메서드 실행 전에 권한 검사를 수행하여, 접근 권한이 없는 경우 예외를 발생시키는 작업.
- 예외 처리: 공통 예외 처리 로직을 작성하여 모든 메서드에서 동일한 방식으로 예외를 처리.
AOP는 이러한 공통적인 기능을 코드베이스의 다양한 위치에 쉽게 적용할 수 있게 해주며, 코드의 모듈성과 재사용성을 높여줍니다.
3. 그림예시

Share article