웹 계층에 서블릿(Servlet) API를 기반으로 클라이언트의 요청을 처리하는 모듈로 클라이언트의 요청을 편리하게 해주는 기능을 제공한다.
서블릿(Servlet)
- 자바로 웹을 할 수 있는 기술, 자체도 Java 파일
- 클라이언트의 요청을 처리하도록 특정 규약에 맞춰 Java 코드로 작성하는 클래스 파일
- 아파치 톰캣은 이러한 서블릿들이 웹 애플리케이션으로 실행할 수 있도록 해주는 서블릿 컨테니어 중 하나이다.
- Spring MVC 내부에서는 서블릿을 기반으로 웹 애플리케이션을 동작하며, 스프링 부트는 기본적으로 아파치 톰캣이 내장되어 있다.
MVC 패턴이란?
- 코드에 대한 유지보수를 어떻게 하면 편하게 할 수 있을까를 고민하다가 탄생하게 된 패턴 중 하나
- 애플리케이션을 개발할 때 사용하는 디자인 패턴
- 애플리케이션의 개발 영역을 MVC(Model, View, Controller)로 구분하여 각 역할에 맞게 코드를 작성하는 개발 방식
- MVC 패턴을 도입하면서 UI 영역과 도메인(비즈니스 로직) 영역으로 구분되어 서로에게 영향을 주지 않으면서 개발과 유지보수를 할 수 있게 되었다.
모델 : 애플리케이션의 정보(데이터)
뷰 : 텍스트, 체크박스 항목 등과 같은 사용자 인터페이스 요소
컨트롤러 : 데이터와 비즈니스 로직 사이의 상호 동작 관리
- 사용자가 보는 페이지, 데이터 처리, 그리고 이 2가지를 중간에서 제어하는 컨트롤러 이렇게 3가지로 구성되는 하나의 애플리케이션을 만들면 각각 맡은 역할에만 집중을 할 수 있게 된다.
- 사용 목적 : 각 컴포넌트가 서로 분리되어 각자의 역할에 집중할 수 있기 때문에 시스템 결합도를 낮출 수 있다. 또한 유지보수가 쉬우며, 중복 코드를 제거할 수 있고, 애플리케이션의 확장성 및 유연성이 증가한다.
Model
작업의 처리 결과 데이터
- Spring MVC 기반의 앱 애플리케이션이 클라이언트의 요청을 전달 받으면 요청 사항을 처리하기 위한 작업을 한다.
- 처리한 작업의 결과 데이터를 클라이언트에게 응답으로 돌려주어야 하는데 이때 클라이언트에게 응답으로 돌려주는 작업의 처리 결과 데이터를 Model이라고 한다.
서비스 계층(Service layer) : 클라이언트의 요청 사항을 구체적으로 처리하는 영역
비즈니스 로직(Business Logic) : 요청 사항을 처리하기 위해 Java 코드로 구현한 것
View
Model을 이용하여 웹 브라우저와 같은 애플리케이션의 화면에 보이는 리소스(Resourse)를 제공하는 역할을 한다.
- HTML 페이지 출력
- PDF, Excel 등의 문서 형태로 출력
- XML, JSON 등 특정 형식의 포맷으로 변환
Controller
클라이언트 측의 요청을 직접적으로 전달받는 Endpoint로써 Model과 View의 중간에서 상호작용을 해주는 역할을 한다.
클라이언트 측의 요청을 전달받아 비즈니스 로직을 거친 후, Model 데이터가 만들어지면, 이 Model 데이터를 View로 전달하는 역할을 한다.
컨트롤러란? (FrontController와 Dispatcher)
- 요청을 할 때마다 Java파일이 호출된다. => 요청의 종류가 3개이면 3개의 Java 파일이 필요하다.
- 하나의 Java파일에서 모든 요청을 받는 FrontController 사용
- 너무 많은 요청이 한 곳으로 모이는 것을 방지하기 위해 도메인 별로 분기
- if - else문이 많이 필요하다. => 번거롭다. => 도메인 별로 분기
- e.g)
- User 테이블(로그인, 회원가입 -> UserController.java에서 처리)
- Board 테이블(글쓰기, 글삭제, 글 수정 -> BoardController.java)
- Product 테이블(상품 등록, 상품 목록 보기 -> ProductController.java)
- 요청을 어디로 보내야할지 분기해주는 것이 필요
- 분기의 일은 Dispatcher가 해준다. (ServletDispatcher, RequestDispatcher)
MVC 패턴들
MVC1
우리가 흔히 사용하고 있는 MVC 패턴은 사실 MVC1, MVC2 아키텍쳐에서 발전된 패턴이다.
MVC1 패턴이란, 브라우저(사용자)로부터 요청이 들어오면 DB로부터 필요한 데이터를 받은 Model 객체(Java Bean)을 JSP 페이지(View)에 담아 응답으로 보내는 패턴이다.
MVC1에서는 JSP가 View와 Controller 역할을 모두 담당하기 때문에 JSP 페이지 내에 너무 많은 코드가 들어가게 된다.
=> 코드의 가독성이 떨어지고 복잡해진다.
=> 이러한 점을 보완하여 Controller 역할을 하는 Servlet이 추가된 MVC2 패턴이 등장하였다.
MVC2
MVC2 패턴은 요청을 하나의 컨트롤러(Servlet)가 먼저 받는다.
서블릿은 요청에 대한 비즈니스 로직을 처리한 후, 이를 JSP 파일(view)에 반영하는 역할을 수행한다.
Model은 데이터와 비즈니스 로직을 담당한다.
MVC2 패턴은 MVC1 패턴보다 구조가 복잡해질 수 있으나, 이러한 문제점들을 해결하기 위해 각종 프레임워크들이 지금까지 잘 발전되어 왔고, 그 중에서 대표적인 것이 바로 스프링 프레임워크이다.
Spring MVC
MVC2 모델이 기반인 웹 모듈
Front Controller가 우선적으로 클라이언트로부터 모든 요청을 받게 되며, 실제 요청의 처리는 개별 컨트롤러 클래스로 위임한다.
- 개별 컨트롤러 클래스는 핸들러(Handle r)라고도 하며, DI를 통해 생성해둔 Bean을 통해 비즈니스 로직 처리 결과를 Model에 담아 다시 프론트 컨트롤러로 보낸다.
- 프론트 컨트롤러는 받은 Model을 알맞은 View 템플릿으로 전달하여 반영시키고, 최종적으로 클라이언트로 보낼 화면을 응답 결과로 전송하는 것이다.
Spring MVC 구조
HandlerMapping, HandlerAdapter, ViewResolver는 스프링이 제공하는 인터페이스로 각각의 구현체들을 다양히 제공하고 있다.
Dispatcher Servlet
HttpServlet을 상속받아 사용하고 서블릿으로 동작한다.
- HTTP Request가 왔을 때 DispatcherServlet이라 불리는 서블릿이 HTTP Request를 처리할 Controller을 지정한다. (Front Controller의 역할을 수행)
- 가장 앞단에서 클라이언트의 요청을 처리하는 Controller로써 요청부터 응답까지 전반적인 처리 과정을 통제한다.
DispatcherServlet을 Front Controller로 설정하는 방법
1. web.xml에 명시한다.
2. org.springframework.web.WebApplicationInitializer 인터페이스를 구현한다.
Handler Mapping
- 요청을 직접 처리할 컨트롤러를 탐색한다.
- 구체적인 Mapping은 xml파일이나 java config 관련 어노테이션 등을 통해 처리할 수 있다.
Handler Adapter
- 매핑된 컨트롤러의 실행을 요청한다.
- 핸들러의 종류에 관계없이 핸들러 어댑터로 해당 핸들러를 실행할 수 있다.
Handler(Controller)
DispatcherServlet이 전달해준 HTTP 요청을 처리하고 결과를 Model에 저장한다.
- 직접 요청을 처리하며, 처리 결과를 반환한다.
- 결과가 반환되면 HandlerAdapter가 ModelAndView 객체로 변환되며, 여기에는 View Name과 같이 응답을 통해 보여줄 View에 대한 정보와 관련된 데이터가 포함되어 있다.
ModelAndView
컨트롤러에서 처리한 데이터(모델)과 해당 데이터를 표시할 View의 정보를 가진 객체
View Resolver
View Name을 확인한 후, 실제 컨트롤러로부터 받은 로직 처리 결과를 반영할 View 파일(jsp)을 탐색한다.
View
로직 처리 결과를 반영한 최종 화면을 생성한다.
Spring MVC 동작 구조
요청흐름
- 서블릿이 호출되면 HttpServlet이 제공하는 service()가 호출된다.
- Spring MVC는 FrameworkServlet.service()를 시작으로 여러 메서드가 호출되면서 DispacherServlet.doDispatch()가 최종적으로 호출된다.
동작 순서
클라이언트가 서버에 요청을 하면, Front Controller인 DispatcherServlet 클래스가 요청을 받는다. 이 때 DispatcherServlet은 다음과 같은 과정을 거친다.
- 핸들러 조회: DispatcherServlet은 servlet-context.xml 파일의 @Controller 인자를 통해 등록된 요청 위임 컨트롤러를 찾아 URL에 매핑된 핸들러(컨트롤러)를 조회한다.
- 핸들러 어댑터 조회: 조회된 핸들러(컨트롤러)를 실행할 수 있는 핸들러 어댑터를 조회한다.
- 핸들러 어댑터 실행: 핸들러 어댑터를 실행하여 실제 핸들러를 호출한다.
- 핸들러 실행: 핸들러 어댑터가 실제 핸들러(컨트롤러)를 실행한다.
핸들러(컨트롤러)는 요청을 처리할 Service(서비스)를 받아 비즈니스 로직을 수행한다. 서비스는 요청에 필요한 작업을 수행하고, 데이터베이스(DB)에 접근이 필요하면 DAO에 요청하여 처리를 위임한다. DAO는 DB 정보를 DTO를 통해 서비스에게 전달한다. 서비스는 전달받은 데이터를 컨트롤러에게 전달한다.
- ModelAndView 반환: 핸들러 어댑터는 핸들러가 반환한 정보를 ModelAndView 객체로 변환하여 DispatcherServlet에게 반환한다.
- viewResolver 호출: DispatcherServlet은 viewResolver를 찾아 실행한다.
- View 반환: viewResolver는 View의 논리 이름을 물리 이름으로 변환하고, 렌더링 역할을 담당하는 View 객체를 반환한다.
- 뷰 렌더링: DispatcherServlet은 반환된 View 객체를 통해 뷰 렌더링을 지시하고, 뷰는 클라이언트에게 응답할 화면을 렌더링한다.
최종적으로, DispatcherServlet은 렌더링된 뷰를 클라이언트에게 응답하여 요청 처리를 마친다.
MVC를 지키면서 코딩하는 방법
1. Model은 Controller와 View에 의존하지 않아야 한다.
언제나 깔끔하고 정제된 데이터를 꺼내 쓸 수 있게 Model 내부에 Controller와 View에 관련한 코드가 있으면 안된다. 데이터에 관련된 코드만 모아둔다.
2. View는 Model에만 의존해야 하고, Controller에는 의존하면 안 된다.
View 내부에 Model의 코드만 있을 수 있고, Controller의 코드가 있으면 안 된다.
3. View가 Model로부터 데이터를 받을 때는, 사용자마다 다르게 보여주어야 하는 데이터에 대해서만 받아야 한다.
- 모든 사용자에게 똑같이 보여져야 하는 부분(e.g "주문하기", "배달 정보", 어플 배경은 하얀색)은 모델로부터 받으면 안되고 뷰가 자체적으로 가지고 있어야 하는 정보다.
4. Controller은 Model과 View에 의존해도 된다.
Controller 내부에는 Model과 View의 코드가 있을 수 있다.
5. View가 Model로부터 데이터를 받을 때, 반드시 Controller에서 받아야 한다.
잡담
이론?만 살펴봤는데 실제 코드는 어떻게 작성하는지 공부해야겠다.
그리고 찾다보니 김영한님의 강의가 유명한 것 같은데 카테켐 강의 듣는게 끝나면 들어봐야겠다.
참조
https://www.youtube.com/watch?v=yemt7vcUl9s&t=81s&ab_channel=%EB%A9%94%ED%83%80%EC%BD%94%EB%94%A9
https://www.youtube.com/watch?v=ogaXW6KPc8I&ab_channel=%EC%9A%B0%EC%95%84%ED%95%9C%ED%85%8C%ED%81%AC
https://ittrue.tistory.com/234
'Spring' 카테고리의 다른 글
Validation in Spring Boot (0) | 2024.07.02 |
---|---|
Validating Form Input (0) | 2024.07.02 |
Serving Web Content with Spring MVC (0) | 2024.06.28 |
Mapping Requests (0) | 2024.06.26 |
REST APIs Explained - 4 Components (0) | 2024.06.26 |