서블릿
회원 추가 서블릿
@WebServlet(name = "memberFormServlet", urlPatterns = "/servlet/members/newform")
public class MemberFormServlet extends HttpServlet {
@Override
protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
response.setContentType("text/html");
response.setCharacterEncoding("utf-8");
PrintWriter w = response.getWriter();
w.write("<!DOCTYPE html>\n" +
"<html>\n" +
...
"</html>\n");
}
}
회원 저장 서블릿
@WebServlet(name = "memberSaveServlet", urlPatterns = "/servlet/members/save")
public class MemberSaveServlet extends HttpServlet {
private MemberRepository memberRepository = MemberRepository.getInstance();
@Override
protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
String username = request.getParameter("username");
int age = Integer.parseInt(request.getParameter("age"));
Member member = new Member(username, age);
memberRepository.save(member);
response.setContentType("text/html");
response.setCharacterEncoding("utf-8");
PrintWriter w = response.getWriter();
w.write("<!DOCTYPE html>\n" +
"<html>\n" +
...
"</html>\n");
}
}
회원 조회 서블릿
@WebServlet(name = "memberListServlet", urlPatterns = "/servlet/members")
public class MemberListServlet extends HttpServlet {
private MemberRepository memberRepository = MemberRepository.getInstance();
@Override
protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
response.setContentType("text/html");
response.setCharacterEncoding("utf-8");
List<Member> members = memberRepository.findAll();
PrintWriter w = response.getWriter();
w.write("<!DOCTYPE html>\n");
w.write("<html>\n");
...
for (Member member : members) {
w.write(" <tr>\n");
w.write(" <td>" + member.getId() + "</td>\n");
w.write(" <td>" + member.getUsername() + "</td>\n");
w.write(" <td>" + member.getAge() + "</td>\n");
w.write(" </tr>");
}
...
w.write("</html>\n");
}
}
간단한 회원 추가, 회원 저장, 회원 조회 기능이다.
화면에 보이는 html 을 전부 자바 코드로 작성하여 해당 클래스에서 관리된다.
서블릿의 단점
- 자바 코드로 HTML 을 작성해야 해서 복잡하다.
템플릿 엔진의 등장
이처럼 자바 코드로 HTML 을 작성하는 것이 아닌
HTML 문서에 동적으로 변경하는 부분에만 자바 코드를 넣을 수 있도록 하는 `템플릿 엔진`이 등장했다.
ex. `JSP`, `Thymeleaf`, `Freemarker`, `Velocity` 등
JSP
JSP 라이브러리 추가
// build.gradle
implementation 'org.apache.tomcat.embed:tomcat-embed-jasper'
implementation 'jakarta.servlet:jakarta.servlet-api'
implementation 'jakarta.servlet.jsp.jstl:jakarta.servlet.jsp.jstl-api'
implementation 'org.glassfish.web:jakarta.servlet.jsp.jstl'
회원 추가 JSP
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
...
</html>
회원 저장 JSP
<%@ page import="hello.servlet.domain.member.MemberRepository" %>
<%@ page import="hello.servlet.domain.member.Member" %>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%
MemberRepository memberRepository = MemberRepository.getInstance();
String username = request.getParameter("username");
int age = Integer.parseInt(request.getParameter("age"));
Member member = new Member(username, age);
memberRepository.save(member);
%>
<html>
...
<ul>
<li>id=<%=member.getId()%></li>
<li>username=<%=member.getUsername()%></li>
<li>age=<%=member.getAge()%></li>
</ul>
...
</html>
회원 조회 JSP
<%@ page import="java.util.List" %>
<%@ page import="hello.servlet.domain.member.MemberRepository" %>
<%@ page import="hello.servlet.domain.member.Member" %>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%
MemberRepository memberRepository = MemberRepository.getInstance();
List<Member> members = memberRepository.findAll();
%>
<html>
...
<%
for (Member member : members) {
out.write(" <tr>\n");
out.write(" <td>" + member.getId() + "</td>\n");
out.write(" <td>" + member.getUsername() + "</td>\n");
out.write(" <td>" + member.getAge() + "</td>\n");
out.write(" </tr>");
}
%>
...
</html>
`<%@ page contentType="text/html;charset=UTF-8" language="java" %>` : JSP 문서라는 뜻
`<%@ page import="hello.servlet.domain.member.MemberRepository" %>` : 자바의 import 문
`<% ~~ %>` : 자바 코드 입력
`<%= ~~ %>` : 자바 코드 출력
`${ ~~ }` : request 에 담긴 데이터를 조회
`<%@ tagLib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>` 추가 후
<c:forEach var="item" items="${members}">
...${item.id} 사용...
</c:forEach>
이때, request 에 담긴 데이터에서 "members" 이름을 가진 데이터를 for 문으로 순회하며 조회할 수 있다.
JSP 의 특징
- JSP는 최초 요청 시 서블릿으로 변환되며 이후에는 서블릿처럼 동작한다.
- request, response 는 JSP 기본 내장 객체에 포함되어 있어 별도 선언 없이 사용할 수 있다.
- HTML 을 중심으로 하고, 동적으로 변경하는 부분에만 자바 코드를 부분 입력/출력한다.
JSP 의 단점
- 비즈니스 로직과, 뷰를 처리하는 로직이 합쳐져 있다.
- JSP 의 역할이 너무 많다.
MVC 패턴의 등장
비즈니스 로직과 뷰를 처리하는 로직을 한 곳에서 처리하는 것이 아닌,
비즈니스 로직은 서블릿처럼 다른 곳에서 처리하고 JSP 는 HTML 로 뷰를 처리하는 역할만 담당하도록 하는 MVC 패턴이 등장했다.
MVC 패턴
클라이언트 | ① 호출 → → → → |
컨트롤러 (컨트롤러 로직) |
② → → → → |
서비스, 리포지토리 (비즈니스 로직) (데이터 접근) |
|
↓ ↓ ④ ↓ ↓ |
③ 데이터 전달 ↘ | ||||
Model | |||||
⑤ 데이터 참조 ↗ | |||||
← ← ← ← ⑥ 응답 |
뷰 (뷰 로직) |
회원 추가 컨트롤러
@WebServlet(name = "mvcMemberFormServlet", urlPatterns = "/servlet-mvc/members/newform")
public class MvcMemberFormServlet extends HttpServlet {
@Override
protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
String viewPath = "/WEB-INF/views/new-form.jsp";
RequestDispatcher dispatcher = request.getRequestDispatcher(viewPath);
dispatcher.forward(request, response);
}
}
회원 저장 컨트롤러
@WebServlet(name = "mvcMemberSaveServlet", urlPatterns = "/servlet-mvc/members/save")
public class MvcMemberSaveServlet extends HttpServlet {
private MemberRepository memberRepository = MemberRepository.getInstance();
@Override
protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
String username = request.getParameter("username");
int age = Integer.parseInt(request.getParameter("age"));
Member member = new Member(username, age);
memberRepository.save(member);
request.setAttribute("member", member);
String viewPath = "/WEB-INF/views/save-result.jsp";
RequestDispatcher dispatcher = request.getRequestDispatcher(viewPath);
dispatcher.forward(request, response);
}
}
회원 조회 컨트롤러
@WebServlet(name = "mvcMemberListServlet", urlPatterns = "/servlet-mvc/members")
public class MvcMemberListServlet extends HttpServlet {
private MemberRepository memberRepository = MemberRepository.getInstance();
@Override
protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
List<Member> members = memberRepository.findAll();
request.setAttribute("members", members);
String viewPath = "/WEB-INF/views/members.jsp";
RequestDispatcher dispatcher = request.getRequestDispatcher(viewPath);
dispatcher.forward(request, response);
}
}
`dispatcher.forward()` : 다른 서블릿이나 JSP 로 이동할 수 있는 기능. 서버 내부에서 다시 호출이 발생
HttpServletRequest 가 제공하는 임시 저장소 기능가 Model 역할을 대신할 수 있다.
`request.setAttribute()` : request 객체에 데이터를 보관하여 뷰에 전달
`request.getAttribute()` : 뷰가 request 객체에 저장된 데이터를 조회
MVC 패턴의 특징
- 항상 컨트롤러를 거쳐 뷰로 넘어간다.
- 컨트롤러와 뷰가 분리되었다
> 참고 <
`/WEB-INF` 경로 안에 JSP 가 있으면, 외부에서 이 JSP 를 직접 호출할 수 없다.
컨트롤러에서만 JSP 를 호출하기 위해, 해당 경로 안에 JSP 파일을 생성한다.
MVC 패턴의 단점
- `dispatcher.forward()` 중복, `viewPath` 중복
- 사용하지 않는 `request`, `rsponse` 파라미터
- 요청을 처리하는 서블릿마다 중복 코드가 발생하기 때문에 공통처리가 어려움
> 다음 글에서 위의 MVC 패턴을 점진적으로 리팩토링 하여 Spring MVC 의 모습으로 변환할 것이다.
출처 | 스프링 MVC 1(김영한) - 인프런
'💠프로그래밍 언어 > Java' 카테고리의 다른 글
[Spring] SpringMVC 의 세부적인 구조 (핸들러, 핸들러 어댑터, 메시지 컨버터) (1) | 2025.03.19 |
---|---|
[Spring] 리팩토링하며 점진적으로 Spring MVC 프레임워크 만들기 !! (0) | 2025.03.14 |
[Spring] 서블릿의 HttpServletRequest 와 HttpServletResponse 란?? (1) | 2025.03.13 |
[Spring] 웹 서버와 웹 애플리케이션 서버(WAS) + 서블릿 컨테이너 (0) | 2025.03.13 |
[Spring] 프로토타입 빈, request 빈에 대해 ! (Provider, 프록시) (1) | 2025.02.28 |