[Spring] 스프링 MVC 1편 - 백엔드 웹 개발 핵심 기술 - 강의 정리 - 3

JJAM·2022년 9월 15일
0
post-thumbnail

📖 서블릿, JSP, MVC 패턴

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

  • 회원 정보
    username, age
  • 기능
    회원 저장, 회원 목록 조회

✏️ 회원 도메인 모델

src/main/java/hello/servlet/domain/member/Member

회원 도메인에 필요한 id(식별자), username, age를 만들고,
기본 생성자와 username과 age 정보가 들어간 회원 정보 생성자를 만들어 준다.

@Getter @Setter
public class Member {

	// id, username, age 생성

    // 기본 생성자

    // 회원 정보 생성자
    public Member(String username, int age) {
        this.username = username;
        this.age = age;
    }
}

✏️ 회원 저장소

src/main/java/hello/servlet/domain/member/MemberRepository

회원 저장소는 싱글톤 패턴을 적용하며,
회원을 저장하는 save, id로 회원을 조회하는 findById, 전체 회원을 조회하는 findAll, 저장소를 비우는 clearStore 를 만든다.

public class MemberRepository {

    // key: id, value: Member(username, age) HashMap 생성
    // id 값이 1씩 증가하는 시퀀스 생성

    // 싱글톤 패턴 적용
    // (객체의 인스턴스가 오직 1개만 생성, 객체를 미리 생성해두고 가져옴)

    // 회원 저장
    public Member save(Member member) {
        ...
    }

    // id로 회원을 조회
    public Member findById(Long id) {
        ...
    }
    
    // 전체 회원 조회
    public List<Member> findAll() {
        ...
    }

    // 저장소 비우기
    public void clearStore() {
        ...
    }
}

✏️ 회원 저장소 테스트 코드

src/test/java/hello/servlet/domain/member/MemberRepositoryTest

회원 저장소에서 만든 기능이 제대로 작동하는지 확인하는 Test 코드를 만든다.

class MemberRepositoryTest {

    // 싱글톤
    MemberRepository memberRepository = MemberRepository.getInstance();

    @AfterEach // Test 끝날 때, 저장소 내용 모두 비우기
    void afterEach() {
        ...
    }

    @Test
    void save() {
    
        // 멤버 객체 생성

        // 멤버 객체 저장
        Member saveMember = memberRepository.save(member);

        // 저장된 멤버의 아이디를 저장소에서 찾음
        Member findMember = memberRepository.findById(saveMember.getId());

        // 저장된 멤버와 찾은 멤버가 같은 지 비교
    }

    @Test
    void findAll() {
    
        // 멤버 객체 생성

        // 멤버 객체 저장

        // 저장소에 저장된 모든 멤버 찾기
        List<Member> result = memberRepository.findAll();

        // 찾은 멤버가 2개 인지 비교

        // 찾은 결과에 member1, member2 있는지 비교
    }
}

Test를 실행하면 정상적으로 동작하는 것을 확인할 수 있다.

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

✏️ 회원 등록 폼 서블릿

src/main/java/hello/servlet/web/servlet/MemberFormServlet

회원 등록 폼을 서블릿으로 만들기 위해, 우선 서블릿 어노테이션을 추가한다.

@WebServlet(name = "memberFormServlet", urlPatterns = "/servlet/members/new-form")

회원 등록 폼을 화면에다가 그려줘야 하기 때문에, responsebodyHTML 폼 응답 보낸다.

PrintWriter w = response.getWriter();
        w.write("<!DOCTYPE html>\n" +
                	...
                "<form action=\"/servlet/members/save\" method=\"post\">\n" +
                	...
                "</html>\n");

http://localhost:8080/servlet/members/new-form에 들어가 보면 HTML 폼의 내용이 그려진 것을 볼 수 있다.

✏️ 회원 저장 서블릿

src/main/java/hello/servlet/web/servlet/MemberSaveServlet

회원 등록 폼에 내용을 입력해서 제출했을 때, 그 회원 정보를 저장하는 서블릿을 만들기 위해 우선 서블릿 어노테이션을 추가한다.

@WebServlet(name = "memberSaveServlet", urlPatterns = "/servlet/members/save")

폼에서 전송한 내용이 쿼리 파라미터 형식으로 전달되기 때문에 request.getParameter() 로 조회한다.

String username = request.getParameter("username");
int age = Integer.parseInt(request.getParameter("age"))

조회한 username와 age 값으로 member객체를 생성하고, 멤버 객체를 저장소에 저장한다. 참고로 memberRepository는 싱글톤이다.

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

저장소에 저장한 member 객체의 내용을 볼 수 있게 responsebodyHTML 응답 보내는 코드를 작성한다.

