MVC(Model-View-Controller)
MVC는 소프트웨어 공학에서 사용되는 소프트웨어 디자인 패턴 중 하나이다.
Model-View-Controller라는 3개의 역할로 나뉜 구조이며 사용자 인터페이스로부터 비즈니스 로직을 분리하는 것이 목적이다. 그래야 애플리케이션의 시각적 요소와 비즈니스 로직을 서로 영향 없이 쉽게 고칠 수 있게 만들 수 있기 때문이다.
Model
View 렌더링에 필요한 데이터 혹은 로직의 처리를 맡는 부분이다.
일반적으로 POJO(Plain Old Java Object)로 구성된다.
💡 POJO
JLS(Java Language Specification)에 의해 강제된 것 이외의 제한에 의존하지 않는 자바 오브젝트를 부르는 용어
💡 JLS
자바 언어의 명세서를 뜻한다. 자바 명세에 관한 더 자세한 내용은 이 포스팅을 참고 바란다.
View
사용자가 보는 화면을 위한 디스플레이 데이터 또는 프레젠테이션을 맡는 부분이다.
Model 데이터의 렌더링을 담당한다.
JSP(Java Server Pages) 이외에도 Thymeleaf, Groovy, Freemarker 등 여러 Tempate Engine이 있다.
💡 JSP
Java 언어를 기반으로 하는 Server Side 스크립트 언어이자
HTML 코드에 Java 코드를 넣어 동적인 웹 페이지를 생성하는 웹 애플리케이션 도구이다.
⚠ 간혹 Controller에도 JSP를 사용한다는 자료가 보이는데, 이는 현재 사용 중인 MVC2 패턴의 이전 모델인 MVC1에서
View와 Controller 모두 JSP로 작성된 구조를 말하는 것이므로 참고 바란다.
💡 Tempate Engine
템플릿 양식과 특정 데이터 모델에 따른 입력 자료를 합성하여 결과 문서를 출력하는 소프트웨어(또는 소프트웨어 컴포넌트)를 말한다.
그중 웹 템플릿 엔진이란 웹 문서가 출력되는 템플릿 엔진을 말한다.
웹 템플릿 엔진은 View의 코드와 DB 연결 부분을 담당하는 데이터 로직 코드를 분리해주는 기능을 한다.
Controller
View와 Model 사이의 인터페이스 역할을 하며, View와 Model에 대한 사용자의 입력이나 요청에 대해 비즈니스 로직 처리 후 그 결과를 Model에 담아 View에 전달하는 것을 맡는 부분이다.
Servlet이 이에 해당한다.
💡 Servlet
클라이언트가 어떠한 요청을 하면 그에 대한 결과를 다시 전송해주는 역할을 하는 자바 프로그램을 말한다.
스프링의 MVC에서 요청(Request)을 처리하는 세부 과정
⚠ 위 그림의 번호와 아래의 번호는 정확히 매칭 되는 것이 아니므로 참고 바란다.
1. 웹 브라우저에서 서버에 어떤 요청(Request)을 한다면 스프링에서 제공하는 DispatcherServlet이라는 클래스(일종의 Front Controller)가 요청을 가로채서 선처리 작업(지역 정보 결정, 멀티파트 파일 업로드 처리 등)을 수행한다.
참고로 web.xml 예제 코드를 찾아서 살펴보면 필요한 URL 패턴에 서블릿을 매핑해 요청을 DispatcherServlet이 가로채게 해두는 것을 볼 수 있다.
💡 Front Controller
컨트롤러 중에서도 맨 앞단에서 유저의 유청을 받는 컨트롤러를 말한다.
💡 web.xml
Servlet 클래스는 JSP 페이지와 달리 설치뿐만 아니라 등록을 하는 과정을 필요로 한다. 여기서 Servlet 클래스를 등록하는 위치의 이름을 Web Application Deployment Descriptor라고 하는데 이 역할을 하는 위치가 바로 web.xml이다.
이는 웹 애플리케이션 디렉터리마다 하나씩만 존재할 수 있다.
Tomcat 같은 WAS(Web Application Server) 구동 시 메모리에 로딩되며, /WEB-INF디렉터리에 존재하는 web.xml을 읽어서 웹 애플리케이션의 설정을 구성하기 위해 존재한다.
💡 WAS
일종의 미들웨어, 클라이언트의 요청 중 보통 웹 애플리케이션이 동작하도록 지원하는 목적을 가진다. 즉, 동적 페이지를 제공한다. Servlet(HttpServlet을 상속받은 클래스)을 사용하여 동적인 페이지를 생성할 수 있다.
2. 요청을 가로챈 DispatcherServlet은 HandlerMapping에게 어떤 컨트롤러에 요청을 위임하면 좋을지 물어본다. (HandlerMapping은 servlet-context.xml [혹은 설정 파일]에서 @Controller로 등록한 것들을 컴포넌트 스캔을 해서 찾아뒀기 때문에 어느 컨트롤러에게 요청을 위임해야 할지 알고 있다.) 요청을 처리할 컨트롤러 빈(Bean) 객체를 DispatcherServlet에 다시 전달한다.
💡 개별 컨트롤러 클래스를 핸들러(Handler)라고 한다.
💡 스프링의 컴포넌트 스캔(Component Scan)에 대해 추후 포스팅 후 링크를 걸어둘 테니 참고 바란다.
💡 빈
빈 오브젝트 생성, 관계 설정, 사용, 제거 등 오브젝트 전반에 걸친 모든 제어권을 애플리케이션이 갖는 게 아니라 프레임워크의 컨테이너에게 넘기는 개념을 말한다. 참고로 스프링 프레임워크 만의 개념이 아니며, 스프링에서 이 '오브젝트'를 '빈(Bean)'이라고 칭하는 것이다.
3. 컨트롤러 빈 객체는 여러 종류(@Controller를 사용하는 컨트롤러, Controller 인터페이스 구현체, HttpRequestHandler 인터페이스 구현체 등)가 존재한다. 이 모든 방법은 동일하게 처리할 수 있도록 DispatcherServlet은 HandlerAdapter 빈에게 요청의 실제 처리를 위임한다.
4. 컨트롤러에서는 해당 요청을 처리할 메서드를 찾아서 실행한다. 때에 따라서는 그 안의 Service를 주입(DI) 받아 비즈니스 로직을 Service에게 위임한다.
5. Service 사용이 필요한 경우, Service에서는 요청에 필요한 작업 대부분(코딩)을 담당하며 DB에 접근이 필요하면 DAO(Data Access Object)를 주입받아 DB 처리는 DAO에게 위임한다.
💡 DAO
DB의 데이터에 접근하기 위해 생성하는 객체를 말한다.
6. DAO는 ORM(Hibernate 등)이나 SQL Mapper(MyBatis, Spring JDBC 등)이나 JDBC를 이용해서 SQL 쿼리문을 실행해 DB에 저장된 정보를 객체로 받아 Service에게 다시 돌려준다. (이때, 보통 Request와 함께 날아온 DTO(Data Transfer Object) 객체(@RequestParam, @RequestBody 등)로 부터 DB 조회에 필요한 데이터를 받아와 쿼리문을 만들어 보내고, 결과로 받은 Entity 객체를 가지고 Response에 필요한 DTO객체로 변환한다.)
💡 ORM(Object Relational Mapping)
객체와 관계형 DB를 맵핑하는 기술이다. SQL 쿼리가 아닌 직관적인 메서드 코드로 데이터를 조작한다. ORM은 객체 간의 관계를 바탕으로 SQL을 자동 생성하고 메서드로 이를 조작하도록 한다.
💡 SQL(Structured Query Language) Mapper
SQL(관계형 데이터베이스 시스템[RDBMS]에서 자료를 관리 및 처리하기 위해 설계된 언어)을 직접 작성하여 SQL 쿼리 결과와 객체의 필드를 맵핑하여 데이터를 객체화하는 것을 말한다.
💡 JDBC
데이터를 생성한 프로그램이 종료되더라도 사라지지 않는 데이터의 특성인 'Persistence(영속성)'을 자바 진영에서 구현해낸 것을 말한다.
💡 DTO
계층 간 데이터 교환을 위한 자바빈(JavaBean) 객체를 뜻한다.
💡 자바빈(JavaBean)
특정한 관례를 지키려는 자바빈 API 스펙 표준이 지켜진 클래스를 말하는 것이다.
7. 모든 비즈니스 로직을 끝낸 Service가 결과물을 컨트롤러에게 넘긴다.
8. HandlerAdapter는 컨트롤러의 처리 결과를 ModelAndView라는 객체로 변환해서 DispatcherServlet에 넘긴다.
💡 ModelAndView
컨트롤러 처리 결과 후 응답할 View와 View에 전달할 값을 저장 및 전달하는 클래스를 말한다.
9. DispatcherServlet은 ViewResolver에게 받은 뷰의 대한 정보를 넘긴다.
10. ViewResolver는 해당 JSP를 찾아서(응답할 View 객체를 찾거나 생성) DispatcherServlet에게 알려준다. 응답을 생성하기 위해 JSP를 사용하는 ViewResolver는 매번 새로운 View객체를 생성해서 DispatcherServlet에 반환한다.
11. DispatcherServlet은 ViewResolver로부터 받은 응답을 위한 View객체에게 렌더링을 지시하고 View객체는 응답 로직을 처리한다.
12. JSP를 사용하는 경우 View객체는 JSP를 실행하여 웹 브라우저에 전송할 응답 결과를 생성하고 이로써 모든 과정이 끝난다. 결과적으로 DispatcherServlet이 클라이언트에게 렌더링 된 View를 응답한다.
MVC 패턴의 한계
MVC에서 View는 Controller에 연결되어 화면을 구성하는 단위 요소이므로 다수의 View들을 가질 수 있다.
그리고 Model은 Controller를 통해서 View와 연결되지만, 이렇게 Controller를 통해서 하나의 View에 연결될 수 있는 Model도 여러 개가 될 수 있다.
즉, 화면에 복잡한 화면과 데이터의 구성 필요한 구성이라면, Controller에 다수의 Model과 View가 복잡하게 연결되어 있는 상황이 생길 수 있다.
MVC가 너무 복잡하고 비대해져서 새 기능을 추가할 때마다 크고 작은 문제점을 가지고 소드 분석이나 테스트도 더 어려워지는데 이런 형태의 MVC를 Massive-View-Controller라고 부르기도 한다.
Controller는 View와 라이프 사이클과 강하게 연결되어있어서 분리할 수도 없고 코드 분석/수정과 테스트가 모두 힘들어진다. 그리고 복잡하게 엮어있는 Model과 View는 여러 Side-Effect를 불러와 프로그램 운영을 힘들게 한다.
💡 그래서 이 문제점을 보완한 여러 다양한 패턴들(MVP, MVVM, Viper, Clean Architecture, Flux, Redux, RxMVVM 등)이 있는데 기회가 되면 하나씩 포스팅할 테니 참고 바란다.
출처
MVC1, MVC2
https://junu0516.tistory.com/92
Model-View-Controller
https://gmlwjd9405.github.io/2018/12/20/spring-mvc-framework.html
Template Engine
https://gmlwjd9405.github.io/2018/12/21/template-engine.html
Servlet
https://gmlwjd9405.github.io/2018/10/28/servlet.html
스프링의 MVC에서 요청(Request)을 처리하는 세부 과정
https://jeong-pro.tistory.com/96
웹서버, 미들웨어, WAS 란?
JDBC, SQL Mapper, ORM
https://xlffm3.github.io/spring%20data/orm-sqlmapper-jdbc/
MVC 패턴의 한계
Spring Boot에서의 JSP 제한사항
'Spring' 카테고리의 다른 글
@Autowired의 동작원리 (1) | 2021.05.12 |
---|---|
스프링 컨테이너(BeanFactory, ApplicationContext) (1) | 2021.05.06 |
IoC(Inversion of Control, 제어의 역전) / DI(Dependency Injection, 의존관계 주입) (0) | 2021.04.28 |
POJO(Plain Old Java Object) (0) | 2021.04.28 |