간단한 기능을 하는 회원 관리 웹 애플리케이션을 만들어봤다.
회원 정보로는 이름과 나이를 가지고 저장하고 조회할 수 있는 기능을 한다.
그래서 Member라는 클래스 안에 id, name, age 를 필드로 가지게 했고 MemberRepository를 만들어서 저장,조회 기능을 가지게 했다.
(싱글톤으로 만드는데 spring의 기능을 사용하고 순수 서블릿 만으로 구현하기 때문에 private 생성자로 막아놓고 get메서드로 memberRepository를 가질 수 있게 했다.)
servlet 의 불편한 점 (응답 메시지 작성)
-reponse.setContentType("text/html")
-reponse.setCharacterEncoding("utf-8")
-getParameter 로 가져와서 하는것은 정말 편리하다.
-하지만 자바 코드로 html 문서를 작성 하는것이 너무 어렵다.
-서블릿과 자바 코드만으로 HTML을 만들었는데, 진짜 한눈에 봐도 비효율적이라는 것을 알 수 있다.
>>템플릿 엔진 사용. 위에서 방식은 자바코드를 html로 만든것이지만 템플릿 엔진은 html에 자바 코드를 넣는것
>>템플릿 엔진 종류(JSP , Thymeleaf , Freemarker , Velocity) JSP는 거의 사용 안 하는 추세이다.
JSP 사용해보기
-build.gradle에 jsp 라이브러리 추가를 해야한다.
-<%@ page contentType="text/html;charset=UTF-8" language="java" %>
>> jsp문서라는 뜻을 알려주는 코드
>>jsp 코드는 html과 비슷한데 jsp는 서버 내부에서 서블릿으로 변환되서 보여준다.
-<%~~%>
>> 자바 코드를 입력할 수 있다.
-<%=~~%>
>>자바 코드를 출력할 수 있다.
*jsp 코드를 보면 서블릿 코드와 비슷한데 html을 중심으로 하고, 자바 코드를 부분부분 입력했다 (<% ~ %>) 를 사용해서 자바 코드를 넣어줄 수 있다*
jsp를 사용해서 html안에 자바코드를 넣어서 동적으로 하기는 했지만 jsp에 회원을 저장하는 비즈니스 로직도 있고 화면을 보여주는 html코드도 있다.
>> 하지만 이는 너무 많은 역할을 담당하고 있다. 비즈니스 로직은 비즈니스 로직만 처리하고 html로 화면을 그리는것은 화면만 그리는데에 집중 할 수 있게 해야한다.
>>그래서 MVC패턴을 사용한다.
MVC (Model View Controller)
Controller
>> HTTP 요청을 받아서 파라미터를 검증하고, 비즈니스 로직을 실행, 그리고 뷰에 전달할 결과를 데이터를 조회해서 모델에 담는다. (Servlet)
Model
>> 뷰에 출력할 데이터를 담아두고, 뷰가 필요한 데이터를 모두 모델에 담아서 전달해주는 덕분에 뷰는 화면을 랜더링 하는 일에만 집중 한다.
View
>> 모델에 담겨있는 데이터를 사용해서 화면을 그린다. (Jsp)
* Controller에 비즈니스 로직까지 둘 수 있지만 또 이렇게 되면 컨트롤러가 너무 많은 역할을 담당하기 때문에 비즈니스 로직, 데이터 접근은 Service라는 계층을 별도로 만들어서 처리한다. 말 그대로 컨트롤을 해주는 부분이라고 할 수 있다. 그리고 컨트롤러는 비즈니스 로직이 있는 서비스를 호출하는 역할을 담당한다.*
* Model은 httpServletRequest 객체를 사용하는데 request 내부에 데이터 저장소를 가지는데 request.setAttribute() , request.getAttribute()을 사용해서 보관, 조회를 한다.
필기하면서 들은거라 그림이 좀 지저분하다.
위 그림에서 말하는
비즈니스 로직 = 회원을 저장하고, 주문 로직 => 즉, 서비스나 리포지토리에 저장한다고 볼 수 있다.
컨트롤러 로직 => 파라미터를 꺼내고 고객이 제대로 요청한건지, 맞는지 확인, 잘못된건 오류내어 튕겨줌 => 만약 맞다면 서비스나 리포지토리을 호출해서 그 안에 있는 주문하거나 하는 로직을 실행한다.
MVC 활용(회원 등록 폼 컨트롤러 / 뷰)
서블릿을 컨트롤러로 사용하고, JSP를 뷰로 사용해서 MVC 패턴을 적용해보자.
-Model은 HttpServletRequest 객체를 사용한다.
-request는 내부에 데이터 저장소를 가지고 있는데, request.setAttribute() , request.getAttribute() 를 사용하면 데이터를 보관하고, 조회할 수 있다.
-회원 등록 폼 컨트롤러에서는 viewPath = "/WEB-INF/..." 로 jsp 경로를 넣어준다.
ex) String viewPath = "/WEB-INF/views/new-form.jsp";
-request.getRequestDispathcher(viewPath)
-dispatcher.forward()
>>다른 서블릿이나 JSP로 이동할 수 있게 해준다 (서버 내부에서 다시 호출이 발생)
*/WEB-INF 로 경로를 잡으면 이 경로안에 JSP가 있으면 외부에서 직접 JSP를 호출 할 수 없다 (url에 경로대로)
>> 항상 컨트롤러를 통해서 JSP를 호출 해야한다*
>> redirect vs forward
> 리다이렉트는 실제 클라이언트(웹 브라우저)에 응답이 나갔다가, 클라이언트가 redirect 경로로 다시 요청한다. 따라서 클라이언트가 인지할 수 있고, URL 경로도 실제로 변경된다.
> 반면에 포워드는 서버 내부에서 일어나는 호출이기 때문에 클라이언트가 전혀 인지하지 못한다.
-회원 등록 폼 뷰에서는 <form action="save" method="podt"> 와 같이 form의 action을 보면 상대경로( / 로 시작X)인 것을 확인할 수 있다.
-이렇게 상대경로를 사용하면 폼 전송시 현재 URL이 속한 계층 경로 + save가 호출된다.
-현재 계층 경로: /servlet-mvc/members/
-결과: /servlet-mvc/members/save
MVC 활용 (회원 저장 컨트롤러 / 뷰, 회원 목록 조회 컨트롤러 / 뷰)
@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 {
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에 데이터를 보관한다.
request.setAttribute("member", member);
String viewPath = "/WEB-INF/views/save-result.jsp";
RequestDispatcher dispatcher = request.getRequestDispatcher(viewPath);
dispatcher.forward(request, response);
}
}
-회원 저장 컨트롤러에서는 MvcMemberSaveSerlet 클래스를 만들어서 정보들을 담아서 뷰에 다시 넘겨줘야한다.
-.getParameter("age") ...를 통해 username , age 를 알아내고 new member(username,age) 로 객체를 생성한다.
-request.setAttribute("member",member); key = "member" 이고 위에서 만든 member 객체를 넘겨주는 것이다. (뷰로)
-그때 위에서 한 것 처럼 "/WEB-INF/.../save-result.jsp" 경로를 저장해두고
-.getRequestDispatcher(path) 로 넘겨주고
-.forward(requet,response)로 뷰에 넘겨주는 것이다.
-회원 저장 뷰에서는 .getAttribute()로 데이터를 꺼낼 수 있지만, JSP에서 ${} 문법을 제공해서 더 쉽게 데이터를 가져올 수 있다.
- id=${member.id}
- username=${member.username}
- age=${member.age}
-회원 목록 조회 컨트롤러에서도 똑같이 위에 방법과 비슷하고 memberRepository.findAll() 을 사용해서 List를 넘겨줬다.
-회원 목록 조회 뷰에서는 <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%> 를 선해서 c태그를 이용해 request에 담긴 리스트 데이터를 쉽게 꺼내서 출력할 수 있다. members 리스트에서 member 를 순서대로 꺼내서 item 변수에 담고, 출력하는 과정을 반복한다
-MVC패턴을 사용하니까 컨트롤러는 컨트롤러 뷰는 화면만 랜더링하는 역할을 확실하게 구분하는것이 보였다.
한계점
-getRequestDispather 이나 .forward 처럼 컨트롤러에서 중복되는 코드가 많았다.
-HttpServletRequest , HttpServletResponse 등 사용하지 않는 객체들도 있고 이런 것들은 테스트 케이스를 작성하기도 어렵다.
-위에서 중복되는 코드를 메서드로 뽑아서 하면 될 것 같지만 어차피 메서드도 항상 호출을 해야한다. 만약 실수로 호출하지 않으면 문제가 또 발생한다.
>>이러한 한계점을 해결하기 위해서는 컨트롤러가 호출되기 전에 공통 기능을 처리 해야하는데 이러한 기능을 하는 것이 프론트 컨트롤러이다.
>>프론트 컨트롤러(Front Controller) 패턴이 먼저 실행되서 공통 기능을 수행하고 다른 컨트롤러로 연결해준다. (입구를 하나로 만드는 것)
*스프링 MVC의 핵심도 프론트 컨트롤러에 있다*
'Spring' 카테고리의 다른 글
서블릿(Servlet)이란? (JSP(Java Server Page)) (0) | 2023.03.02 |
---|---|
Spring MVC 4 - MVC 프레임워크 만들기 (0) | 2023.02.27 |
Spring MVC - 2 (서블릿) (0) | 2023.02.27 |
Spring MVC 1 - 웹 서버와 웹 애플리케이션 서버(WAS) (0) | 2023.02.27 |
@RequestMapping 애너테이션 (0) | 2023.02.23 |