PrintWriter w = response.getWriter();
        w.write("<html>\n" +
					...
                " <li>id="+member.getId()+"</li>\n" +
                	...
                "</html>");

그러면 폼에서 작성한 내용이 HTML 응답으로 통해 볼 수 있다.

✏️ 회원 목록 조회 서블릿

src/main/java/hello/servlet/web/servlet/MemberListServlet

저장된 모든 회원 목록을 조회하는 기능을 만들기 위해 서블릿 어노테이션을 추가한다.

@WebServlet(name = "memberListServlet", urlPatterns = "/servlet/members")

memberRepository에 저장된 모든 List 가져온다. 참고로 memberRepository는 싱글톤이다.

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

저장 목록를 보기 위한 message bodyHTML 응답을 보내는 코드를 작성한다.
참고로 저장소에 저장된 모든 목록을 보기 위해 for 루프를 통해서 회원 수 만큼 동적으로 생성한다.

PrintWriter w = response.getWriter();
        w.write("<html>");
        	...
        for (Member member : members) { // 동적
            	...
            w.write(" <td>" + member.getId() + "</td>");
            	...
        }
        	...
        w.write("</html>");

그러면 http://localhost:8080/servlet/members에서 저장한 회원 목록 조회를 할 수 있다.

📒 JSP로 회원 관리 웹 애플리케이션 만들기

서블릿으로 자바 코드로 HTML을 만드는 것은 매우 불편해 차라리 HTML 문서에 자바 코드를 넣는 것이 더 편리하다.
그래서 템플릿 엔진을 사용하면, HTML 문서에서 필요한 곳만 동적으로 변결할 수 있다.
템플릿 엔진에는 JSP, Thymeleaf, Freemarker, Velocity등이 있다.

JSP를 사용하기 위해 build.gradle에 라이브러리를 추가한다.

implementation 'org.apache.tomcat.embed:tomcat-embed-jasper'
implementation 'javax.servlet:jstl'

✏️ 회원 등록 폼 JSP

src/main/webapp/jsp/members/new-form.jsp

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
  ...
  <body>
    <form action="/jsp/members/save.jsp" method="post">
      ...
    </form>
  </body>
</html>

JSP로 폼을 만들고 http://localhost:8080/jsp/members/new-form.jsp에 들어가면 정상적으로 폼이 만들어진 것을 확인할 수 있다.

✏️ 회원 저장 JSP

위에서 작성한 폼의 내용을 전송했을 때 회원을 저장하는 JSP 파일을 만들어 준다.

<%@ page import=" " %> - 자바의 import 문과 같음

<% ~~ %> - 자바 코드 입력, request, response 사용 가능

<%= ~~ %> - 자바 코드 출력

src/main/webapp/jsp/members/save.jsp

<%@ page import="hello.servlet.domain.member.MemberRepository" %>
<%@ page import="hello.servlet.domain.member.Member" %>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%

   // MemberSaveServlet처럼 
   // 쿼리 파라미터 조회 -> 멤버 객체 생성 -> 멤버 객체를 저장소에 저장 
 
%>
<html>
  ...
    <ul>
      <li>id=<%=member.getId()%></li>
      ...
    </ul>
    ...
</html>

그러면 JSP로 만든 폼에 데이터를 입력하고 전송하면, 입력한 데이터의 값을 볼 수 있게 되며, 저장소에 값을 저장하게 된다.

✏️ 회원 목록 조회 JSP

JSP로 회원 목록하는 코드를 작성한다.

src/main/webapp/jsp/members.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" %>

<%
	// MemberListServlet 처럼
    // 저장소에 저장된 모든 List 가져옴
%>

<html>
  ...
    <tbody>
    <%
      for (Member member : members) {
        ...
        out.write(" <td>" + member.getId() + "</td>");
        ...
      }
    %>
    </tbody>
  ...
</html>

정상적으로 JSP로 회원 목록을 확인할 수 있다.

JSP의 한계
JAVA 코드, 데이터를 조회하는 리포지토리 등등 다양한 코드가 모두 JSP에 노출되어 있음
-> MVC 패턴 등장

📒 MVC 패턴 - 개요

Model View Controller(MVC)

서블릿이나, JSP로 처리하던 것을 컨트롤러(Controller)뷰(View) 라는 영역으로 역할을 나눔

  • 컨트롤러

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

  • 모델

    뷰에 출력할 데이터를 담아 전달함

  • 모델에 담겨있는 데이터를 사용해서 화면을 그림 ex) HTML 생성

📒 MVC 패턴 - 적용

서블릿컨트롤러로 사용하고, JSP로 사용해서 MVC 패턴을 적용하고,
모델HttpServletRequest 객체를 사용할 것이다.

