개발/Servlet, JSP

Servlet - 서블릿, JSP, MVC 패턴

잇(IT) 2023. 6. 14. 12:21
728x90

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

1. 회원 정보

   1. 이름 : username

   2. 나이 : age

2. 기능 요구사항

   1. 회원 저장

   2. 회원 목록 조회


- Servlet

@WebServlet(name = "memberFormServlet", urlPatterns = "/servlet/members/new-form")
public class MemberFormServlet extends HttpServlet {

    private MemberRepository memberRepository = MemberRepository.getInstance();

    @Override
    protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        resp.setContentType("text/html");
        resp.setCharacterEncoding("utf-8");

        PrintWriter w = resp.getWriter();
        w.write("<!DOCTYPE html>\n" +
                "<html>\n" +
                "<head>\n" +
                " <meta charset=\"UTF-8\">\n" +
                " <title>Title</title>\n" +
                "</head>\n" +
                "<body>\n" +
                "<form action=\"/servlet/members/save\" method=\"post\">\n" +
                " username: <input type=\"text\" name=\"username\" />\n" +
                " age: <input type=\"text\" name=\"age\" />\n" +
                " <button type=\"submit\">전송</button>\n" +
                "</form>\n" +
                "</body>\n" +
                "</html>\n");
    }
}

- 서블릿으로 회원 등록 폼을 전부 만들기 위해선 보이는 화면까지 자바 코드로 HTML을 작성해야 한다.

 

- 파라미터로 넘어오는 값은 String이기 때문에 int로 사용하고 싶다면 parseInt와 같이 형변환을 한 후 사용해야 한다.

 

- 템플릿엔진을사용하면 HTML 문서에서필요한곳만코드를적용해서동적으로변경할있다.

- 템플릿엔진에는 JSP, Thymeleaf, Freemarker, Velocity등이있다.


- JSP

<%@ page contentType="text/html;charset=UTF-8" language="java" %>

- 줄은 JSP문서라는뜻이다. JSP 문서는이렇게시작해야한다.

<%@ page import="hello.servlet.domain.member.Member" %>
<%@ page import="hello.servlet.domain.member.MemberRepository" %>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%
    //request, response 그냥 문법상 사용 가능
    MemberRepository memberRepository = MemberRepository.getInstance();

    System.out.println("MemberSaveServlet.service");
    String username = request.getParameter("username");
    int age = Integer.parseInt(request.getParameter("age"));

    Member member = new Member(username, age);
    memberRepository.save(member);
%>
<html>
<head>
    <title>Title</title>
</head>
<body>
성공
<ul>
    <li>id=<%=member.getId()%></li>
    <li>username=<%=member.getUsername()%></li>
    <li>age=<%=member.getAge()%></li>
</ul>
<a href="/index.html">메인</a>
</body>
</html>

JSP자바코드를그대로사용할있다.

- <%@ page import="hello.servlet.domain.member.MemberRepository" %>자바의 import 문과같다.

- <% ~~ %>부분에는자바코드를입력할있다.

- <%= ~~ %>부분에는자바코드를출력할있다.

- 회원저장 JSP보면, 회원저장서블릿코드와같다. 다른점이있다면, HTML중심으로하고, 자바코드를부분부분입력해주었다.<% ~ %>사용해서 HTML 중간에자바코드를출력하고있다.

 

- JSP만 사용하게 되면 JSP에 JAVA코드, Repository, 등 다양한 코드가 노출이 되어 있다.


- MVC 패턴

 

- Model View Controller

- MVC 패턴은지금까지학습한처럼하나의서블릿이나, JSP처리하던것을컨트롤러(Controller)(View)라는영역으로서로역할을나눈것을말한다. 애플리케이션은보통 MVC 패턴을사용한다.

1. 컨트롤러: HTTP 요청을받아서파라미터를검증하고, 비즈니스로직을실행한다. 그리고뷰에전달할결과데이터를조회해서모델에담는다.

2. 모델: 뷰에출력할데이터를담아둔다. 뷰가필요한데이터를모두모델에담아서전달해주는덕분에뷰는비즈니스로직이나데이터접근을몰라도되고, 화면을렌더링하는일에집중할있다.

3. 뷰: 모델에담겨있는데이터를사용해서화면을그리는일에집중한다. 여기서는 HTML생성하는부분을말한다.

- 서블릿을컨트롤러로사용하고, JSP뷰로사용해서 MVC 패턴을적용한다.

- Model HttpServletRequest 객체를사용한다.

- request내부에데이터저장소를가지고있는데,request.setAttribute(),request.getAttribute()사용하면데이터를보관하고, 조회할있다.

