IT recording...

[스프링 MVC1] 3. 서블릿,JSP,MVC 패턴 본문

Spring

[스프링 MVC1] 3. 서블릿,JSP,MVC 패턴

I-one 2022. 1. 14. 11:24

원문 링크

https://adorable-aspen-d23.notion.site/MVC1-3-JSP-MVC-272054e5d8d34abaa62293af7d98e8f4

 

[스프링 MVC1] 3. 서블릿,JSP,MVC 패턴

목차

adorable-aspen-d23.notion.site

김영한님의 [스프링 MVC 1편 - 백엔드 웹 개발 핵심 기술] 강의를 듣고 작성한 글입니다.

1. 요구사항 분석

  • 회원 관리 어플리케이션
  • 회원 정보
    • username, age
  • 기능 정보
    • 회원 저장
    • 회원 목록 조회
  • 회원 도메인 제작, 리포지토리 구성
@Getter
@Setter
public class Member {
    private Long id;
    private String username;
    private int age;

    public Member(){
    }

    public Member(String username, int age){
        this.username = username;
        this.age = age;
    }
}
/**
 * 동시성 문제가 고려되어 있지 않음, 실무에서는 ConcurrentHashMap, AtomicLong 사용 고려
 */
public class MemberRepository {
    private static Map<Long, Member> store = new HashMap<>();
    private static long sequence = 0L; //id 하나씩 증가

    //싱글톤으로 생성
    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();
    }
}

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

  • 자바 코드로 html을 구성해야 하기 때문에 권장되지 않는다.
    • 동적으로 html을 구성하기 위해 템플릿 엔진이 등장했다.
    • 템플릿 엔진 : HTML문서에서 필요한 부분만 코드를 적용하여 동적으로 변경 가능
      • JSP, Tymeleaf, Freemarker, Velocity 등
@WebServlet(name = "memberFormServlet", urlPatterns = "/servlet/members/new-form")
public class MemberFormServlet extends HttpServlet {
    private MemberRepository memberRepository = MemberRepository.getInstance();

    @Override
    protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        //응답으로 http 응답이 나가야함
        response.setContentType("text/html");
        response.setCharacterEncoding("utf-8");

        //서블릿을 사용하게 되면 자바 코드로 html을 작성해야 하기 때문에 불편하다.
        PrintWriter w = response.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");
    }
}

3. JSP이용

  • gradle 추가
//JSP 추가 시작
implementation 'org.apache.tomcat.embed:tomcat-embed-jasper'
implementation 'javax.servlet:jstl'
//JSP 추가 끝
<%@ page import="hello.servlet.domain.member.MemberRepository" %>
<%@ page import="hello.servlet.domain.member.Member" %>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%
// request, response 사용 가능
 MemberRepository memberRepository = MemberRepository.getInstance();
 System.out.println("save.jsp");
 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);
%>
<html>
<head>
 <meta charset="UTF-8">
</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 안에서는 자바 코드를 그대로 사용 가능하다.
  • 서블릿에서 HTML을 생성하는 부분을 자바 코드로 작성해야 했던 단점은 보완되었지만,
    • 비즈니스 로직과 뷰가 분리되지 않는 단점이 존재한다.
    • → 유지보수 어려움
    ⇒ MVC 패턴 등장

4. MVC패턴

  • 개요
    • 너무 많은 역할
      • 하나의 서블릿이나 jsp가 비즈니스 로직과 뷰 렌더링까지 가져가게 되면, 유지보수가 어렵다.
    • 변경의 라이프사이클
      • 결정적으로 비즈니스 로직과 뷰는 라이프 사이클이 다르다.
  • Model View Controller
  • 컨트롤러
    • HTTP 요청을 받아 파라미터를 검증하고, 비즈니스 로직을 실행한다.
    • 뷰에 전달한 결과 데이터롤 조회해서 모델에 담는다.
  • 모델
    • 뷰에 출력할 데이터를 담아둔다.
    • 뷰가 필요한 데이터를 모두 모델에 담아 전달해주는 덕분에 뷰는 비즈니스 로직이나 데이터 접근을 몰라도 되고, 화면을 렌더링 하는 일에 집중할 수 있다.
    • 모델에 담겨있는 데이터를 사용해서 화면을 그리는 일에 집중한다.
    • HTML 생성
  • 적용
@WebServlet(name = "mvcMemberListServlet", urlPatterns = "/servlet-mvc/members")
public class MvcMembeListServlet extends HttpServlet {
    private MemberRepository memberRepository = MemberRepository.getInstance();

    @Override
    protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        List<Member> members = memberRepository.findAll();

        //model에 담기
        request.setAttribute("members",members);

        String viewPath = "/WEB-INF/views/members.jsp";
        RequestDispatcher dispatcher = request.getRequestDispatcher(viewPath);
        dispatcher.forward(request,response);
    }
}
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ taglib prefix="c" uri="<http://java.sun.com/jsp/jstl/core>"%>
    


메인
id username age
${item.id} ${item.username} ${item.age}


  • redirect vs forward
    • redirect
      • 클라이언트에 응답이 나갔다가, redirect 경로로 다시 요청하는 것
    • forward
      • 서버 내부에서 일어나는 호출, 클라이언트는 알 수 없다.

5. MVC패턴 한계

  • 포워드 중복
RequestDispatcher dispatcher = request.getRequestDispatcher(viewPath);
dispatcher.forward(request, response);
  • ViewPath 중복
String viewPath = "/WEB-INF/views/new-form.jsp";

prefix : /WEB-INF/views/ 부분
suffix : .jsp 부분
이 중복된다.
  • 사용하지 않는 코드들
  • request,response를 모두 사용하지 않는 경우가 많다.
  • 공통 처리가 어렵다.
    • 기능이 복잡해질수록 컨트롤러에서 공통으로 처리해야 하는 부분이 증가한다.

⇒ 프론트 컨틀롤러 패턴 도입

Comments