본문 바로가기

SpringBoot

[Spring] Filter, Interceptor 그리고 AOP

개요

프로그램을 개발할 때 공통적인 기능(예: 로깅, 인증, 트랜잭션 관리)을 분리하여 코드의 유지보수성을 높이는 개념이

관심사 분리(Separation of Concerns, SoC)입니다.

 

예를 들어, 로그를 출력하기 위해 객체의 메소드 내부에 직접 로깅 코드를 추가할 수 있지만,

이 방식은 모든 객체에서 중복된 로직을 반복적으로 작성해야 하는 문제가 있습니다.

 

또한, 세션 관리, 트랜잭션 처리, 인증/인가(Authorization) 체크 등 다양한 공통 기능도 동일한 문제를 가지며,

이러한 코드가 객체 내부에 쌓이면 핵심 비즈니스 로직보다 보일러 플레이트 코드가 많아지는 문제가 발생합니다.

 

이를 해결하기 위해 Spring에서는 Filter, Interceptor, AOP를 통해 관심사를 분리할 수 있도록 지원합니다.

각각의 개념과 차이점을 살펴보고, 어떤 상황에서 활용하면 좋은지 정리합니다.


내용

1. Filter

서블릿 수준에서 HTTP 요청과 응답을 가로챕니다.

Filter는 Dispatcher Servlet 앞과 뒤에서 실행되며, 지정된 자원에 대해서 요청 내용을 변경하거나 확인할 수 있습니다.

일반적으로 XSS 방어, 인코딩 변환, 데이터 압축 등을 처리할 때 사용합니다.

 

Spring에서는 web.xml에 필터를 등록해야 하지만,

Spring Boot에서는 FilterRegistrationBean을 사용해 Filter를 등록할 수 있습니다.

 

예시
<!-- Spring web.xml 설정 -->

<!-- Filter 선언 -->
<filter>
	<filter-name>encoding</filter-name>
	<filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
	<init-param>
		<param-name>encoding</param-name>
		<param-value>UTF-8</param-value>
	</init-param>
</filter>
<!-- Filter 적용 -->
<filter-mapping>
	<filter-name>encoding</filter-name>
	<!-- 모든 URL에 대해 적용 -->
	<url-pattern>/*</url-pattern>
</filter-mapping>
// spring boot setting
@Bean
fun encodingFilter(): FilterRegistrationBean<CharacterEncodingFilter> {
    val registrationBean = FilterRegistrationBean(CharacterEncodingFilter("UTF-8", true))
    registrationBean.addUrlPatterns("/*") // 모든 요청에 적용
    return registrationBean
}

 

Filter는 자바 서블릿에서 제공하는 기능(J2EE 스펙)이며, Spring의 @Component bean과 같은 객체에는 직접 접근할 수 없습니다.

 


2. Interceptor

Interceptor는 Spring MVC의 컨트롤러(핸들러) 실행 전후로 요청을 가로채는 역할을 합니다.

Filter와 달리, Spring MVC에서 제공하는 기능이며, Spring의 bean 객체에도 접근할 수 있습니다.

주로 권한 체크, 언어 설정, 프로그램 실행 시간 계산 등 공통적인 전처리 작업에 활용됩니다.

 

예시
@Component
class AuthCheckInterceptor : HandlerInterceptor {
    
    override fun preHandle(
        request: HttpServletRequest, 
        response: HttpServletResponse, 
        handler: Any
    ): Boolean {
        val session = request.getSession(false)
        
        if (session?.getAttribute("authInfo") != null) {
            return true // 로그인 상태
        }

        // 로그인 상태 아니므로 로그인 페이지로 redirect 및 false 반환
        response.sendRedirect("${request.contextPath}/login")
        return false
    }
}

 


3. AOP

AOP(Aspect-Oriented Programming)는 OOP의 단점을 보완하여 공통 기능을 비즈니스 로직과 완전히 분리하는 방법입니다.

Filter와 Interceptor는 주로 HTTP 요청 흐름에서 특정 URL 패턴을 기준으로 동작하지만,

AOP는 더 세밀하게 특정 annotation이 붙은 메서드나 특정 패키지 내 메서드에 적용할 수 있습니다.

 

예시
3.1 의존성 추가
//build.gradle에 의존성 추가
dependencies {
    implementation 'org.springframework.boot:spring-boot-starter-aop'
}

 

3.2 로깅을 위한 커스텀 annotation 생성
@Target(AnnotationTarget.FUNCTION)
@Retention(AnnotationRetention.RUNTIME)
annotation class LogExecutionTime

 

3.3 모든 서비스 메소드 실행 전후에 로그를 출력하는 AOP (annotation 기반)
@Aspect
@Component
class LoggingAspect {
    private val logger = LoggerFactory.getLogger(LoggingAspect::class.java)

    // 패키지 경로 확인 필요
    @Around("@annotation(com.example.aop.LogExecutionTime)")
    @Throws(Throwable::class)
    fun logExecutionTime(joinPoint: ProceedingJoinPoint): Any? {
        val startTime = System.currentTimeMillis()

        logger.info("▶ 실행: {}({})", joinPoint.signature.name, joinPoint.args)

        val result = joinPoint.proceed() // 실제 메서드 실행

        val endTime = System.currentTimeMillis()
        logger.info("▶ 완료: {} 실행시간 = {}ms", joinPoint.signature.name, (endTime - startTime))

        return result
    }
}

 

3.4 테스트용 서비스
@Service
class SampleService {
    @LogExecutionTime
    fun sayHello(name: String): String {
        return "Hello, $name!"
    }
}

 

3.5 컨트롤러에서 서비스 호출
@RestController
class SampleController(private val sampleService: SampleService) {

    @GetMapping("/hello")
    fun hello(@RequestParam name: String): String {
        return sampleService.sayHello(name)
    }
}

 

3.6 실행 결과
INFO ▶ 실행: sayHello([John])
INFO ▶ 완료: sayHello 실행시간 = 5ms

 

 


정리

관심사 분리(Separation of Concerns, SoC)는 유지보수성과 확장성을 고려할 때 필수적인 개념입니다.

Spring에서는 이를 효과적으로 적용할 수 있도록 Filter, Interceptor, AOP를 제공합니다.

 

Filter

  • 서블릿 레벨에서 HTTP 요청과 응답을 가로채어 XSS 방어, 인코딩 변환 등의 역할을 수행합니다.
  • 하지만 Spring 컨텍스트 외부에서 동작하기 때문에 Spring bean에는 직접 접근할 수 없습니다.

Interceptor

  • Spring MVC 내부에서 컨트롤러(핸들러) 실행 전후로 요청을 가로채어 권한 체크, 로깅, 실행 시간 측정 등의 기능을 수행할 수 있습니다.
  • Spring bean에도 접근이 가능하여 Filter보다 세밀한 제어가 가능합니다.

AOP

  • 객체의 메소드 단위에서 트랜잭션 관리, 로깅, 예외 처리 등의 공통 기능을 적용할 수 있도록 해줍니다.
  • annotation을 활용하면 특정 메소드에만 손쉽게 적용할 수도 있습니다.

이러한 기능들을 적절히 활용하면 핵심 비즈니스 로직을 더 깔끔하게 유지하면서도, 공통 기능을 효율적으로 적용할 수 있습니다.

상황에 맞게 Filter, Interceptor, AOP 중 어떤 것을 적용할지 고민하면서 개발하면 더욱 체계적인 애플리케이션을 만들 수 있을 거라 생각합니다.

'SpringBoot' 카테고리의 다른 글

[Spring] DTO와 VO  (1) 2025.02.24