목표
- Spring의 간단한 변천사를 알아본다
- Spring MVC 구조를 이해한다
- Spring MVC의 동작을 이해한다
Spring의 발전과 MVC의 탄생
Spring Framework
- 2023년 기준으로 말하는
Spring Framework(이하 스프링)
은 스프링 5를 의미함
- 스프링을 말할 때는 보통
Spring WEB MVC(이하 스프링 MVC)
를 칭함
- 스프링 4.0에서
Spring Boot(이하 스프링부트)
가 등장하면서, 기존의 스프링은 Spring Legacy(스프링 레거시)
로 불리게 됨
Spring Framework 구조
Spring Framework Runtime
Java Servlet과 Servlet Container
Java Servlet
Java Servlet(이하 서블릿)
은 Common Gateway Interface(이하 CGI)
를 자바 클래스로 '잘' 구현하기 위한 프로그램으로 javax.servlet
패키지로 구현되어 있음
- 일반적으로 동적으로 페이지를 구현할 수 있는 프로그램이라고만 생각하지만, 서블릿 자체도 CGI 통신 규약을 통해 동작함
- 서블릿에 대한 자세한 내용은 이전에 공부했던 서블릿과 JDBC 아키텍처를 참고
Servlet Container
- 일반적으로 말하는
WEB Container/Servlet Container(이하 서블릿 컨테이너)
는 Apache Tomcat Web Application Server(이하 톰캣)
을 말함
- 톰캣은 내부에
Apache Web Server(이하 웹서버)
와 서블릿 컨테이너를 소유하고 있음
- 하나의 서블릿 컨테이너에는 하나의
Web Application
이 존재하고 있음, 하나의 톰캣에는 하나의 서블릿 컨테이너, 하나의 JVM
- 이로 인해 톰캣은 HTTP 요청을 웹서버로 받고, 웹서버는 서블릿 컨테이너에 해당 요청을 전달, 서블릿 컨테이너는 받은 요청을 보유한 서블릿과 매핑하여 응답을 전달함
여기서 잠깐, Servlet Context
- 하나의 톰캣 내에 여러 서블릿이 존재할 수 있음
- 서블릿 컨테이너는 HTTP 요청이 들어오면 새로운 쓰레드를 생성함
- 각 서블릿의 정보는
Servlet Context(이하 서블릿 컨텍스트)
에 공유 되며, 서블릿 컨텍스트 메소드를 통해 init()
, service()
, destroy()
로 서블릿 Life-Cycle(생명주기)를 관리함
Spring MVC 이전
- 서블릿을 통해 개발자는 HTTP 요청/응답을 처리할 수 있었지만, 서블릿의 설정과 생명주기, HTML 등을 직접 관리해야했기 때문에 굉장히 불편했음
- 이를 개선하기 위해
Java Server Page(이하 JSP)
가 탄생했지만, JSP는 페이지와 비즈니스 로직이 동시에 존재하는 치명적 단점이 존재했고, 객체지향 원칙 중 하나인 Single-responsibility principle(이하 SRP)
를 어기게 되었음
- SRP를 지키기 위해 페이지, 비즈니스 로직, 데이터를 각각 View, Controller, Model로 분리하는 MVC 패턴이 탄생함
Spring MVC의 구조
MVC 1
- 초기 스프링 MVC의 구조는 MVC 1이라고 하는 구조를 띄었음
- JSP내에서 비즈니스 로직이 분리되었지만 여전히
View
와 Controller
가 JSP내에 동시에 있었기 때문에 프로그램이 커질수록 굉장히 복잡
하고, JSP 내에 로직이 결합되어 있어 유지보수성이 최악
이었음
MVC 2
- MVC 1 패턴을 개선하여 MVC 2 패턴이 나왔고, 스프링 위원회가 채택하며 현재의 스프링 MVC는 MVC 2 패턴을 이야기 함
- MVC 1 패턴과 달리 MVC 2 패턴은
View
, Controller
가 분리되어 각 서비스 별로 수정이 가능함
- 스프링 MVC는
org.springframework.spring-webmvc
라는 이름의 라이브러리로 구현되어 있음
Spring MVC의 동작
Spring MVC의 특징
- 스프링 MVC의 가장 큰 특징은
DispatcherServlet(이하 디스패처 서블릿)
을 사용한 Front-Controller
패턴으로 각 컨트롤러 별 공통된 로직을 추출하여 필터링하고, 각 컨트롤러에 작업을 분배하는 역할을 수행
- 스프링의 MVC2 준수와 디스패처 서블릿의 동작
Spring MVC의 실행
- 스프링 MVC의 실행은 크게 디스패처 서블릿으로의 인입, 각 컨트롤러로 분배, 서비스 호출로 이루어짐
Dispatcher Servlet
- 클라이언트로부터 받은 요청을 모아 필터링 및 전처리 후 각 요청 URL에 맞는
Mapping Handler
에게 요청을 보냄
Handler Mapping
- 요청 URL과 매핑된 핸들러를 선택하는 전략
- 여러 매핑 방법이 있지만 일반적으로
RequestMappingHandlerMapping
인터페이스를 구현한 @RequestMapping
을 사용함
Handler Adapter
- 디스패처 서블릿에 핸들러를 매핑할 수 있는 인터페이스 구현체
- 위와 동일하게
@RequestMapping
어노테이션을 처리할 수 있는 RequestMappingHandlerAdapter
를 일반적으로 사용함
View Resolver
- View에서 View 오브젝트 자체가 아닌 View의 이름만을 사용하는 방식
InternalResourceViewResolver
인터페이스 구현체를 이용하여 View 이름을 통해 ModelAndView
객체로 변환
동작 순서
- 위와 같은 구현체들을 통해 실제 스프링 MVC의 동작은 다음과 같이 이루어짐
- 핸들러 조회: 핸들러 매핑을 통해 요청 URL에 매핑된 핸들러(컨트롤러)를 조회
- 핸들러 어댑터 조회: 핸들러를 실행할 수 있는 핸들러 어댑터를 조회
- 핸들러 어댑터 실행: 핸들러 어댑터를 실행
- 핸들러 실행: 핸들러 어댑터가 실제 핸들러를 실행
- ModelAndView 반환: 핸들러 어댑터는 핸들러가 반환하는 정보를 ModelAndView로 변환해서 반환
- viewResolver 호출: viewResolver를 찾고 실행
- 이 때 JSP라면
InternalResourceViewResolver
가 자동 등록 및 실행
- View 반환: viewResolver는 뷰의 논리 이름을 물리 이름으로 바꾸고, 렌더링 역할을 담당하는 뷰 객체를 반환
- JSP의 경우
InternalResourceView(JstlView)
를 반환하는데, 내부에 forward() 로직이 존재
- 뷰 렌더링: 뷰를 통해서 뷰를 렌더링 한다