월루를 꿈꾸는 대학생

[MVC-1] 서블릿 , JSP , MVC패턴 본문

Programing/Spring Boot

[MVC-1] 서블릿 , JSP , MVC패턴

하즈시 2023. 7. 25. 22:56
728x90

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

그냥 회원정보에 이름과 나이를 넣고 저장 및 조회하는 기능을 구현한다!

지금은 그냥 자바로만!
스프링없으니까 싱글톤 보장도 안 되니까 싱글톤도 알아서 만든다

public class MemberRepository {  
    private Map<Long, Member> store = new HashMap<>();  
    private static long sequence = 0L;  

    private static final MemberRepository instance = new MemberRepository();  

    public static MemberRepository getInstance(){  
        return instance;  
    }  
    // 생성자를 프라이빗으로 해줘야 밖에서 생성못함  
    private MemberRepository(){  

    }  

    public Member save(Member member) {  
        member.setId(++sequence);  
        store.put(member.getId(), member);  
        return member;  
    }  
    public Member findById(Long id) {  
        return store.get(id);  
    }  
    public List<Member> findAll() {  
        return new ArrayList<>(store.values());  
    }  
    public void clearStore() {  
        store.clear();  
    }  
}

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

MemberFormServlet 에는 회원 정보 입력할 수 있는 html form을 하나씩하나씩 만들어서 응답에 넣어줘야하는데 매우 귀찮음..

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");

응답을 할 때 해당 write안에다가 동적으로 변수 같은 거 넣어서 만들기 가능
다만 저렇게 만들어서 응답해주기가 매우 귀찮

// 다음과 같이 동적으로 만들기 가능 for (Member member : members) {  
    w.write(" <tr>");  
    w.write(" <td>" + member.getId() + "</td>");  
    w.write(" <td>" + member.getUsername() + "</td>");  
    w.write(" <td>" + member.getAge() + "</td>");  
    w.write(" </tr>");  
}

서블릿을 활용해서 값 받아와서 응답해주고 로직실행해주는 거는 좋은데 동적인 html만들기 가능
응답할 때 html만들어주는 게 매우매우 귀찮다

그래서 템플릿 엔진을 사용
-> html코드에다가 중간 중간에 자바코드를 넣는 것

  • jsp , 타임리프

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

  • jsp 파일에다가 html코드와 자바코드를 넣어서 화면에 응답해줌

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
첫 줄에 넣어줘야 인식함

<%@ page import="hello.servlet.domain.member.MemberRepository" %>
자바 코드 import해줌

<% ~~ %>
자바 코드 입력 가능

<%= ~~ %>
자바 코드 출력 가능

<%@ 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>  
<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>    <%  
        for (Member member : members) {  
            out.write(" <tr>");  
            out.write(" <td>" + member.getId() + "</td>");  
            out.write(" <td>" + member.getUsername() + "</td>");  
            out.write(" <td>" + member.getAge() + "</td>");  
            out.write(" </tr>");  
        }  
    %>  
    </tbody>  
</table>  
</body>  
</html>
  • tr , td 부분은 반복문을 돌려서 동적 html 만들어둠
서블릿과 JSP 한계

서블릿은 뷰 화면을 만드는 작업이 매우 지저분
JSP는 뷰 화면은 html 은 깔끔해짐

다만 jsp 파일 안에 비즈니스 로직과 뷰 영역이 혼재있음
또한 jsp 파일 안에 수많고 다양한 코드가 노출되어 있어서 유지보수가 답도 없다 = 코드가 너무 길어지고 비즈니스와 화면단을 나눌 수가 없으니까

mvc 패턴 등장

  • 비즈니스 로직과 뷰를 나눔

MVC 패턴 개요

개요
  • 너무 많은 역할 : jsp는 한 페이지에 비즈니스 로직이랑 뷰 전부 코드에 넣고 돌려야하니까 유지보수가 힘들어진다
  • 변경 라이프사이클 : 뷰랑 로직의 변경들은 따로따로 일어나기 때문에 하나의 코드로 관리하는 것은 비효율
  • 기능 특화 : jsp 는 화면 랜더링 최적화인데 이 부분만 담당하는게 효과적
Model View Controller
  1. 컨트롤러
  • http 요청의 파라미터 검증
  • 비즈니스 로직 실행 -> 코드가 있는게 아니라 실행이다 로직은 서비스에 저장됨 컨트롤러는 단순히 호출만!
  • 결과 데이터를 모델에 담아서 뷰에 전달
  1. 모델
  • 뷰에 출력할 데이터를 담아두는 곳
  • 모델에 담겨 있는 데이터를 활용해서 화면에 집중
  • 모델 덕분에 따로 비즈니스 로직없이 해당 값들로 화면 그리기만 하면 되니까 굳굳



MVC 패턴 - 적용

서블릿 : 컨트롤러
jsp : 뷰
로 역할을 나눠서 코드 작성

