728x90

간단한 기능을 하는 회원 관리 웹 애플리케이션을 만들어봤다.

회원 정보로는 이름과 나이를 가지고 저장하고 조회할 수 있는 기능을 한다.

그래서 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 패턴

필기하면서 들은거라 그림이 좀 지저분하다.

위 그림에서 말하는  

비즈니스 로직 = 회원을 저장하고, 주문 로직 => 즉, 서비스나 리포지토리에 저장한다고 볼 수 있다.

컨트롤러 로직 => 파라미터를 꺼내고 고객이 제대로 요청한건지, 맞는지 확인, 잘못된건 오류내어 튕겨줌 => 만약 맞다면 서비스나 리포지토리을 호출해서 그 안에 있는 주문하거나 하는 로직을 실행한다.

 

 


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의 핵심도 프론트 컨트롤러에 있다*

 

728x90

+ Recent posts