이끌든지 따르든지 비키든지

Framework/Spring

[Spring] Dispatcher-Servlet 알아보기

SeongHo5 2024. 2. 2. 21:44

들어가기 앞서 - 서블릿(Servlet)이란?

동적 웹 페이지(Dynamic Web Page) 생성에 사용되는 서버 사이드 프로그램입니다.

HTTP 프로토콜을 사용하며, 클라이언트의 요청에 대한 처리와 그 결과를 클라이언트에게 반하는 역할을 합니다.

서블릿은 일반적으로 HTML, JSON, XML 등의 형태로 데이터를 클라이언트에 전송하며, 이를 통해 동적인 웹 페이지나 웹 애플리케이션을 구현할 수 있습니다.

 

디스패처 서블릿(Dispatcher-Servlet)이란?


 

디스패처 서블릿은  웹 애플리케이션에서 들어오는 모든 요청을 가장 먼저 받아 각 요청을 알맞은 컨트롤러로 전달하는 역할을 하는데, 이는 프론트 컨트롤러 패턴을 구현한 것입니다.

 

💡프론트 컨트롤러 패턴?
프론트 컨트롤러 패턴은 웹 애플리케이션 아키텍처에서 사용되는 디자인 패턴 중 하나이다. 이 패턴의 주된 목적은 클라이언트의 모든 요청을 단일 진입 지점을 통해 받아들이고, 공통 처리 작업을 수행한 후 적절한 핸들러에 요청을 전달하는 것이다.

 

 

 

디스패처 서블릿의 동작 과정


디스패처 서블릿의 처리 과정을 모식화하면 아래와 같습니다.

 

HTTP 요청의 처리 과정 (ResponseEntity를 반환하는 경우)

  1. 요청 수신
    1. 서블릿 필터 전처리: 요청이 들어오면, 먼저 서블릿 필터(Servlet Filter)를 통해 인코딩 설정 등의 전처리 작업을 수행합니다.
    2. 요청 수신: 서블릿 필터를 통과한 요청을 디스패처 서블릿이 수신합니다.
  2. 핸들러 매핑 조회
    1. URL 매핑 조회: 요청 URL을 분석하고, 핸들러 매핑 정보를 조회합니다.
    2. 핸들러 선택: 매핑 정보에 따라 요청을 위임할 컨트롤러와 해당 메서드(핸들러)를 선택합니다.
  3. 핸들러 실행
    1. 핸들러 어댑터 호출: 핸들러 어댑터를 통해 메서드의 파라미터를 바인딩하고, 메소드를 호출합니다.
    2. 컨트롤러 메소드 실행: (개발자가 작성한) 컨트롤러 메서드가 실행되어 비즈니스 로직을 처리하고, View 또는 ResponseEntity 객체를 반환합니다.
  4. 뷰 리졸버와 뷰 렌더링 (뷰 반환 시)
    1. 뷰 이름 리졸빙: 컨트롤러가 문자열 형태의 뷰 이름을 반환하면, 디스패처 서블릿은 ViewResolver를 사용하여 이 이름에 해당하는 실제 뷰 객체를 찾습니다.
    2. 모델 데이터 준비: 컨트롤러에서 반환된 모델 데이터는 뷰에 전달되어 데이터를 화면에 표시할 수 있도록 합니다.
    3. 뷰 렌더링: 준비된 모델 데이터와 뷰 객체를 사용하여 최종적인 HTML 페이지를 생성해 반환합니다.
  5. ResponseEntity 반환 (HTTP 응답 직접 제어 시)
    1. HTTP 응답 생성: 컨트롤러 메서드가 ResponseEntity 객체를 반환하는 경우, 이 객체는 HTTP 응답의 본문(Body), 상태 코드, 헤더 등을 직접 제어할 수 있습니다.
    2. 응답 전송: ResponseEntity에 설정된 정보에 따라 HTTP 응답이 구성되고, 클라이언트에게 직접 전송됩니다. 이 경우 별도의 뷰 리졸빙이나 렌더링 과정은 거치지 않습니다.

어떻게 요청을 위임할 컨트롤러를 찾을까?

