초기 웹 개발은 템플릿 안에 서버 코드를 섞어 넣는 방식이 주류였다(ASP, PHP 그리고 JSP의 스크립틀릿) 배우기 쉽고 결과가 바로 보이는 장점이 있었지만 프로젝트 규모가 클 수록 스파게티 코드로 유지보수가 어려웠다.
MVC(Model - View - Controller) : 화면, 데이터, 제어를 역할별로 분리해 개발 유지보수를 쉽게 만드는 아키텍쳐 패턴이다.
Model
: 비즈니스 로직과 데이터(도메인, 서비스, DAO)View
: 사용자에게 보여줄 화면(템플릿, JSP/Thymeleaf/HTML)Controller
: 요청을 받아 검증/권한 확인 후 Model
을 호출하고 결과를 View
에 전달어떻게 생겼나?
JSP
가 요청을 직접 받고(request.getParameter), 비즈니스 호출(JavaBean/DAO) 화면 출력까지 한 페이지에서 해결한다.RequestDispatcher.forward()
로 다른 JSP
로 넘겨 출력만 분리하기도 했지만 컨트롤러 역할은 여전히 JSP가 맡았다.등장 배경
드러난 문제
이렇게 Model1은 빠르게 만들 수 있는 장점이 있지만 규모가 커질수록 관리가 어렵다는 한계를 드러냈다.
개념
JSP
가 직접 받음 👉 JSP 안에서 파라미터 처리/검증/비즈니스 호출 (보통 JavaBean/DAO) 👉 결과를 같은 JSP
또는 다른 JSP
로 출력Controller
역할을 JSP
가 겸함 Model(JavaBean/DAO)
은 별도 클래스요청 흐름
Browser → list.jsp(컨트롤러 역할) → JavaBean/DAO → request.setAttribute(...)
→ (같은 JSP에서 렌더 or 다른 JSP로 forward)
짧은 예시
<%@ page import="com.example.MemberDao" %>
<jsp:useBean id="memberDao" class="com.example.MemberDao" />
<%
var list = memberDao.findAll(); // 컨트롤러 역할
request.setAttribute("members", list); // 모델 전달
%>
<ul>
<c:forEach var="m" items="${members}">
<li>${m.name}</li>
</c:forEach>
</ul>
어떻게 생겼나?
Servlet
이 Front Controller
가 되어 라우팅, 검증, 권한, 예외, 로깅 등 흐름 제어를 한곳에서 처리Service/DAO
로 비즈니스 데이터 계층을 분리JSP
는 View
전용(JSTL/EL)으로 표현만 담당request.setAttribute()
로 모델을 담아 forward
로 JSP
에 전달왜 이렇게 바뀌었나?
개념
Servlet
이 Controller, JSP
는 View전용(JSTL/EL로만 표현)Front Controller(하나의 서블릿)
로 URL 매핑/검증/권한/예외/로그를 중앙집중💡 Model2
는 규모와 팀을 견딜 수 있는 구조를 제공했고 오늘날 Spring MVC/Boot로 사실상 표준이 되었다.
요청 흐름
Browser → Servlet(Controller) → Service → DAO
→ request.setAttribute(... 모델 ...)
→ forward("/WEB-INF/views/xxx.jsp") → JSP(View)
짧은 예시
// controller
@WebServlet("/members/detail")
public class MemberController extends HttpServlet {
private final MemberService svc = new MemberService();
@Override protected void doGet(HttpServletRequest req, HttpServletResponse resp)
throws IOException, ServletException {
String id = req.getParameter("id");
if (id == null || id.isBlank()) { resp.sendError(400); return; }
var member = svc.findById(id);
if (member == null) { resp.sendError(404); return; }
req.setAttribute("member", member);
req.getRequestDispatcher("/WEB-INF/views/member/detail.jsp").forward(req, resp);
}
}
// view
<!-- /WEB-INF/views/member/detail.jsp -->
<h1>회원 상세</h1>
<p>이름: ${member.name}</p>
<% ... %>
, JDBC 직결