HTTP 요청이 컨트롤러까지 오는 과정
웹 애플리케이션을 개발하다 보면, 클라이언트에서 API 요청이 들어와서 컨트롤러에 도달하고, 다시 응답이 돌아가는 과정이 어떻게 내부적으로 흘러가는지 궁금할 때가 많습니다. 이번 글에서는 Spring Boot 기반 애플리케이션에서 HTTP 요청이 지나가는 전체 흐름을
Filter → DispatcherServlet → Interceptor → AOP → Controller
순서로 각 단계에서 어떤 역할을 하는지 정리해보겠습니다.
전체 흐름 요약
[클라이언트 요청]
↓
Filter (Servlet API)
↓
[DispatcherServlet]
↓
Interceptor
↓
AOP (Aspect-Oriented Programming)
↓
Controller (@RestController, @Controller)
↓
AOP AfterReturning
↑
Interceptor postHandle / afterCompletion
↑
Filter 응답 처리
↑
[클라이언트 응답 완료]
1. Filter 란?
Filter는 Servlet API의 표준 인터페이스로 웹 애플리케이션에서 들어오는 HTTP 요청(Request), 나가는 HTTP(Response) 를 가로채서 전처리와 후처리를 할수 있습니다.
이 필터는 DispatcherServlet보다 먼저 실행되므로, Spring MVC 처리 이전에 공통 로직을 수행하는 데 적합합니다.
Spring Boot에서는 @Component 또는 FilterRegistrationBean을 사용하여 필터를 등록할 수 있습니다.
예시 코드
@Component
public class MyFilter implements Filter {
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
System.out.println(">>> Filter: 요청 시작");
chain.doFilter(request, response); // 다음 단계로 넘김
System.out.println("<<< Filter: 응답 완료");
}
}
Filter, Interceptor, AOP 차이점 알고가기
이 세가지는 모두 공통 관심사(Cross-Cutting Concern) 를 처리하기 위한 용도지만 동작시점, 위치, 용도가 다릅니다.
공통 관심사란?
- 비즈니스 로직과 직접적 연관은 없지만 여러 컴포넌트에서 반복되는 부가기능을 뜻합니다.
- 로깅, 인증/인가, 트랜잭션 처리, 예외처리, CORS/압축/응답 캐싱 등이 있습니다.
언제 어떤걸 써야 될까
Filter
- HTTP 요청/응답 전체를 감싸야 할때 (예: CORS, 로깅)
- 이유: DispatcherServlet 밖에서 동작하기 때문
Interceptor
- 로그인 체크, 세션 검사, URI 기반 권한 분기 처리
- 이유: 컨트롤러 진입 전후 로직 제어에 적절함
AOP
- 트랜잭션, 성능 측정, 공통 로직 삽입 처리
- 이유: 서비스, 비즈니스 로직 중심 처리 가능
표로 정리한 차이점
항목 | Filter | Interceptor | AOP |
---|---|---|---|
소속 | Servlet 스펙 (javax.servlet) | Spring MVC | Spring AOP |
실행 시점 | DispatcherServlet 이전/이후 | Controller 전/후 | 메서드 호출 전/후, 예외 발생 시 |
적용 대상 | 모든 HTTP 요청 | 컨트롤러(HandlerMapping 대상) | 빈(Bean)의 메서드 |
주요 용도 | 인증, 보안, 로깅, CORS 처리 등 | 인증, 로깅, 요청/응답 가공 | 트랜잭션, 로깅, 공통 로직 처리 |
제어 방식 | doFilter() | preHandle() , postHandle() 등 | @Aspect , @Around , @Before 등 |
Spring 의존성 | 없음 (Servlet API) | 있음 (Spring MVC) | 있음 (Spring AOP) |
설정 방법 | @Component 또는 Bean 등록 | WebMvcConfigurer 로 등록 | @Aspect , AOP 설정 필요 |
비즈니스 연관성 | 낮음 (단순 흐름 제어) | 중간 (Controller 레벨 로직 제어) | 높음 (핵심 로직 주변에 적용) |
2. DispatcherServlet
DispatcherServlet 은 클라이언트 요청을 어떤 컨트롤러가 처리할지 결정하고 결과를 돌려주는 프론트 컨트롤러 역할을 합니다.
Spring Boot는 기본적으로 내장 Tomcat을 통해 요청을 받고, 그 요청은 DispatcherServlet 으로 전달됩니다. 이후 핸들러 매핑을 통해 요청을 적절한 컨트롤러 메서드에 분배하고 응답을 반환하는 역할을 합니다.
- 어떤 컨트롤러를 실행할지 찾음
- HandlerMapping → HandlerAdapter → Controller 실행
기본적으로 SpringBoot에서 DispatcherServlet은 자동 등록되지만 수동으로 Bean 등록을 하여 커스터마이징 할수도 있습니다.
DispatcherServlet 내부 동작 흐름
1. 클라이언트 요청
- 클라이언트가 HTTP 요청 전송 => DispatcherServlet 으로 전달
2. 핸들러 매핑 (Handler Mapping)
- 요청 URL에 맞는 컨트롤러 메서드를 찾기위해 핸들러 매핑을 사용합니다.
- 예:
/users
요청이 들어올 경우@GetMapping("/users")
메서드 호출
3. 핸들러 어댑터 (Handler Adaptor)
- 요청을 처리할수 있도록 핸들러 어댑터가 컨트롤러 메서드와 요청을 연결합니다.
- 요청을 실행 가능한 상태로 변환하는 역할
4. 컨트롤러 실행
- 컨트롤러 메서드가 실행되면서 비즈니스 로직을 처리하고 HTTP 응답을 생성합니다.
5. 응답 반환
- 컨트롤러에서 반환된 HTTP 응답은 HttpMessageConverter 를 사용해 JSON 또는 XML 형태로 클라이언트에 반환됩니다.
3. Interceptor
Interceptor 는 HandlerInterceptor 인터페이스를 구현하며 HTTP 요청이 컨트롤러에 도달하기 전/후, 그리고 응답이 완료된 후에 사용자 정의 로직을 처리할수 있도록 하는 컴포넌트 입니다.
- Filter, DispatcherServlet 이후
- Controller 실행 전/후
Interceptor의 주요 메서드
preHandle()
- 호출 시점: 컨트롤러 실행 전
- 용도: 인증, 로깅, 요청 차단
postHandle()
- 호출 시점: 컨트롤러 실행 후
- 용도: 응답 데이터 가공, 로깅
afterCompletion()
- 호출 시점: 응답완료 이후
- 용도: 리소스 정리, 예외 로깅처리
예시 코드
@Component
public class LoggingInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request,
HttpServletResponse response, Object handler) {
System.out.println("요청 URI: " + request.getRequestURI());
return true; // false 반환 시 요청 중단
}
@Override
public void postHandle(HttpServletRequest request,
HttpServletResponse response,
Object handler,
ModelAndView modelAndView) {
System.out.println("컨트롤러 처리 완료");
}
@Override
public void afterCompletion(HttpServletRequest request,
HttpServletResponse response,
Object handler, Exception ex) {
System.out.println("응답 완료 후 실행");
}
}
Filter, Interceptor 비교
Filter는 서블릿 레벨의 전처리에 사용되고 Interceptor는 컨트롤러 진입 전후의 공통 비즈니스 로직을 처리에 사용됩니다.
기능 | Filter | Interceptor |
---|---|---|
요청 인코딩 설정 | ✅ 가능 | ❌ 부적합 |
CORS 처리 | ✅ 자주 사용 | ❌ 일반적으로 사용 안 함 |
세션/쿠키 검사 | 가능 (시스템적으로) | ✅ 매우 자주 사용 |
로깅 / 모니터링 | 가능 (전체 요청) | ✅ 컨트롤러 진입 기준으로 |
인증 / 인가 | 가능하지만 비추천 | ✅ 적합 (JWT, Role 체크 등) |
파일 업로드 제한 | ✅ 가능 (request size 제어) | ❌ 사용 불가 |
REST API 전처리 | 가능 | ✅ 더 자연스러움 |
4. AOP
AOP는 관점 지향 프로그래밍(Aspect-Oriented Programming) 의 약자로 비즈니스 로직외에 반복되는 공통처리 로직(보안, 로깅, 트랜잭션)들을 분리해서 모듈화 하는 프로그래밍 방식입니다.
Spring Boot 에서 AOP 사용 목적
- 메서드 수준의 로직 전/후 처리가 필요한 경우
- 공통 로직을 모듈화 하여 여러 레이어(Controller, Service, Repository)에 일괄 적용하고 싶을 때
- 조건이 단순 URL이 아닌 패키지, 어노테이션, 파라미터, 반환 타입 등
예시 코드
@Aspect
@Component
public class LoggingAspect {
// 포인트컷: 모든 Controller 메서드
@Pointcut("execution(* com.example.demo.controller..*(..))")
public void controllerMethods() {}
// 전처리 Advice
@Before("controllerMethods()")
public void logBefore(JoinPoint joinPoint) {
System.out.println("[Before] 호출된 메서드: " + joinPoint.getSignature().getName());
}
// 후처리 Advice
@AfterReturning(pointcut = "controllerMethods()", returning = "result")
public void logAfter(Object result) {
System.out.println("[After] 반환값: " + result);
}
// Around Advice (실행 전/후 모두 처리)
@Around("controllerMethods()")
public Object logAround(ProceedingJoinPoint pjp) throws Throwable {
long start = System.currentTimeMillis();
Object result = pjp.proceed(); // 원래 메서드 실행
long end = System.currentTimeMillis();
System.out.println("실행 시간: " + (end - start) + "ms");
return result;
}
}
AOP 주요 어노테이션 정리
1. @Aspect
- 클래스가 AOP를 수행함을 뜻하는 어노테이션 입니다. @Component와 함께 사용하여 Spring Bean 으로 등록합니다.
@Aspect
@Component
public class LoggingAspect {
// AOP 로직 작성
}
2. @Pointcut
- 공통 로직을 적용할 대상 지점(Join Point)을 적용합니다.
- 메서드 이름이나 복잡한 조건도 조합하여 사용할수 있습니다.
- 형식: execution(), within(), @annotation(), bean() 등..
@Pointcut("execution(* com.example.service..*(..))")
public void allServiceMethods() {}
3. @Before
- 대상 메서드 실행전에 수행됩니다.
- 인증, 파라미터 검증, 사전 로깅등 선처리 작업에 사용됩니다.
@Before("allServiceMethods()")
public void beforeLog(JoinPoint joinPoint) {
System.out.println("메서드 실행 전: " + joinPoint.getSignature().getName());
}
4. @After
- 대상 메서드 실행후에 항상 수행됩니다.
- 메서드의 정상 실행 여부와 관계없이 실행되며 리소스 정리등에 유용합니다.
@After("allServiceMethods()")
public void afterLog() {
System.out.println("메서드 실행 후 무조건 수행");
}
5. @AfterReturning
- 대상 메서드가 정상적인 리턴일때만 수행됩니다.
- 결과 데이터의 후처리, 반환값 로깅 처리에 사용됩니다.
returning
속성으로 실제 반환값을 파라미터로 받을수 있습니다.
@AfterReturning(pointcut = "allServiceMethods()", returning = "result")
public void afterReturningLog(Object result) {
System.out.println("정상 반환 값: " + result);
}
6. @AfterThrowing
- 대상 메서드가 예외를 리턴할때 수행됩니다.
- 예외 로깅, 알림 전송, 트랜잭션 롤백등 예외 처리 용도로 사용합니다.
@AfterThrowing(pointcut = "allServiceMethods()", throwing = "ex")
public void afterThrowingLog(Exception ex) {
System.out.println("예외 발생: " + ex.getMessage());
}
7. @Around
- 대상 메서드의 전/후 처리를 모두 수행합니다.
- 로직 실행 전/후, 예외처리, 실행시간 측정 등 모든 처리를 하기때문에 AOP 로직에 자주 사용됩니다.
ProceedingJoinPoint
를 통해 대상 메서드를 직접 실행 proceed() 해야 합니다.
@Around("allServiceMethods()")
public Object aroundLog(ProceedingJoinPoint joinPoint) throws Throwable {
long start = System.currentTimeMillis();
Object result = joinPoint.proceed(); // 대상 메서드 실행
long end = System.currentTimeMillis();
System.out.println("실행 시간: " + (end - start) + "ms");
return result;
}
어노테이션 | 역할 | 주요 용도 | 설명 / 예시 |
---|---|---|---|
@Aspect | Aspect 클래스 정의 | AOP 대상 클래스 선언 | 해당 클래스가 AOP 역할(공통 관심사)을 수행함을 명시 |
@Pointcut | 공통 로직을 적용할 대상 지점 정의 | 실행 대상 지정 (컨트롤러, 서비스 등) | 메서드 실행, 어노테이션, 패키지 등 다양한 조건으로 지정 가능 |
@Before | 대상 메서드 실행 전 수행 | 로깅, 인증 체크 등 | @Before("포인트컷") |
@After | 대상 메서드 실행 후 (무조건) 수행 | 리소스 정리, 로그 출력 등 | 정상/예외 관계없이 무조건 실행 |
@AfterReturning | 대상 메서드 정상 종료 후 수행 | 결과 로깅, 후처리 등 | 반환값 활용 가능 |
@AfterThrowing | 대상 메서드 예외 발생 시 수행 | 예외 로깅, 알림 | 예외 정보 캐치 가능 |
@Around | 대상 메서드 전/후 모두 제어 | 실행 시간 측정, 트랜잭션 등 | 가장 유연하고 강력한 Advice |
'WEB > Spring' 카테고리의 다른 글
Spring vs SpringBoot 비교, 차이점 알아보기 (2) | 2025.06.04 |
---|---|
Spring Boot CORS 사용 목적과 브라우저 보안 SOP (0) | 2025.06.04 |
TDD(테스트 주도 개발, Test-Driven Development), Junit (0) | 2024.11.05 |
의존성 주입 애노테이션 @RequiredArgsConstructor, @AllArgsConstructor, @NoArgsConstructor 핵심 정리 (0) | 2024.10.31 |
[Spring] 의존관계 주입(Dependency Injection) 개념과 Bean 중복방지 (0) | 2023.12.14 |
댓글