월루를 꿈꾸는 대학생
[MVC-1] 서블릿 , JSP , MVC패턴 본문
회원관리 웹 애필리케이션 요구사항
그냥 회원정보에 이름과 나이를 넣고 저장 및 조회하는 기능을 구현한다!
지금은 그냥 자바로만!
스프링없으니까 싱글톤 보장도 안 되니까 싱글톤도 알아서 만든다
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
- 컨트롤러
- http 요청의 파라미터 검증
- 비즈니스 로직 실행 -> 코드가 있는게 아니라 실행이다 로직은 서비스에 저장됨 컨트롤러는 단순히 호출만!
- 결과 데이터를 모델에 담아서 뷰에 전달
- 모델
- 뷰에 출력할 데이터를 담아두는 곳
- 뷰
- 모델에 담겨 있는 데이터를 활용해서 화면에 집중
- 모델 덕분에 따로 비즈니스 로직없이 해당 값들로 화면 그리기만 하면 되니까 굳굳
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 패턴을 통해서 비즈니스 로직과 뷰를 나눌 수 있어 편해짐
- 컨트롤러 부분에서 코드의 중복이 많고 필요하지 않은 코드도 있음
- 포워드 중복
- view로 이동할 때 항상 코드 호출이 필요
String viewPath = "/WEB-INF/views/new-form.jsp"; RequestDispatcher dispatcher = request.getRequestDispatcher(viewPath); dispatcher.forward(request, response);
- view로 이동할 때 항상 코드 호출이 필요
2.사용하지 않는 코드
- request , response는 사용할 때도 있지만 response경우 안 할때도 많음
HttpServletRequest request, HttpServletResponse response
- 공통처리가 어려움
- 컨트롤러에서 처리할 공통적인 부분이 있을텐데 해당 부분을 메서드로 뽑아도 계속 메서드를 호출해야하고 호출하지 않아도 문제가 될 수 있으니 ... 호출자체도 중복..
즉 이런 문제를 해결하기 위해서 서블릿이 호출 되기 전 공통처리를 먼저 처리해야함
그런 수문장역할이 필요 = 프론트 컨트롤러 패턴 (스프링 mvc의 핵심 )
'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 |