Spring MVC 구조와 동작 원리

민준·2025년 3월 3일
post-thumbnail

1. Spring MVC란?

Spring MVC는 Model-View-Controller 패턴을 기반으로 하는 프레임워크
이를 통해 클라이언트의 요청을 적절히 처리하고 응답을 반환하는 구조를 가짐.


2. Spring MVC의 핵심 흐름

  1. 클라이언트(웹 브라우저)가 요청을 보냄

  2. DispatcherServlet (Front Controller)이 요청을 받음

  3. HandlerMapping을 이용하여 적절한 Controller를 찾음

  4. HandlerAdapter를 이용하여 해당 Controller를 실행

  5. Controller가 로직을 수행하고 Model(데이터)을 View에 전달

  6. ViewResolver가 적절한 View를 선택하고 렌더링

  7. 최종적으로 클라이언트에게 HTML 응답을 반환


2.1. DispatcherServlet (디스패처 서블릿)의 역할

Spring MVC에서 DispatcherServlet은 "Front Controller" 역할

Front Controller: 모든 요청을 단일 진입점(DispatcherServlet)으로 받아 적절한 컨트롤러에게 위임하는 패턴

DispatcherServlet의 동작 과정

  • 클라이언트의 HTTP 요청을 받음
  • HandlerMapping을 사용해 어떤 컨트롤러가 처리할지 찾음
  • HandlerAdapter를 통해 해당 컨트롤러를 실행
  • 컨트롤러가 로직을 수행하고 데이터를 반환하면, 적절한 View를 찾아 렌더링
  • 최종적으로 클라이언트에게 응답을 반환

2.2. HandlerMapping과 HandlerAdapter

DispatcherServlet은 요청을 어떤 컨트롤러가 처리할지 찾는 과정이 필요
이를 돕는 것이 HandlerMapping과 HandlerAdapter

1). HandlerMapping (컨트롤러 찾기)

  • 요청이 들어오면 어떤 컨트롤러가 처리할 것인지 매핑하는 역할
  • Spring에서는 @RequestMapping이 달린 컨트롤러 메서드를 찾아줌

2). HandlerAdapter (컨트롤러 실행)

  • HandlerMapping이 찾은 컨트롤러를 실행하는 역할

  • 다양한 방식의 컨트롤러 호출을 지원하기 위해 존재 (예: @RequestMapping, HttpRequestHandler 등)

    protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException {
       if (this.handlerAdapters != null) {
           for (HandlerAdapter adapter : this.handlerAdapters) {
               if (adapter.supports(handler)) {
                   return adapter;
               }
           }
       }
       throw new ServletException("No adapter for handler [" + handler + "]");
    }
  • Spring MVC에서 가장 많이 사용하는 RequestMappingHandlerAdapter를 통해 @RequestMapping이 있는 컨트롤러가 실행됨


2.3. Controller의 역할

Controller는 클라이언트 요청을 받아 비즈니스 로직을 수행하고, 응답을 반환하는 역할을 합니다.

@Controller
@RequestMapping("/users")
public class UserController {

    @Autowired
    private UserService userService;

    @GetMapping("/{id}")
    public ResponseEntity<User> getUser(@PathVariable Long id) {
        User user = userService.getUserById(id);
        return ResponseEntity.ok(user);
    }
}
  • @Controller : 해당 클래스가 Spring MVC 컨트롤러임을 의미
  • @RequestMapping("/users") : "/users"로 시작하는 요청을 처리
  • @GetMapping("/{id}") : "/users/{id}" 요청을 처리
  • @Autowired : Spring이 자동으로 UserService를 주입

2.4. View의 역할

Spring MVC에서는 컨트롤러가 데이터를 처리한 후, View를 통해 사용자에게 응답을 반환

  • View는 HTML, JSON, XML 등의 형식으로 응답을 생성할 수 있음
  • Spring은 기본적으로 Thymeleaf, JSP, JSON (REST API 응답) 등을 지원

3. Spring Bean과 컨트롤러 객체 생성

Spring에서는 개발자가 new를 사용하지 않아도 객체가 자동으로 생성
이것은 Spring Container(스프링 컨테이너)Bean(빈)을 관리하기 때문

3.1. Spring Bean의 개념

  • Spring이 관리하는 객체를 Bean이라고 부름
  • Singleton(싱글톤) 패턴으로 관리 (한 개의 인스턴스를 공유)
  • Spring Container가 객체를 생성하고 필요할 때 자동으로 주입

3.2. Spring Bean 등록 방법

  1. 자동 등록 : @Component, @Service, @Repository, @Controller 사용
  2. 수동 등록 : @Configuration + @Bean 사용

3.3. Bean 주입 방식 (DI, Dependency Injection)

Spring에서는 의존성 주입(DI, Dependency Injection)을 통해 객체를 관리합니다.

1). 생성자 주입 (추천)

@Controller
@RequestMapping("/users")
@RequiredArgsConstructor
public class UserController {
    private final UserService userService;
}
  • final 키워드를 사용해 불변성 유지 가능
  • 순환 참조 문제를 컴파일 타임에 방지 가능

2). 필드 주입

@Controller
@RequestMapping("/users")
public class UserController {
    @Autowired
    private UserService userService;
}
  • 코드가 간결하지만 테스트 어려움 (Mock 주입 불가능)
  • Spring 5 이후에는 권장하지 않음

3). 수정자 주입 (Setter Injection)

@Controller
@RequestMapping("/users")
public class UserController {
    private UserService userService;

    @Autowired
    public void setUserService(UserService userService) {
        this.userService = userService;
    }
}
  • 선택적 주입이 가능하지만, 객체가 변경될 위험이 있음

4. Spring MVC 구조 전체 흐름

  1. 클라이언트 요청
  2. DispatcherServlet이 요청을 받음
  3. HandlerMapping을 이용해 적절한 컨트롤러를 찾음
  4. HandlerAdapter를 이용해 컨트롤러 실행
  5. 컨트롤러가 비즈니스 로직 수행 후 데이터를 반환
  6. ViewResolver가 적절한 뷰를 선택해 렌더링
  7. 클라이언트에게 최종 응답 반환

0개의 댓글