@WebServlet(name = "mvcMemberFormServlet", urlPatterns = "/servlet-mvc/members/new-form")
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);//controller에서 view로 이동할 때 사용한다.
        dispatcher.forward(request, response);
    }
}

- dispatcher.forward() : 다른서블릿이나 JSP이동할있는기능이다. 서버내부에서다시호출이 발생한다.

- /WEB-INF : 경로안에 JSP있으면외부에서직접 JSP호출할없다. 우리가기대하는것은항상컨트롤러를통해서 JSP호출하는것이다.

 

 - redirect vs forward

1. 리다이렉트는실제클라이언트(브라우저)응답이나갔다가, 클라이언트가 redirect 경로로다시요청한다. 따라서클라이언트가인지할있고, URL 경로도실제로변경된다. 반면에포워드는서버내부에서일어나는호출이기때문에클라이언트가전혀인지하지못한다.

 

- new-form.jsp

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
  <meta charset="UTF-8">
  <title>Title</title>
</head>
<body>
<!-- 상대경로 사용, [현재 URL이 속한 계층 경로 + /save] -->
<form action="save" method="post">
  username: <input type="text" name="username" />
  age: <input type="text" name="age" />
  <button type="submit">전송</button>
</form>
</body>
</html>

 

- 여기서 form action보면절대경로(/시작)아니라상대경로(/시작X)것을확인할있다. 이렇게상대경로를사용하면전송시현재 URL속한계층경로 + save호출된다.

1. 현재계층경로:/servlet-mvc/members/

2. 결과:/servlet-mvc/members/save

 

@WebServlet(name = "mvcMemberSaveServlet", urlPatterns = "/servlet-mvc/members/save")
public class MvcMemberSaveServlet extends HttpServlet {

    private MemberRepository memberRepository = MemberRepository.getInstance();

    @Override
    protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        String username = req.getParameter("username");
        int age = Integer.parseInt(req.getParameter("age"));

        Member member = new Member(username, age);
        memberRepository.save(member);

        //Model에 데이터를 보관한다.
        req.setAttribute("member", member);

        String viewPath = "/WEB-INF/views/save-result.jsp";
        RequestDispatcher dispatcher = req.getRequestDispatcher(viewPath);
        dispatcher.forward(req,resp);
    }
}

- HttpServletRequest Model사용한다.

- request제공하는setAttribute()사용하면 request 객체에데이터를보관해서뷰에전달할있다.

- 뷰는request.getAttribute()사용해서데이터를꺼내면된다.

 

- save-result.jsp

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
  <title>Title</title>
</head>
<body>
성공
<ul>
  <li>id=${member.id}</li>
  <li>username=${member.username}</li>
  <li>age=${member.age}</li>
</ul>
<a href="/index.html">메인</a>
</body>
</html>

- <%= request.getAttribute("member")%>모델에저장한 member 객체를꺼낼있지만, 너무복잡해진다.

- JSP${}문법을제공하는데, 문법을사용하면 request attribute담긴데이터를편리하게조회할있다.

 

@WebServlet(name = "mvcMemberListServlet", urlPatterns = "/servlet-mvc/members")
public class MvcMemberListServlet extends HttpServlet {

    private MemberRepository memberRepository = MemberRepository.getInstance();

    @Override
    protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {

        List<Member> members = memberRepository.findAll();

        req.setAttribute("members", members);

        String viewPath = "/WEB-INF/views/members.jsp";
        RequestDispatcher dispatcher = req.getRequestDispatcher(viewPath);
        dispatcher.forward(req, resp);
    }
}

- request 객체를사용해서List<Member> members모델에보관했다.

 

- members.jsp

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<html>
<head>
  <meta charset="UTF-8">
  <title>Title</title>
</head>
<body>
<a href="/index.html">메인</a>
<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>
</body>
</html>

- 모델에담아둔 members JSP제공하는 taglib기능을사용해서반복하면서출력했다.members리스트에서member순서대로꺼내서item변수에담고, 출력하는과정을반복한다.

- <c:forEach>기능을사용하려면다음과같이선언해야한다.

* <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>


- MVC 패턴 - 한계

 

- 포워드중복

1. View이동하는코드가항상중복호출되어야한다. 물론부분을메서드로공통화해도되지만, 해당메서드도항상직접호출해야한다.

 

- 사용하지않는코드

1. 다음코드를사용할때도있고, 사용하지않을때도있다. 특히 response현재코드에서사용되지않는다.

 

- 공통 처리가 어렵다 

 

 

 

 

 

 

 

 

 

 

 

 

 

출처 : 인프런 - 우아한 형제들 기술이사 김영한의 스프링 완전 정복 (스프링 핵심원리 - 기본 편)

728x90