동적인 웹 페이지를 생성하기 위해 사용되는 도구이며, 템플릿을 기반으로 정적인 부분과 동적인 데이터를 결합하여 HTML, XML 등의 문서를 생성하는 역할을 수행. SSR(Server Side Rendering)에 사용.
- 템플릿 엔진이 나온 이유
- 자바 코드로 HTML을 만드는 것이 아닌 HTML 문서에 동적으로 변경해야 하는 부분만 자바 코드를 넣을 수 있다면 더 편리하기 때문에
- 대표적인 템플릿 엔진
- Thymeleaf
- Spring과 통합이 잘 되어 있으며 다양한 기능 포함
- JSP(Java Server Pages)
- 예전엔 많이 사용했으나 현재는 잘 쓰지 않는 추세
Servlet이나 JSP만으로 비즈니스 로직과 View Rendering까지 모두 처리하면 너무 많은 역할(= 책임이 너무 많음)을 하게 되고 유지보수가 굉장히 어려워져서 고안된 패턴. Web Application은 일반적으로 MVC(Model View Controller) 패턴을 사용.
- Servlet과 JSP 구조
- Servlet 문제점
- 화면을 그리는 View 영역과 비즈니스 로직이 모두 섞여있음
- Servlet 동작 순서
- 사용자가 Client(브라우저)를 통해 서버에 HTTP Request(API 요청)을 함
2. 요청을 받은 Servlet 컨테이너는HttpServletRequest
,HttpServletResponse
객체를 생성
3. 설정된 정보(URL, HTTP Method)를 통해 어떠한 Servlet에 대한 요청인지 조회
4. 해당 Servlet에서 service 메서드를 호출한 뒤 브라우저의 요청 Method에 따라doGet()
혹은doPost()
등의 메서드를 호출
5. 서버에서HttpServletResponse
객체에 응답을 담아 Client(브라우저)에 반환
6. 응답이 완료되면 생성한HttpServletRequest
,HttpServletResponse
객체를 소멸- JSP 문제점
- Servlet 코드에서 HTML을 만드는 부분인 View가 분리되었지만, 비즈니스 로직의 일부가 JSP 파일 안에 존재
- 여전히 많은 책임을 가지고 있으며 유지 보수가 어려움
하나의 Servlet이나 JSP로 처리하던 것들을 Model, View, Controller 영역으로 나눈 것.
- 핵심 내용
- 서로 연관이 없는 코드끼리 함께 존재할 필요가 없기 때문에 완전히 분리
- 화면 구성에 수정이 발생하면 View 변경
- 요구사항에 수정이 발생하면 비즈니스 로직 변경
- MVC 패턴 구조
- Controller
- HTTP Request를 전달받아 파라미터를 검증
- 비즈니스 로직 실행
- 비즈니스 로직을 컨트롤러에 포함하게되면 많은 역할을 담당하게 되므로 Service Layer를 별도로 만들어서 처리
- Database와 상호작용 하는 Repository Layer를 추가로 구성
- 컨트롤러도 비지니스 로직을 포함할 수 있지만 일반적으로 Service Layer를 호출
- View에 전달할 결과를 조회하여 Model 객체에 임시로 저장
- Model
- View에 출력할 Data를 저장하는 객체
- View는 비즈니스 로직이나 Data 접근을 몰라도 되며, View Rendering에만 집중(책임 분리)
- View
- Model 객체에 담겨져 있는 Data를 사용하여 화면을 Rendering
MVC 패턴 적용 후 View는 화면을 그리는 역할만 수행하면 되었지만 Controller는 여전히 문제가 존재.
- View로 이동하는 forward(Servlet에서 다른 Servlet이나 JSP 호출)가 중복 호출됨
- View의 path 입력이 중복됨
- JSP 파일의 경로 혹은 이름이 바뀌면 모두 변경되어야 함
- JSP 이외의 확장자를 사용하면 전체가 변경되어야 함
- JSP에서 모두 해결하기 때문에
HttpServletResponse
객체를 사용하는 경우가 적음
HttpServletRequest
와HttpServletResponse
는 테스트 코드를 작성하기도 힘듦- 공통 기능이 추가될수록 컨트롤러에서 처리해야 하는 부분이 증가
Servlet(Controller)이 호출되기 전에 공통 기능을 하나의 Servlet에서 처리해주는 패턴. 프론트 컨트롤러(Servlet) 하나에 모든 클라이언트측 요청이 들어옴.
- 모든 요청을 하나의 프론트 컨트롤러가 받으며 공통 기능을 처리
- 요청을 처리할 수 있는 컨트롤러를 찾아서 호출 (Controller Mapping)
- 프론트 컨트롤러를 제외한 나머지 컨트롤러는 Servlet을 사용하지 않아도 됨
- 일반 컨트롤러들은
HttpServlet
을 상속받거나,@WebServlet
을 사용하지 않아도 됨
- 공통 처리 로직에 모든 컨트롤러가 연결되기 위해서는 모든 컨트롤러의 리턴 형태가 동일해야 함
- 하지만, 컨트롤러마다 리턴 형태는 다를 수 있으며 이것을 동일하게 맞추려고 한다면, 해당 애플리케이션은 확장성, 유지보수성을 잃게 됨
- 공통 로직에서 리턴 형태별로 처리할 수 있지만 그렇게 되면 책임이 너무 커짐
- 또한, 컨트롤러에서 리턴 형태가 변경되면 공통 로직도 변경되어야 함
다양한 컨트롤러(Handler)를 유연하게 만들기위해 도입된 패턴. 컨트롤러들은 동일한 인터페이스를 구현하도록 하고 해당 인터페이스와 공통 로직 사이에 어댑터를 두어 유연하게 만드는 방식.
- 어댑터 패턴 구조
- 컨트롤러(Handler)는 비지니스 로직을 처리하고 알맞은 결과를 반환
- 어댑터는 공통 로직과 컨트롤러(Handler)를 연결
- 프론트 컨트롤러는 공통으로 처리되는 로직을 수행
- 어댑터 패턴 장점
- 프론트 컨트롤러, 어댑터, 핸들러 모두 각자의 역할만 수행(책임 분리)
- 새로운 컨트롤러(Handler)가 추가되어도 공통 로직의 변경이 발생하지 않음
MVC 패턴에 프론트 컨트롤러 패턴, 어댑터 패턴이 적용된 구조.
- 실행순서
1. Client로 부터 HTTP 요청(Request)을 받음
2. Handler 조회
- Handler Mapping을 통해 요청 URL에 Mapping된 Handler(Controller)를 조회
3. Handler를 처리할 Handler Adapter 조회
4. Handler Adapter 실행(handle)
- 알맞은 어댑터가 존재한다면 Handler Adapter에게 요청을 위임
5. Handler 실행(호출)
- Handler Adapter가 실제 Handler(Controller)를 호출하여 실행 및 결과 반환
6. Model And View 반환(return)
- Handler Adapter는 Handler가 반환 하는 정보를 ModelAndView 객체로 변환하여 반환
7. viewResolver 호출(알맞은 View 요청)
- View Resolver를 찾고 실행
8. View 반환
- View Resolver는 View의 논리 이름을 물리 이름으로 전환하는 역할을 수행하고 Rendering 역할을 담당하는 View 객체를 반환
9. View Rendering
- View를 통해서 View를 Rendering💡 DispatcherServlet
- 클라이언트 HTTP Request를 알맞게 파싱하고 클라이언트에게 알맞은 응답을 반환
- 핸들러 목록 정보와 핸들러 어댑터 목록 정보를 알고 있음
💡 HandlerAdapter
- 자신이 처리할 수 있는 Handler인지 확인할 수 있는 기능(Method)이 필요
- 프론트 컨트롤러에서 요청을 위임받았을 때 핸들러에게 요청을 지시하는 기능이 필요
- return 시 Handler로부터 전달받은 결과를 알맞은 응답으로 변환
💡 Handler
- 요청에 대한 로직을 수행하는 기능이 필요