서블릿, JSP, MVC 패턴

유동우·2023년 4월 8일
0
post-thumbnail

회원 관리 웹 애플리케이션 요구사항

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

  • 싱글톤 패턴은 객체를 단 하나만 생성해서 공유해야함
  • 따라서 생성자를 private 으로 막아둠

Testcase

  • 테스크 코드의 순서가 랜덤으로 실행되므로
@AfterEach
void afterEach() {
    memberRepository.clearStore();
}

@AfterEach를 이용해 저장소를 항상 clear 해줘야 한다.
@AfterEach는 테스트 코드가 다 종료되면 수행된다.


서블릿으로 회원 관리 웹 애플리케이션 만들기

MemberFormServlet - 회원 등록 폼

  • 단순하게 회원 정보를 입력할 수 있는 HTML Form 을 만들어서 응답
  • JAVA code로 HTML을 만듦으로 쉽지 않다.

MemberSaveServlet - 회원 저장 동작 순서

  • 1.파라미터를 조회해서 Member 객체를 만든다
  • 2.Member 객체를 MemberRepository를 통해서 저장한다
  • 3.Member 객체를 사용해서 결과 화면용 HTML을 동적으로 만들어서 응답한다.

MemberListServlet - 회원 목록 동작 순서

    1. memberRepository.findAll()을 통해 모든 회원을 조회한다.
    1. 회원 목록 HTML을 동적으로 생성하고 응답하기 위해 회원 수 만큼 for 루프 지정한다

서블릿으로 구현할때 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코드, 데이터조회 레포지토리 등등 너무 많이 역할이 존재한다.

MVC 패턴의 등장

비즈니스 로직: 서블릿처럼 다른곳에서 처리
화면을 그리는 일: 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편 - 백엔드 웹 개발 핵심 기술

profile
효율적이고 꾸준하게

0개의 댓글