본문 바로가기
Spring

Spring HTTP 처리 과정

by CodingMasterLSW 2025. 6. 29.

흐름도 그림

위의 그림을 보면서, 요청이 전달되는 과정에서 각 단계별 어떤 역할을 하는지 간단하게 이해하고 넘어가 보자.

 

Tomcat

- Client/ Web Server의 HTTP 요청을 HttpServletRequest 객체로 변환한다.

 

Filter

- 인증/인가를 Filter를 통해 검증한다. ex) 유효한 권한을 가진 사람의 요청인가?

만약 권한이 없는 사람이라면, 예외를 발생시킨다.

 

Filter에서 권한 오류가 발생한다면, 해당 오류는 @ControllerAdvice / @ExceptionHandler에서 잡을 수 없다. 위의 그림을 보면 알 수 있듯이, DispatcherServlet 앞단에 Filter가 위치하기 때문이다.

 

DispatcherServlet (Front Controller)

- HttpServletRequest 요청을 읽고, 적절한 핸들러에게 수행 역할을 위임한다. 누구에게 일을 시킬지 결정하는 팀 리더 같은 개발자라고 생각하면 편하다.

 

HandlerMapping

- HTTP URI를 읽고, 해당 URI와 일치하는 Controller 메서드가 있는지 탐색한다.

@PostMapping("/login-crew")
public Token crewLogin(@RequestBody LoginRequestDto loginRequestDto) {
    return loginService.crewLogin(loginRequestDto);
}

 

보통 Controller 메서드 상단에 HttpMethod + uri를 명시하는데, HandlerMapping이 이와 일치하는 uri를 탐색한다. 이후, 일치하는 메서드가 존재한다면, HandlerMapping이 HandlerExecutionChain 객체를 DispatcherServlet에게 반환한다.

 

TMI : HandlerExecutionChain 객체 내부에는 Handler 객체(해당 Controller의 빈 객체 + 메서드 참조값), Handler 객체에 해당하는 Interceptor List가 들어있다.

package org.springframework.web.servlet;

public class HandlerExecutionChain {

    private static final Log logger = LogFactory.getLog(HandlerExecutionChain.class);

    private final Object handler;

    private final List<HandlerInterceptor> interceptorList = new ArrayList<>();

    private int interceptorIndex = -1;

 

Interceptor

요청에 대해서 일치하는 uri가 존재한다면, 미리 정의해 둔 메서드(preHandle)를 실행시킨다. Filter와 비슷하지만 조금 더 세밀하게 제어할 수 있음. HandlerExecutionChain에 속한 List <Interceptor>를 순회하면서 preHandle 메서드를 실행한다.

 

 

예시코드(안 읽어도 상관없음)

@Component
public class CoachAuthorizeInterceptor implements HandlerInterceptor {

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response,
        Object handler) throws Exception {
        String authToken = request.getHeader("Authorization");
        String token = resolveToken(authToken);
        Claims claims = jwtProvider.getClaimsAndValidateToken(token);
        String memberTypeStr = (String) claims.get("memberType");
        MemberType memberType = MemberType.valueOf(memberTypeStr);
        if (!memberType.name().equals("COACH")) {
            throw new IllegalStateException("코치만 접근 가능합니다.");
        }
        return HandlerInterceptor.super.preHandle(request, response, handler);
    }
@Configuration
public class WebConfig implements WebMvcConfigurer {

    private final CoachAuthorizeInterceptor coachAuthorizeInterceptor;

    public WebConfig(CoachAuthorizeInterceptor coachAuthorizeInterceptor) {
        this.loginArgumentHandler = loginArgumentHandler;
        this.coachAuthorizeInterceptor = coachAuthorizeInterceptor;
        this.crewAuthorizeInterceptor = crewAuthorizeInterceptor;
    }
 
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(coachAuthorizeInterceptor)
            .addPathPatterns("/coaches/**")
            .addPathPatterns("/coach-reservations/**");
    }
}

 

HandlerAdapter

컨트롤러 실행을 명령한다. 또한 HTTP 요청으로부터 필요한 파라미터들을 추출하고, Controller 메서드의 파라미터 타입에 맞게 변환 및 바인딩한다. ex) @RequestParam, @RequestBody, @PathVariable 등의 애노테이션 처리

 

이후에 실행된 결괏값 (View)를 DispatcherServlet에게 다시 전달해 준다.


정리해 보니까 어때?

스프링 요청에 대한 흐름은 반드시 알고 있어야 한다고 생각한다. 뭐 몰라도 개발할 수는 있겠지만, 특정 부분에서 오류가 발생했을 때 원인을 파악하고 훨씬 더 빠르게 대응할 수 있다고 생각한다. 또, 흐름을 한 번 이해하면 개발하는 데 있어 조금 더 적절한 선택지를 고를 수 있다고 생각한다. 무작정 다른 사람들의 코드를 따라 하는 게 아니라, 나만의 기준을 세울 수 있다고 해야 할까...? 

 

ex1) 로그를 Filter와 Interceptor 어디서 찍어야 할까?

ex2) 권한 검증을 Filter와 Interceptor 어디서 해야 할까?

 

스프링은 참 어렵다. 그래도 한 번 정리하니깐 요청/응답에 대해서 전체적인 감은 잡혀서 다행이다...!

 

잘못된 내용에 대한 피드백은 언제든지 환영입니다!

'Spring' 카테고리의 다른 글

Spring Core 꼬리 질문 해보기  (0) 2025.04.28
Spring Response/Request 어노테이션  (0) 2025.04.16