스프링 웹 MVC 1편 - 3. 서블릿, JSP, MVC 패턴

링딩·2022년 7월 26일
0

스프링 MVC

목록 보기
3/18

이 글은 김영한 강사님의 강의를 참고하여 작성하였습니다.

Chap 3. 서블릿, JSP, MVC 패턴

이 곳에서 가장 많이 다룰 것은 각각의 차이점이 될 것 같은데
이 중 핵심만 작성하기 위해 간결하게 작성하였다.

1. 서블릿의 주요 메서드 및 폼

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

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

✨ 서블릿의 장단점

  • 서블릿을 이용하니 일일히 service() 내에 '자바코드'를 이용해 HTML을 만들었다.
    -> 그러다 보니 너무 복잡 비효율 적이다.
    => HTML을 분리하고 필요한 부분만 자바코드를 넣는 방법은 없을까? 🤔
    => 템플릿 엔진을 이용 (JSP, 타임리프..)하여 HTML 문서를 분리하고 필요 부분만 '자바 코드'를 써서 동적으로 변경하자



2. JSP

1. JSP 적용 방법

build.gradle에 라이브러리 추가

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

2. JSP 코드 일부

회원 등록 폼 JSP

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
 
<title>Title</title>
</head>
<body>
<form action="/jsp/members/save.jsp" method="post">
 username: <input type="text" name="username" />
 age: <input type="text" name="age" />
 
<button type="submit">전송</button>
</form>
</body>
</html>
  • <%@ page contentType="text/html;charset=UTF-8" language="java" %>
    -> 첫 줄은 JSP문서라는 뜻
  • JSP는 첫 줄을 제외하곤 거의 HTML이다.
  • JSP는 서버 내부에서 서블릿으로 변환 됨

회원저장 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과 '자바코드'가 섞여서 지저분 해짐.
  • JSP를 사용하며 '뷰'를 분리하긴 했음
    -> 그럼에도 코드의 상단은 '회원 저장을 위한 비즈니스 로직' 포함
    -> 코드의 하위는 '뷰' 영역
    => 리포지토리부터 다양한 코드가 JSP에 노출.. 너무 많은 역할을 맡음
    => 유지보수 어떻게 할까...
  • 결론: 비즈니스 로직과 뷰 단을 확실히 나눠서 각자의 일에 집중하도록 해보자 답: MVC 패턴



3. MVC 패턴

🤔 등장의 이유

1. JSP가 너무 많은 역할을 가짐
• 비즈니스 로직부터 시작해 '뷰 렌더링'까지...
=> 유지보수의 어려움
2. 변경의 라이프 사이클
• 비즈니스 로직과 UI 등 서로에게 영향을 주지 않아야 함.
-> 변경의 라이플 사이클이 다른 둘을 '하나의 코드'로 관리한다
=> 좋지 않다 💦
3. 기능 특화의 필요
• JSP 처럼 '뷰 템플릿'은 화면 렌더링에 최적화 되어 있다.
-> 해당 본업만 시키자

이 곳 MVC패턴에서의 MVC의 각각 쓰임

  • 컨트롤러
    - HTTP 요청을 받아서 파라미터를 검증
    • 비즈니스 로직을 호출한다. 그리고 뷰에 전달할 결과 데이터를 조회해서 모델에 담아줌
      => 원래라면 비즈니스 로직을 담는 '서비스'라는 계층이 따로 있음.
  • 모델
    - 뷰에 출력할 데이터를 담아둔다.
    -> 뷰는 비즈니스 로직이나 데이터 접근을 몰라도 됨.
    -> 화면을 렌더링 하는 일에 집중할 수 있다.

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

코드

컨트롤러 _ 회원 등록 폼

@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);
 
}
}
  • /WEB-INF
    - 이 경로에 있는 파일들은 '외부'에서 직접 호출이 불가하여 '컨트롤러'를 통해 호출이 가능
  • dispatcher.forward()
    - 서부 내부에서 일어나는 호출(다른 서블릿이나 JSP)
    - redirect 의 경우 실제 '웹 브라우저(클라이언트)'에 요청 응답 등의 반복으로 호출된다는 것에서 둘의 차이가 있다.

회원 등록 폼 - 뷰

<%@ 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>

상대경로 사용
그렇게 되면 폼 전송시, "현재 url속한 계층 경로" + save가 호출
/servlet-mvc/members/save



MVC 컨트롤러 단점

1. 포워드 중복

-> View로 이동하는 코드가 항상 중복 호출 (메서드 또한..)

  1. ViewPath에 중복
    ->/WEB-INF/views/
    - 만일 다른 '템플릿'으로 변경해야 되면 대공사..
    - 조회: request.getAttribute(name)

  2. HttpServletResponse, HttpServleRequest
    - 별로 사용하지 않고 쓰일 때만 쓰이는 경우 발생.

  3. 정리하면 공통 처리가 어렵다는 문제
    • 기능이 많을 수록 '공통 처리 부분' 증가
    • 매번 쓰기에 호출한다고 해도 문제는 달라지지 x
      => '프론트 컨트롤러' 패턴 도입
      => 이걸 왜? 컨트롤러 호출 전 '공통 기능' 처리를 위해 (애초에 입구를 1개로 고정)

profile
초짜 백엔드 개린이

0개의 댓글