Model

  • HttpServletRequest객체를 사용
  • 내부에 저장소를 가지고 있는데 이 저장소에 대이터를 저장하고 보관 가능
  • 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);  
        dispatcher.forward(request,response);  
    }  
}

dispatcher.forward() : 다른 서블릿 , JSP로 이동 가능 . 서버 내부에서 다시 호출
리다이렉트가 아님(클라이언트 갔다가 다시 바뀌는 거)
서버 안에서 다시 호출해서 해당 경로 jsp파일 보여줌

/WEB-INF

  • 해당 경로에 jsp 파일 있는 경우 외부에서 url로 jsp 파일 못 부름
  • 컨트롤러를 통해서만 접근해서 클라이언트에게 보여줄 수 있음

redirect vs forward

  • 리다이렉트는 실제 클라이언트에게 응답 갔다가 클라이언트가 다시 서버에게 redirect에 적힌 경로를 요청한 다음 다시 받는 과정이다. 총 2번 받겠네
  • 포워드는 서버 내부에서 호출하니까 클라이언트는 인식도 못함
<form action="save" method="post">
    username: <input type="text" name="username" />
    age: <input type="text" name="age" />
    <button type="submit">전송</button>
</form>

다음과 같이 action에 경로를 "save" 로 해두면 절대 경로 (/로 시작하는 거)가 아닌 상대경로로 인식함
즉 넘겨주는 경로는 현재 경로 계층에서 + save를 더한 것

멤버 저장하는 서블렛

@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 {  
        //http 요청으로 값을 받고 해당 값들을 따로 변수로 저장 후 멤버 객체에 넣고 리포지토리에 저장  
        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이라는 저장소에  model객체를 저장  
        request.setAttribute("member", member);  
        String viewPath = "/WEB-INF/views/save-result.jsp";  
        // 내부에서 다른 jsp호출하도록 실행  
        RequestDispatcher dispatcher = request.getRequestDispatcher(viewPath);  
        dispatcher.forward(request, response);  
    }  
}
  • model이라는 저장소에 member객체를 잠시 저장한 다음 jspv파일 호출할 때 request객체에 담아서 보냄

다시 호출 되는 jsp파일에서 request객체를 받고 거기 포함된 model 저장소에 있는 member 객체를 jsp파일이 확인 한 다음 해당 객체에 있는 변수들을 통해서 동적으로 뷰를 랜더링

${ } : 해당 문법을 사용해서 request의 attribute에 담긴 데이터를 조회할 수 있음

<ul>  
  <%--해당 Jsp파일에서 모델 값을 받고 해당 모델을 통해서 동적으로 html파일을 만든다--%>  
  <li>id=${member.id}</li>  
  <li>username=${member.username}</li>  
  <li>age=${member.age}</li>  
</ul>

mvc패턴을 사용하면 기존에 jsp만 쓰는 것 보다 코드 보기가 훨씬 편해진다 -> 코드를 나누니 양이 한 번에 볼 코드가 적고 유지보수가 편해짐

jsp 반복문

jsp 파일 위에

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

코드를 추가한 후 아래와 같이 반복문을 사용해서 동적 뷰를 랜더링 가능

<c:forEach var="item" items="${members}">  
    <tr>  
        <td>${item.id}</td>  
        <td>${item.username}</td>  
        <td>${item.age}</td>  
    </tr></c:forEach>

MVC 패턴 한계

  • mvc 패턴을 통해서 비즈니스 로직과 뷰를 나눌 수 있어 편해짐
  • 컨트롤러 부분에서 코드의 중복이 많고 필요하지 않은 코드도 있음
  1. 포워드 중복
    • view로 이동할 때 항상 코드 호출이 필요
      String viewPath = "/WEB-INF/views/new-form.jsp";
      RequestDispatcher dispatcher = request.getRequestDispatcher(viewPath);
      dispatcher.forward(request, response);

2.사용하지 않는 코드

  • request , response는 사용할 때도 있지만 response경우 안 할때도 많음

    HttpServletRequest request, HttpServletResponse response

  1. 공통처리가 어려움
    • 컨트롤러에서 처리할 공통적인 부분이 있을텐데 해당 부분을 메서드로 뽑아도 계속 메서드를 호출해야하고 호출하지 않아도 문제가 될 수 있으니 ... 호출자체도 중복..

즉 이런 문제를 해결하기 위해서 서블릿이 호출 되기 전 공통처리를 먼저 처리해야함
그런 수문장역할이 필요 = 프론트 컨트롤러 패턴 (스프링 mvc의 핵심 )

출처
https://inf.run/wFfL

728x90

'Programing > Spring Boot' 카테고리의 다른 글

[MVC-1] 스프링 MVC - 구조 이해  (0) 2023.08.01
[MVC-1] MVC 프레임 워크 만들기  (0) 2023.08.01
[MVC-1] 서블릿  (0) 2023.07.25
[MVC-1] 웹 애플리케이션 이해  (0) 2023.07.25
[핵심원리 - 기본] 빈스코프  (0) 2023.07.25