왜냐하면 request는 내부에 데이터 저장소를 가지고 있어 request.setAttribute() , request.getAttribute() 를 사용하면 데이터를 저장하고 조회할 수 있기 때문이다.

✏️ 회원 등록 폼 controller, view

src/main/java/hello/servlet/web/servletmcv/MvcMemberFormServlet

서블릿을 사용해 회원 등록 폼 controller를 만들기 위해 서블릿 어노테이션을 추가한다.

@WebServlet(name ="mvcMemberFormServlet",urlPatterns = "/servlet-mvc/members/new-form")

JSP(view) 호출을 하기 위해 경로를 viewPath에 담고, 서버 내부에서 다시 호출 하기 위해 forward를 사용한다.

String viewPath = "/WEB-INF/views/new-form.jsp";

RequestDispatcher dispatcher = request.getRequestDispatcher(viewPath);
dispatcher.forward(request, response);

src/main/webapp/WEB-INF/views/new-form.jsp

위에 있는 서블릿(controller)에서 호출한 JSP(view) 를 만든다.
해당 경로에서 WEB-INF는 외부에서 직접 JSP를 호출할 수 없게 해준다.

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
    ...
        <form action="save" method="post">
            username: <input type="text" name="username" />
            age: <input type="text" name="age" />
        	...
        </form>
    ...
</html>

http://localhost:8080/servlet-mvc/members/new-form에 들어가면 정상적으로 회원 등록 폼을 볼 수 있다.

✏️ 회원 저장 controller, view

src/main/java/hello/servlet/web/servletmcv/MvcMemberSaveServlet

서블릿을 사용해 회원 저장 controller를 만들기 위해 서블릿 어노테이션을 추가한다.

@WebServlet(name = "mvcMemberSaveServlet", urlPatterns = "/servlet-mvc/members/save")

MemberSaveServlet 처럼
쿼리 파라미터 조회 -> 멤버 객체 생성 -> 멤버 객체를 저장소에 저장하는 코드를 작성하고,
request 내부에 데이터 저장소를 사용하여 model에 데이터 보관하는 코드를 작성한다.

request.setAttribute("member",member);

JSP(view) 호출을 하기 위해 경로를 viewPath에 담고, 서버 내부에서 다시 호출 하기 위해 forward를 사용한다.

String viewPath = "/WEB-INF/views/save-result.jsp";
// 컨트롤러에서 뷰로 이동하는 코드

src/main/webapp/WEB-INF/views/new-form.jsp

위에 있는 서블릿(controller)에서 호출한 JSP(view) 를 만든다.

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
    ...
        <li>id=${member.id}</li>
        <li>username=${member.username}</li>
        <li>age=${member.age}</li>
    ...
</html>

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

회원 등록 폼을 제출하면 정상적으로 작동되는 것을 볼 수 있다.

✏️ 회원 목록 조회 controller, view

src/main/java/hello/servlet/web/servletmcv/MvcMemberListServlet

서블릿을 사용해 회원 목록 조회 controller를 만들기 위해 서블릿 어노테이션을 추가한다.

@WebServlet(name = "mvcMemberListServlet", urlPatterns = "/servlet-mvc/members")

MemberListServlet 처럼 저장소에 저장된 모든 List 가져오고,
request 내부에 데이터 저장소를 사용하여 model에 데이터 보관하는 코드를 작성하고,
JSP(view) 호출을 하기 위해 경로를 viewPath에 담고, 서버 내부에서 다시 호출 하기 위해 forward를 사용한다.

String viewPath = "/WEB-INF/views/members.jsp";

src/main/webapp/WEB-INF/views/members.jsp

위에 있는 서블릿(controller)에서 호출한 JSP(view) 를 만든다.

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<html>
    ...
        <c:forEach var="item" items="${members}">
            <tr>
            <td>${item.id}</td>
            <td>${item.username}</td>
            <td>${item.age}</td>
            </tr>
        </c:forEach>
    ...
</html>

http://localhost:8080/servlet-mvc/members에 들어가면 정상적으로 회원 목록을 조회할 수 있다.

📒 MVC 패턴 - 한계

  • 어려운 공통 처리

  • 포워드 중복

RequestDispatcher dispatcher = request.getRequestDispatcher(viewPath);
dispatcher.forward(request, response);
  • ViewPath 중복

String viewPath = "/WEB-INF/views/???.jsp";
  • 사용하지 않는 코드

HttpServletRequest request, HttpServletResponse response

-> 프론트 컨트롤러(Front Controller) 패턴 도입


지금까지 김영한 - 스프링 MVC 1편 - 백엔드 웹 개발 핵심 기술(유료강의) 강의를 참고하여 서블릿, JSP, MVC 패턴 에 대해 공부하였다.

profile
☘️

0개의 댓글