회원 관리 웹 애플리케이션 요구사항
public class MemberRepository {
private static Map<Long, Member> store = new HashMap<>(); //static 사용
private static long sequence = 0L; //static 사용
private static final MemberRepository instance = new MemberRepository();
public static MemberRepository getInstance() {
return instance;
}
private MemberRepository() {
}
회원 저장소에 싱글톤 패턴 적용,
스프링을 사용하면 어짜피 스프링 빈으로 등록이 되지만 일단 스프링을 배제한 서블릿 만으로 구현하는 것이 목적이다.
private
Testcase
@AfterEach
void afterEach() {
memberRepository.clearStore();
}
@AfterEach를 이용해 저장소를 항상 clear 해줘야 한다.
@AfterEach는 테스트 코드가 다 종료되면 수행된다.
서블릿으로 회원 관리 웹 애플리케이션 만들기
서블릿으로 구현할때 JAVA 코드에다가 HTML 넣는것은 너무 비극적이다....
-> 템플릿 엔진 을 사용
템플릿 엔진?
HTML에다가 JAVA 코드를 넣는다.
템플릿이 존재하고, 중간에 값을 바꿀 수 있는 것을 의미
ex) JSP, ** Thymeleaf **, Freemarekt, Velocity 등
(cf) 앞으로 학습할 내용
JSP로 회원 관리 웹 애플리케이션 만들기
JSP 문서의 시작
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
회원 등록 폼 JSP : main/webapp/jsp/members/new-form.jsp
회원 저장 JSP : main/webapp/jsp/members/save.jsp
회원 목록 JSP : main/webapp/jsp/members.jsp -> (여기 작동 안함)
서블릿과 JSP의 한계
서블릿: 뷰(view)화면을 위한 HTMl을 만드는 작업이 자바코드에 섞여서 지저분하고 복잡
JSP: 뷰를 생성하는 HTML 작업을 깔끔하게 가져가고, 동적이 필요한 부분에만 자바코드 적용
BUT
회원 저장과 목록의 JSP 코드를 보면 절반은 비즈니스로직이고, 나머지 절반은 결과를 위한 HTML이다. 즉, JSP에 JAVA코드, 데이터조회 레포지토리 등등 너무 많이 역할이 존재한다.
비즈니스 로직: 서블릿처럼 다른곳에서 처리
화면을 그리는 일: JSP (HTML 사용)
MVC 패턴 - 개요
너무 많은 역할
하나의 서블릿 or JSP = 비즈니스 로직 + 뷰 렌더링 => 너무 빡세다
변경의 라이프 사이클
UI를 수정할때와 비즈니스 로직을 일부 수정할 때가 각각 다르게 발생할 가능성이 매우 높고, 대부분 서로에게 영향을 주지 않는다.
기능 특화
JSP 같은 뷰 템플릿은 화면을 렌더링 하는데 최적화 되어있다. -> 이 부분만 담당하는 것이 가장 효과적이다.
Model View Controller
하나의 서블릿 or JSP에서의 다양한 역할들을 <컨트롤러 + 뷰> 영역으로 서로 역할을 나눈다.
웹 애플리케이션이 보통 MVC 패턴을 사용한다.
(참고)
컨트롤러에 비즈니스 로직을 두기에는 너무 많은 역할을 수행한다.
따라서 비즈니스 로직은 일반적으로 서비스(service) 계층에 따로 만들어 처리하고,
컨트롤러는 이 서비스를 호출하는 역할을 담당한다.
survlet을 controller jsp 를 view로 사용
model은? HttpSurvletRequest 객체를 사용한다
request는 내부에 데이터 저장소를 가지고 있고,
MVC 패턴 - 적용
request.setAttribute()
request.getAttribute()
를 사용해서 데이터를 보관하고, 조회할 수 있다
// hello.servlet.web.servletmvc.MvcMemberFormServlet
String viewPath = "/WEB-INF/views/new-form.jsp";
RequestDispatcher dispatcher = request.getRequestDispatcher(viewPath);
dispatcher.forward(request, response);
dispatcher.forward() : 다른 서블릿이나 JSP로 이동할 수 있는 기능. 서버 내부에서 다시 호출이 발생 (쉽게말하면 서버에서 뺑뺑이)
/WEB-INF : 이 경로안에 JSP가 있으면 외부에서 직접 호출이 불가능하다. (컨트롤러를 통해 JSP(view)를 호출하기를 원한다)
<!-- main/webapp/WEB-INF/views/new-form.jsp -->
<!-- 상대경로 사용, [현재 URL이 속한 계층 경로 + /save] -->
<form action="save" method="post">
username: <input type="text" name="username" />
age: <input type="text" name="age" />
<button type="submit">전송</button>
</form>
/ 가 아닌 상대 경로 (save)를 사용함으로써
현재URL이 속한 계층 경로 + save 가 호출된다.
현재 계층 경로 : /servlet-mvc/members
결과: /servlet-mve/members/save
//MvcMemberSaveServlet
String username = request.getParameter("username");
int age = Integer.parseInt(request.getParameter("age"));
Member member = new Member(username, age);
System.out.println("member = " + member);
memberRepository.save(member);
//Model에 데이터를 보관한다.
request.setAttribute("member", member);
String viewPath = "/WEB-INF/views/save-result.jsp";
RequestDispatcher dispatcher = request.getRequestDispatcher(viewPath);
dispatcher.forward(request, response);
HttpServletRequest를 Model로 사용.
request가 제공하는 setAttribute()를 사용하면 request 객체에 데이터를 보관해서 뷰에 전달할 수 있다.
뷰는 request.getArrtibute()를 사용해서 데이터를 꺼내면 된다.
<!--main/webapp/WEB-INF/views/save-result.jsp -->
<ul>
<li>id=${member.id}</li>
<li>username=${member.username}</li>
<li>age=${member.age}</li>
</ul>
<%= request.getAttribute("member")%> 로 모델에 저장한 member 객체를 꺼낼 수 있지만, 너무 복잡해진다. JSP는 ${} 문법을 제공하는데, 이 문법을 사용하면 request의 attribute에 담긴 데이터를 편리하게 조회할 수 있다.
//MvcMemberListServlet
System.out.println("MvcMemberListServlet.service");
List<Member> members = memberRepository.findAll();
//request 객체를 사용해서 List<member> members를 모델에 보관
request.setAttribute("members", members);
String viewPath = "/WEB-INF/views/members.jsp";
RequestDispatcher dispatcher = request.getRequestDispatcher(viewPath);
dispatcher.forward(request, response);
<!--main/webapp/WEB-INF/views/members.jsp -->
<table>
<thead>
<th>id</th>
<th>username</th>
<th>age</th>
</thead>
<tbody>
<c:forEach var="item" items="${members}">
<tr>
<td>${item.id}</td>
<td>${item.username}</td>
<td>${item.age}</td>
</tr>
</c:forEach>
</tbody>
</table>
MVC 패턴 - 한계
한계
Reference
김영한 님 - 스프링 MVC 1편 - 백엔드 웹 개발 핵심 기술