스프링 MVC에서 URL 정보를 통해 요청을 위임할 컨트롤러를 찾는 과정은 핸들러 매핑(Handler Mapping)을 통해 이루어집니다. 스프링에는 여러 핸들러 매핑 전략이 존재하지만, 최근의 컨트롤러는 대부분 '@Controller', '@RestController' 어노테이션으로 구성하는 방식이기 때문에, 'RequestMappingHandlerMapping'이 가장 일반적으로 사용됩니다.

 

'RequestMappingHandlerMapping' 구현체는 요청 URL 패턴을 분석해, '@RequestMapping' 어노테이션에 지정된 패턴과 일치하는지 검사합니다. 검사 이후, 가장 정확하게 일치하는 핸들러(실제 요청을 처리할 컨트롤러 메서드)를 선택해HandlerExecutionChain 객체에 담아 반환하고, 이를 받은 디스패처 서블릿이 핸들러를 실행하기 위한 핸들러 어댑터를 선택하는 등 요청 처리를 이어갑니다.

 

💡핸들러를 HandlerExecutionChain 객체에 담아 반환하는 이유
요청을 처리하기 위해 결정된 핸들러뿐만 아니라, 요청 처리 전·후에 실행되어야 할 인터셉터(interceptor) 목록도 함께 관리하기 위해서이다.
이 체인으로 인터셉터 목록을 관리하고, 인터셉터의 실행 순서를 관리해 일관된 처리 흐름을 보장한다.

 


 

핸들러 어댑터는 어떻게 파라미터 바인딩 / 응답 직렬화 등을 처리할까?

- 파라미터 바인딩

 

'HandlerMethodArgumentResolver' 인터페이스를 구현한 ArgumentResolver를 사용하여 다양한 종류의 메서드 파라미터를 처리합니다.

 

@RequestParam, @PathVariable 어노테이션은 URL 쿼리 파라미터나 경로 변수를 메소드 파라미터로 바인딩하고, @RequestBody 등 요청 본문에 포함된 데이터는 'HttpMessageConverter'를 사용하여 요청 본문의 내용을 자바 객체로 변환합니다.

 

- 응답 직렬화

 

'HandlerMethodReturnValueHandler' 인터페이스를 구현한 ReturnValueHandler를 사용하여 메서드의 반환 값에 따라 적절한 응답 생성 전략을 선택합니다.

 

컨트롤러 메서드가 @ResponseBody나 ResponseEntity 타입 값을 반환한 경우, 'RequestResponseBodyMethodProcessor'가 반환값 처리를 담당합니다.

 

RequestResponseBodyMethodProcessor의 handleReturnValue 메서드는

// RequestResponseBodyMethodProcessor.java

    @Override
	public void handleReturnValue(@Nullable Object returnValue, MethodParameter returnType,
			ModelAndViewContainer mavContainer, NativeWebRequest webRequest)
			throws IOException, HttpMediaTypeNotAcceptableException, HttpMessageNotWritableException {
		mavContainer.setRequestHandled(true);

		ServletServerHttpRequest inputMessage = createInputMessage(webRequest);
		ServletServerHttpResponse outputMessage = createOutputMessage(webRequest);

		if (returnValue instanceof ProblemDetail detail) {
			outputMessage.setStatusCode(HttpStatusCode.valueOf(detail.getStatus()));
			if (detail.getInstance() == null) {
				URI path = URI.create(inputMessage.getServletRequest().getRequestURI());
				detail.setInstance(path);
			}
		}

		writeWithMessageConverters(returnValue, returnType, inputMessage, outputMessage);
	}
  1. ModelAndViewContainer를 통해 요청이 처리되었음(true)을 설정합니다. - mavContainer.setRequest…
  2. 응답 메시지를 생성합니다. - createInputMessage / createOutputMessage   
    1. InputMessage : 'Accept' 헤더를 검사하는 데 사용합니다.
    2. OutputMessage : 응답 헤더, 상태 코드 등을 설정하는 데 사용합니다.
  3. 반환 값을 HTTP 응답 본문으로 직렬화하고 응답을 구성합니다. - writeWithMessageConverters

writeWithMessageConverters 메서드는 본문(content)을 Content-Type에 따라 JSON, XML 등 형식으로 처리합니다.

 


(참고)

https://mangkyu.tistory.com/18