[Spring] Spring MVC 구조 복습기 Ch.01

Eunbi Lee·2025년 9월 21일

SeaVantage

목록 보기
7/11
post-thumbnail

1️⃣ Background

회사에서 동료 책임님과 함께 Controller unit test 를 개선시킨 경험이 있다. 이때, 오랜만에 마주한 Spring 내부 구조는 생소했던 기억이 있다.

따라서, 관련 개념인 DispathcerSevlet, 그리고 Handler, View 관련 개념을 정리해보고자 한다.

  • Filter 는 다음 포스팅에서.

2️⃣ Spring MVC (Model-View-Controller)


(출처) 김영한 스프링 MVC 1편

전체적인 프로세스는 다음과 같다.

1) 사용자가 HTTP 요청을 보낸다.

  • 쉽게 말해, 웹 브라우저 상에서 우리가 하는 모든 동작이 HTTP 요청이라고 볼 수 있다.

2) Spring boot 는 Dispatcher Servlet 을 Servlet 으로 자동 등록 후, 모든 경로인 urlPatterns="/") 에 대해 등록한다.

Concept

  • Servlet 이란 Java 기반의 server 측 프로그램으로, HTTP 요청응답을 처리해 웹 페이지를 동적으로 생성하기 위한 데이터 입출력 창구(인터페이스)이며, 실제 내용(동적 페이지)은 Servlet(코드)에서 처리해 이 Response에 담겨 브라우저로 전송된다.
    • a. 클라이언트의 요청(HTTP)을 받으면 그 메시지를 자동으로 파싱한다.
    • b. 개발자가 쉽게 사용할 수 있도록 HttpServletRequest, HttpServletResponse 객체를 제공

Code

  • DispatcherServlet.doDispatch() ➡️ FrameworkServlet ➡️ HttpServletBean ➡️ HttpServlet.service()

3) Handler mapping 을 통해 요청 URL 에 매핑된 핸들러인 Controller 를 조회한다.

4) Controller 를 실행할 수 있는 Handler Adapter 를 조회한다.

5) Handler Adapter 를 실행한다.

6) Handler Adapter 가 Controller 를 실행한다.

Code

  • DispatcherSevlet ➡️ HandlerAdapter ➡️ Controller

7) HandlerAdapter 가 Controller 가 반환하는 정보를 ModelAndView 로 변환해서 반환한다.

8) ViewResolver 를 조회 후 실행한다.

9) ViewResolver 는 뷰의 논리 이름(컨트롤러가 반환하는 화면 이름) 을 물리 이름(실제 파일 경로와 이름) 으로 바꾸고, 렌더링 역할을 담당하는 View Object를 반환한다.

Concept

  • Randering 이란, 서버에서 동적으로 생성된 화면(HTML, JSON 등) 데이터를 사용자(웹 브라우저 등)가 볼 수 있게 출력하는 작업이다.

10) View (object) 를 통해 렌더링한다.

Concept

  • View object 란, 데이터(Model)에 따라 실제로 화면 콘텐츠를 생성하고 HTTP 응답 본문에 넣어 클라이언트로 전송하는 역할을 한다.

3️⃣ Make use of Spring MVC

Spring MVC 구조를 실무에선 어떻게 직접적으로 맞닥뜨릴 수 있을까?

그리 멀지 않게 발견할 수 있다.

바로, HTTP 요청 처리를 담당하는 핸들러인 Controller Unit Test 에서 볼 수 있다.

만약, Controller Unit Test 가 실패했을 경우 우리는 다음과 같은 화면을 심심치 않게 마주할 수 있다.

로그만 보더라도 이미 원인을 추측할 수 있지만, 우리가 볼 것은 해당 예외가 던져진 구간이 HttpServlet.service()인 점이다.

DispatcherServlet 은 부모 클래스에서 HttpServlet 을 상속 받아서 사용하고, Servlet 으로 동작하기에 최종적으로 호출된 부분은 HttpServlet.service() 이다.

그리고 HttpServlet 의 요청 정보인 Request 에 대해 친절하게 알려준다.

  • HTTP Method 는 무엇인지
  • 요청을 매핑할 URL 은 무엇인지
  • Request Parameter 은 무엇인지
  • Header 에는 무슨 값이 담겨 있었는지
  • Body 에는 무슨 content 가 있었는지, .. etc.

그리고 실질적인 요청을 처리할 수 있는 핸들러에 대한 정보도 알려준다.

실질적인 예외의 원인을 알려주는 부분이다.

  • NoResourceFoundException 이므로, 리소스를 찾을 수 없다는 의미이다.
    • 즉, URL 매핑이 잘 안되었을 가능성이 ⬆️

그리고, 최종적으로 DispatcherSevlet 이 반환하려고 했던 Response 에 대한 정보도 알려준다.

  • 404 이므로, 역시 리소스를 찾을 수 없었던 원인이 맞았다.

Controller 관련 테스트에서 에러가 터졌을 경우, 핵심은 이 부분이다.

Simple Example

  1. NoSuchBeanDefinitionException
org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'com.example.demo.controller.ExampleController' available

위 예외는 실실적인 요청을 처리할 핸들러인 Controller 를 직접 조회(테스트)하는데, 해당 컨트롤러가 스프링 빈으로 등록되지 않거나, 의존성이 주입되지 않은 경우 발생한다.

  • 이 경우, DispatcherServlet 이 Controller 를 정상적으로 조회하지 못한 경우이므로 관련 의존성을 주입해주면 된다는 사실을 알 수 있다.
  1. UnsatisfiedDependencyException
org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'exampleController' defined in file [...] Unsatisfied dependency expressed through constructor parameter 0

위 예외는 Controller가 필드 또는 생성자를 통해 의존하는 Service 및 Repository Bean 이 없어서 매핑 자체가 실패하는 경우 발생한다.

  • 이 경우, Service (및 Repository) 클래스를 @MockBean 을 통해 Spring 컨테이너에 mock 객체를 Bean 으로 등록하고 DI로 주입하면 된다.
  1. NoHandlerFoundException
org.springframework.web.servlet.NoHandlerFoundException: No handler found for GET /invalid-path

예시로 알려준 예외이다.

즉, 간단하게 Controller Unit Test 에서도 Spring MVC 의 구조를 이해하고 있어야 어떤 원인으로 테스트가 실패했는지 파악할 수 있기 때문에 빠르게 감을 잡을 수 있다.

  • 그렇지 않으면, 로그를 분석하는 것부터 시간이 조금 걸릴 수 있다. (과거의 나 ☠️)

4️⃣ Next: Spring Filter

다음은 Spring Filter 와도 연관지어서, 개선한 Spring Controller Unit Test 에 대해 저술해보도록 하겠다!

성능 테스트와 구현도 중요하지만, 이를 뒷받침하는 기본기도 중요하다는 걸 느끼는 요즘이다.

profile
안녕하세요, 개발자 비비입니다.

0개의 댓글