공통 관심 사항(cross-cutting concerns)
공통관심사항(Cross-Cutting Concern) 은 로직의 핵심 비즈니스와는 별개이지만 여러 곳에서 반복적으로 사용되는 기능
즉, 비즈니스 로직과는 직접적인 관련은 없지만, 여러 계층(Service, Controller 등) 에서 공통적으로 필요한 기능들을 의미한다.
공통 관심 사항의 대표적인 예시는 다음과 같은 것들이 있다.
| 공통 관심 사항 | 설명 |
|---|---|
| ✅ Logging | 요청/응답 로그, 에러 로그 |
| ✅ 인증(Authentication) | 로그인 유저 확인 |
| ✅ 권한(Authorization) | 관리자 권한 체크 |
| ✅ 예외 처리 | 공통 에러 핸들링 |
| ✅ 트랜잭션 처리 | 서비스 실행 시 트랜잭션 관리 |
| ✅ 캐싱 | 중복 호출 방지 |
| ✅ 성능 측정 | 메서드 실행 시간 로그 |
이러한 공통 관심사를 처리하기 위해서 Spring AOP, Servlet Filter나 Spring Intercepter 등이 등장했다.
간략하게 정리하자면 다음 3가지가 있다.
| 기술 | 실행 시점 | 주요 역할 | 관여 계층 |
|---|---|---|---|
| 🔵 Filter | DispatcherServlet 이전 | HTTP 요청/응답 전반 감시 | 서블릿 컨테이너 수준 |
| 🟠 Interceptor | DispatcherServlet 다음, Controller 이전/후 | 인증, 권한, 접근 로그 | Spring MVC 계층 |
| 🟣 AOP (@Aspect) | 메서드 실행 전/후/예외 | 로깅, 트랜잭션, 파라미터 추적 | Bean 메서드 계층 (Service 등) |
🔵 Filter
Filter는 클라이언트의 요청(Request)과 서버의 응답(Response)을 가로채어 전/후 처리할 수 있는 웹 애플리케이션의 구성 요소
클라이언트 ↔ Filter ↔ DispatcherServlet(Controller) 와 같이 DispatcherServlet 앞에서 동작하며 모든 요청이 지나가는 길목에서 검사/수정/로깅/차단 등을 할 수 있다. HTTP 요청/응답을 전처리/후처리하는 가장 바깥단의 가드라인이다. 주로 인코딩, 보안, 토큰 파싱, 로깅, CORS 같은 공통 기능을 담당한다.
Servlet Filter 특징
- 공통 관심사 로직 처리
- 공통된 로직을 중앙 집중적으로 구현하여 재사용성이 높고 유지보수가 쉽다.
- 모든 요청이 하나의 입구를 통해 처리되어 일관성을 유지한다.
- HTTP 요청 및 응답 필터링
Filter Chain- 여러 개의 필터가 순차적으로 적용될 수 있다.
filterChain.doFilter(request, response);다음 필터로 제어를 전달한다.
doFilter()- 실제 필터링 작업을 수행하는 주요 메소드로 필터가 처리할 작업을 정의한다.
- 다음 필터로 제어를 넘길지 여부를 결정한다.
🟠 Interceptor
HTTP 요청과 컨트롤러(Handler) 사이에서 동작하는 가로채기 역할을 수행하는 컴포넌트
이름에서부터 알 수 있듯이, Spring MVC에 들어온 요청이 Controller로 넘어가기 전에 공통로직을 실행하는데 사용하는 것이다. 주로 로그인, 인증, 권한 체크, 접근 기록에 적합하다.
Interceptor의 주요 메서드 3가지
public interface HandlerInterceptor {
boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler);
void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView);
void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex);
}
| 메서드 | 시점 | 용도 |
|---|---|---|
preHandle() |
Controller 호출 전 | 로그인 검사, 인증 체크 등 |
postHandle() |
Controller 호출 후, View 렌더링 전 | 모델 수정, 로깅 |
afterCompletion() |
모든 처리가 끝난 후 | 리소스 정리, 에러 로그 등 |
사용 예시
- 로그인 여부를 체크하는 Interceptor
public class LoginCheckInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
Object user = request.getSession().getAttribute("loginUser");
if (user == null) {
response.sendRedirect("/login");
return false;
}
return true;
}
}
2. 등록 (WebMvcConfigurer)
@Configuration
public class WebConfig implements WebMvcConfigurer {
@Override
public void addInterceptors(InterceptorRegistry registry) { // 어떤 인터셉터를 쓸 지
registry.addInterceptor(new LoginCheckInterceptor())
.addPathPatterns("/**") // 전체 경로에 적용 (적용할 경로)
.excludePathPatterns("/login", "/css/**", "/js/**"); // 제외할 경로
}
}
✅ InterceptorRegistry란 ?
Spring MVC에서 Interceptor를 등록하고 경로 설정할 수 있도록 도와주는 도구(객체)로, 이 프로젝트에 어떤 인터셉터를 어떤 경로에 적용할지 등록하는 명단 작성 도구에 해당한다.
즉, InterceptorRegistry는 스프링이 제공하는 인터셉터 등록부이고, addInterceptors() 안에서 "누굴 어디에 적용할지" 정해주는 역할을 담당한다.
🟣AOP (Aspect-Oriented Programming)
관점 지향 프로그래밍
→ 핵심 로직과 공통된 부가기능(=관심사)을 깔끔하게 분리해서 모듈화하는 기술
AOP의 등장배경
logger.info("시작");
long start = System.currentTimeMillis();
userService.saveUser(user); // 핵심 로직
long end = System.currentTimeMillis();
logger.info("끝, 소요 시간: {}ms", end - start);
다음처럼 로깅할 때마다 모든 서비스에 시작과 끝을 적어야한다면 가독성이 나빠지므로 이러한 공통 처리를 한 곳에서 관리하는 것이 바로 AOP이다.
AOP 용어정리
| 용어 | 의미 |
|---|---|
| Aspect | 공통 로직을 담은 클래스 |
| Advice | 실제 실행될 로직 (before, after 등) |
| JoinPoint | Advice가 적용될 수 있는 지점 |
| Pointcut | Advice가 언제 실행될지 조건 설정 |
| Target | 실제 핵심 기능이 있는 클래스 |
| Weaving | Aspect + Target을 결합하는 과정 |
Advice 종류 (실행 타이밍) → 보통은 @Around 하나로 다 처리하는 경우가 많다 !
| 어노테이션 | 시점 | 사용 예 |
|---|---|---|
@Before |
메서드 실행 전 | 인증, 로깅 시작 |
@After |
메서드 실행 후 (무조건) | 리소스 해제 |
@AfterReturning |
정상 리턴 후 | 결과값 로깅 |
@AfterThrowing |
예외 발생 시 | 에러 로깅 |
@Around |
전/후 모두 + 예외 | 실행 시간 측정, 권한 검사, 로그 |
사용 예시
com.example.service 하위 모든 메서드 실행 시 걸린 시간을 측정하고 출력하는 기능
@Aspect // AOP 기능을 가진 클래스임을 선언 (관점 클래스)
@Component
public class LoggingAspect {
@Pointcut("execution(* com.example.service.*.*(..))")
public void allServiceMethods() {}
@Around("allServiceMethods()")
public Object logExecutionTime(ProceedingJoinPoint joinPoint) throws Throwable {
long start = System.currentTimeMillis(); // 현재 시간 측정 (실행 전)
Object result = joinPoint.proceed(); // 실제 메서드 실행
long duration = System.currentTimeMillis() - start; // 실행이 끝나고 걸린 시간 계산
// 어떤 메서드가 실행됐는지 로깅
System.out.println("[LOG] " + joinPoint.getSignature() + " 실행 시간: " + duration + "ms");
return result;
}
}
총 정리
Filter, Interceptor, AOP를 이해하기 위한 전체 흐름도는 다음과 같다.
[HTTP 요청]
↓
🔵 Filter
↓
DispatcherServlet
↓
🟠 Interceptor (preHandle)
↓
Controller → Service → Repository
↑
🟣 AOP (@Around)
↓
🟠 Interceptor (postHandle)
↓
Response
↓
🔵 Filter (response)
3줄 요약
| 기술 | 역할 비유 | 설명 요약 |
|---|---|---|
| 🔵 Filter | 🛡️ 정문 보안관 | 요청이 들어오면 제일 먼저 확인, 감시하는 서블릿 레벨 기능(예: CORS, 인코딩, 인증 토큰 파싱) |
| 🟠 Interceptor | 👩💼 리셉션 직원 | 컨트롤러 입장 전에 신원 검사하고, 로그 남기거나 접근 차단하는 Spring MVC 전용 훅 |
| 🟣 AOP | 🎥 감독관/카메라 | Service/Bean 메서드 실행 전후로 모든 행동을 감시, 실행 시간, 트랜잭션, 에러 로깅 등 처리 |
'Backend > Spring' 카테고리의 다른 글
| AWS SDK v1 기반 S3 이미지 업로드 구현 (Spring Boot 연동 포함) (0) | 2025.05.13 |
|---|---|
| 커스텀 어노테이션 (0) | 2025.04.22 |
| 회원 탈퇴를 구현하며 겪은 Soft Delete 설계와 Jackson의 LocalDateTime 직렬화 문제 (0) | 2025.04.11 |
| Helper Class (0) | 2025.04.02 |
| ServiceHelper를 통한 Service 코드 책임 분리 (0) | 2025.04.01 |