MVC 패턴의 등장배경
Servlet이나 JSP만으로 비지니스 로직과 View Rendering 까지 모두 처리하면 너무 많은 역할을 하게 되고 유지보수가 굉장히 어려워져서(책임이 너무 많음) MVC 패턴이 등장했다.
Servlet, JSP 구조
Servlet, JSP 방식의 문제점
- Servlet만을 사용한 경우 View를 위한 코드와 비지니스 로직을 처리하는 코드가 Servlet에 모두 존재하여 유지보수가 어려워진다.
- JSP를 사용하여 View를 분리하였지만 비지니스 로직의 일부가 JSP 파일안에 존재한다. 여전히 책임이 많아 유지보수가 어렵다.
템플릿 엔진(Template Engine)
MVC 패턴에서 템플릿 엔진(Template Engine)이 등장한 이유는 View와 비즈니스 로직의 역할을 명확히 구분하여 유지보수를 쉽게 하기 위해서이다. HTML 내에 동적으로 바뀌는 부분만을 손쉽게 처리하기 위해 템플릿 엔진(Thymeleaf, Mustache, Freemarker 등) 을 사용한다.
MVC 패턴
MVC 패턴은 역할을 Model(데이터), View(화면), Controller(제어) 로 나눈 것이다.
- Controller
- HTTP Request를 전달받아 파라미터를 검증한다.
- 비지니스 로직을 실행한다.
- 비지니스 로직을 Controller에 포함하게되면 Controller가 너무 많은 역할을 담당하게 되어 일반적으로 Service Layer를 별도로 만들어서 처리한다.
- Database와 상호작용 하는 Layer를 따로 구분하여 Repository Layer를 추가로 구성한다.
- 위와 관련된 자세한 내용인 Layered Architecture는 다음 강의에서 알아보자.
- Controller도 비지니스 로직을 포함할 수 있지만 일반적으로 Service Layer를 호출하는 역할을 담당한다.
- View에 전달할 결과를 조회하여 Model 객체에 임시로 저장한다.
- Model
- View에 출력할 Data를 저장하는 객체이다.
- View는 비지니스 로직이나 Data 접근을 몰라도 되고 View Rendering에만 집중하면 된다.(책임 분리)
- View
- 예시 코드에서 JSP에 해당하는 영역이다.
- Model 객체에 담겨져 있는 Data를 사용하여 화면을 Rendering 한다.
MVC 패턴의 문제점
MVC 패턴 적용 후 View 역할은 필요한 데이터를 Model에서 참조하여 화면을 그리는 역할만 수행하면 되지만 Controller에 해당하는 부분은 여전히 문제가 있다.
Controller 관련 부분 문제점
- 중복 코드의 증가
dispatcher.forward(request, response)
같은 View로 이동하는 코드가 반복적으로 등장한다.- View 파일의 경로가 바뀌면 모든 Controller의 코드를 수정해야 한다.
- 공통 기능 처리의 어려움
- 모든 Controller에서 공통적으로 처리해야 하는 기능이 있는데 해당 기능을 각 Controller마다 직접 호출해야한다.
- 인증, 로깅 등 반복되는 기능을 Controller마다 구현해야 한다.
- 응답 방식의 일관성 부족
- 어떤 Controller는 JSON을 반환하고, 어떤 Controller는 JSP 페이지를 반환할 수도 있는데 응답 방식이 다를 경우 Controller 간의 일관성이 깨진다.
- 테스트 코드 작성 어려움
- HttpServletRequest와 HttpServletResponse는 Test 코드를 작성하기도 매우 힘들다.
문제점 | 설명 | 해결책 |
---|---|---|
중복 코드 증가 | dispatcher.forward(request, response) 같은 코드 반복 |
프론트 컨트롤러 패턴 사용 |
Controller의 책임 과중 | Controller가 너무 많은 역할을 담당 | 공통 기능을 프론트 컨트롤러에서 처리 |
공통 기능 처리 어려움 | 인증, 로깅 등 반복되는 기능을 Controller마다 구현 | 프론트 컨트롤러에서 공통 처리 |
응답 방식의 일관성 부족 | Controller마다 응답 형식이 다를 수 있음 | 어댑터 패턴 적용 |
테스트 코드 작성 어려움 | HttpServletRequest , HttpServletResponse 의존성 문제 |
Spring MVC에서 MockMvc 사용 |
프론트 컨트롤러 패턴
모든 요청을 하나의 컨트롤러(Front Controller)에서 받아 공통 처리를 수행한 후, 각각의 개별 컨트롤러(Handler)로 요청을 전달하는 패턴이다.
프론트 컨트롤러 패턴의 동작 과정
- 클라이언트가 요청을 보냄 (
GET /users
) - 프론트 컨트롤러(Front Controller)**가 요청을 가로챔 (
/users
) - 공통 처리 로직 수행 (인증, 로깅, 보안 등)
- 요청을 처리할 적절한 컨트롤러를 찾아 실행
- 컨트롤러가 비즈니스 로직을 수행하고 결과를 반환
- 프론트 컨트롤러가 응답을 생성하여 클라이언트에게 반환
어댑터 패턴
호환되지 않는 인터페이스를 변환하여 연결할 수 있도록 하는 디자인 패턴이다.
어댑터 패턴이 필요한 이유
Spring MVC에서 @RequestMapping, Controller 인터페이스, HttpServlet 등 다양한 방식의 컨트롤러가 존재한다.
하지만, 프론트 컨트롤러(DispatcherServlet)는 다양한 컨트롤러를 직접 처리할 수 없다.
즉, 인터페이스가 다르면 프론트 컨트롤러가 컨트롤러를 실행할 방법이 없으므로 프론트 컨트롤러와 컨트롤러 사이에 “어댑터”가 필요한 것이다.
어댑터 패턴의 동작 과정
- 요청을 처리할 적절한
Controller
를 찾음 - 해당
Controller
를 실행할 수 있는HandlerAdapter
를 찾음 HandlerAdapter
가 컨트롤러의 실행 방식에 맞게 변환 후 실행- 컨트롤러가 결과를 반환하면 어댑터가 이를 변환하여 프론트 컨트롤러에 전달
프론트 컨트롤러 패턴과 어댑터 패턴의 구조
┌──────────────┐
│ Client 요청 │ --->
└──────────────┘
│
▼
┌──────────────────┐
│ Front Controller │
├──────────────────┤
│ ① 공통 로직 처리 │
│ ② 컨트롤러 매핑 │
└────────▲─────────┘
│ 컨트롤러 호출 요청
┌────────▼──────────┐
│ HandlerAdapter │
└────────▲──────────┘
│ 컨트롤러 변환 후 실행
┌────────▼──────────┐
│ Controller │
└───────────────────┘
│
▼
┌─────────────────────┬───────────────────────┐
│ Controller 1 │
├─────────────────────────────────────────────┤
│ Controller 2 │
├─────────────────────────────────────────────┤
│ Controller N │
└─────────────────────────────────────────────┘
요약
패턴 | 설명 | 문제점 |
---|---|---|
Servlet | 하나의 서블릿에서 요청을 처리하고 View를 반환 | 비즈니스 로직과 View가 혼재됨 |
JSP | View를 분리하여 렌더링 담당 | 비즈니스 로직이 여전히 JSP에 포함될 수 있음 |
MVC 패턴 | Model-View-Controller로 역할을 분리 | 중복 코드 증가, Controller의 책임 과중 |
프론트 컨트롤러 패턴 | 모든 요청을 하나의 컨트롤러에서 처리 | 응답 형태가 다를 경우 유지보수 어려움 |
어댑터 패턴 | 컨트롤러(Handler)와 프론트 컨트롤러를 분리하여 유연성 확보 | 유지보수성과 확장성 대폭 증가 |
'Backend > Web' 카테고리의 다른 글
Web Application 및 Servlet (1) | 2025.03.19 |
---|