수업내용/Spring

[2022.12.21.수] 모델2(MVC 패턴)

주니어주니 2022. 12. 21. 15:53

 

 

* 모델 1 

 

 

 

* 모델 2 

 

 

 

 

 

 

* MVC 패턴을 적용한 웹 애플리케이션 개발 방식 

 

Model : 업무로직을 수행해서 뷰에 표현할 데이터 제공, 뷰에서 표현하는 데이터 그 자체 

View : Model 에서 제공하는 데이터 표현 (HTML태그 - 디자인코드)

Controller : 클라이언트의 요청 처리 

 

 

 

 

* URL : 자원의 위치 (자원이 실제로 물리적으로 존재해야 함) 

* URI : 자원에 대한 식별자 -> 요청을 식별하기 위해 임의로 붙인 것 (물리적으로 존재하지 않아도 됨)

            (이런 경로로 보내면 ~~를 줄게)

            - 쿼리스트링은 요청 URI에 포함되지 않음 (그 앞까지만 요청을 보냄)

 

 

 

 

1. HTTP 요청접수, 컨트롤러 실행 

 

 

요청 URI 에 따라                  컨트롤러 실행

/model2/home.hta                 HomeController의 execute(request, response) 실행

/model2/loginform.hta           LoginFormController의 execute(request, response) 실행

/model2/registerform.hta       RegisterFormController의 execute(request, response) 실행

 

 

 

 

 

 

 

 

FrontController 안에 controllerMap이 있고, 이 맵 안에 key, value가 있음

key : "/home.hta"      value : HomeController

이 HomeController, RegisterFormController, LoginFormController 들은 모두 Controller 인터페이스를 구현

-> 인터페이스 안에는 전부 execute라는 메소드를 가지고 있음

-> 각각 이 메소드를 구현 (재정의)

 

ControllerMap의 타입은 Map<String, Controller> 

각 hta 요청을 보내면 -> 그거에 해당하는 컨트롤러가 controller에 저장됨

-> controller.execute를 실행하면 각 컨트롤러에서 재정의된 execute가 실행됨 

-> 경로가 String path 에 담김 

 

hta 요청할 때 톰캣이 요청객제, 응답객체 (request, response)를 생성해서 FrontController에 전달 

-> service 메소드를 호출할때 FrontController의 request, response에 대입

-> 각 Controller에도 대입 

-> jsp가 아니어도 Controller에서 모든 요청정보를 뽑을 수 있는 것 

=> 원래 jsp에서 요청처리를 했는데, 앞으로는 Java 클래스(Controller)에서 HTTP 요청 처리 (요청객체, 응답객체 꺼내기) 

 

 

 

2. HTTP 요청처리 후 JSP로 이동

 

 

* 모델1에서는 서블릿(Front Controller), 자바클래스(Controller), JSP(View)가 할일을 JSP가 다 했는데,

모델2에서는 이걸 다 쪼갬

서블릿은 프레임워크에서 제공 -> 자바클래스(Controller)만 하나 늘어난 것

 

* 서블릿(Front Controller)에서 HTTP 요청을 접수, 컨트롤러 실행

-> 자바클래스(Controller)에서 HTTP 요청 처리 후, 서블릿(Front Controller)로 jsp/ 재요청 URL을 보냄 

-> 서블릿(Front Controller)에서 실행이 아니라, 내부이동을 통해 JSP로 요청을 보냄

    (Request Dispatcher객체가 하는일) 

    (requestDispatcher의 메소드 중 include - 다른 jsp 실행하고 다시 되돌아옴

                                                       forward - 그 jsp로 영원히 보내는 것) 

 

 

 

 

 

3. 자바클래스(Controller)에서 JSP(view)로 데이터 전달 

 

 

 

  •  요청객체(HttpServletRequest)에는 요청파라미터, 속성을 담을 수 있음
    • 요청파라미터
      • 쿼리스트링, 폼입력값으로만 저장, 꺼내기만 할 수 있음 
      • name : String
        value : String 
    • 속성
      • 아무때나 값 저장, 꺼낼 수 있음 
      • name : String
        value : Object
      • 데이터를 전달하는 버스 역할
  • 흐름
    • 1. home.hta 요청 
    • 2. 요청객체, 응답객체가 FrontController로 전달
    • 3. FrontController에서 컨트롤러 실행할 때 request와 response를 전달
      -> controller의 execute 메소드 실행 
    • 4. HomeController의 execute 메소드로 request, response 전달
      -> 반환값으로 home.jsp 반환
    • 5. HomeController에서 요청객체의 속성에 값 입력 (request.setAttribute("", );) 
      (요청파라미터는 쿼리스트링, 폼입력값으로만 전달 -> 꺼내기만 할 수 있음
       속성은 아무때나 값 넣고 꺼낼 수 있음!! ) 
      (서블릿 -> Controller -> 서블릿 -> jsp 로 값이 이동하니까 Controller에서 요청객체에 값을 넣어줌)
    • 6. FrontController에서 반환값으로 viewName 을 얻게 됨
      -> reuestDispatcher를 통해 뷰페이지(home.jsp)로 이동
      -> 이동하면서 request 를 전달
    • 7. home.jsp로 request, response가 전달


      ==> 요청객체를 FrontController, HomeController, home.jsp에서 모두 공유하고 있음 

    • 속성에 저장된 값을 jsp에서 꺼내는 법 (EL)
      "message"라는 이름으로 "안녕하세요"를 담아놓은 경우
      ${message }

 

 

 

 

 

 

* home.hta 에서는 값이 잘 나왔는데, home.jsp 에서는 값이 안나옴

 

: hta - 요청 -> FrontController -> Controller -> jsp 로 이동

           -> Controller에서 속성에 값을 담은 것이 그대로 나옴

 

  jsp - 요청 -> jsp 로 바로 이동

           -> Controller를 거치지 않기 때문에 값이 안나옴 

 

=====> jsp로 바로 요청하지 않음!!!

 

-> 직접 jsp를 호출하는 것을 방지하는 방법

: 모든 jsp 파일을 WEB-INF 아래에 둠 (WEB-INF는 보호되는 것이라서) 

 

 

 

 

 

* FrontController

 

package com.sample.model2;

import java.io.IOException;
import java.util.HashMap;
import java.util.Map;

import com.sample.controllers.HomeController;
import com.sample.controllers.LoginFormController;
import com.sample.controllers.RegisterFormController;

import jakarta.servlet.RequestDispatcher;
import jakarta.servlet.ServletConfig;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;

public class FrontController extends HttpServlet {

	private Map<String, Controller> controllerMap = new HashMap<>();
	
	/**
	 * 서블릿의 초기화 메소드다. <br/>
	 * 서블릿 객체가 생성되면 톰캣이 딱 한번 호출해서 서블릿을 초기화시킨다.
	 */
	@Override
	public void init(ServletConfig config) throws ServletException {
		controllerMap.put("/home.hta", new HomeController());					// "/home.hta"를 입력하면 homecontroller()실행
		controllerMap.put("/register-form.hta", new RegisterFormController());	// "/register-form"을 입력하면 registerFormcontroller()실행
		controllerMap.put("/login-form.hta", new LoginFormController());		// "/login-form"을 입력하면 loginformcontroller()실행
	}
	
	@Override
	protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		System.out.println("### FrontController의 service(request, response) 메소드 실행.");
		
		///////////////////////////////////////////////////////////////////////////
		// 요청 URI 분석하기
		///////////////////////////////////////////////////////////////////////////
		
		// Context Path(웹애플리케이션을 구분하는 고유한 경로다. 보통은 프로젝트명과 동일하다.) 조회하기
		String contextPath = request.getContextPath();		// 	/model2
		
		// 요청 URI 조회하기
		String requestURI = request.getRequestURI();
		requestURI = requestURI.replace(contextPath, "");	// 반복되는 /model2를 ""로 대체
		System.out.println("### 요청 URI: " + requestURI);	// /posts/detail.hta
		
		///////////////////////////////////////////////////////////////////////////
		// 요청 URI에 맞는 컨트롤러 실행하기
		///////////////////////////////////////////////////////////////////////////
		try {
			// HashMap객체에 저장된 Controller 인터페이스 구현객체 꺼내기 
			Controller controller = controllerMap.get(requestURI);
			// 조회된 컨트롤러 객체의 execute(request, response) 
			String viewName = controller.execute(request, response); 
			
			// 지정된 뷰페이지(viewName)으로 클라이언트의 요청을 이동시킨다. (=JSP를 실행시키는 것) 
			// 앞에 계속 붙는 "/WEB-INF/views/"를 아예 여기서 viewName 앞에 붙여줌
			viewName = "/WEB-INF/views/" + viewName;
			RequestDispatcher requestDispatcher = request.getRequestDispatcher(viewName);
			requestDispatcher.forward(request, response);
		} catch (Exception e) {
			throw new ServletException(e); 
		}
		
	}
}

 

 

 

* HomeController

 

package com.sample.controllers;

import com.sample.model2.Controller;

import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;

public class HomeController implements Controller{
	
	@Override
	public String execute(HttpServletRequest request, HttpServletResponse response) throws Exception {
		System.out.println("### HomeController의 execute(request, response) 실행");
		
		// view에 데이터 전달하기 
		request.setAttribute("message",	"안녕하세요");
		
		// 요청처리가 완료되면, 데이터를 표현할 뷰페이지 이름을 반환한다. 
		return "home.jsp";
	}

}

 

 

* home.jsp 

 

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" trimDirectiveWhitespaces="true"%>
<!DOCTYPE html>
<html lang="ko">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.2.2/dist/css/bootstrap.min.css" rel="stylesheet" >
<title>웹 애플리케이션</title>
</head>
<body>
<div class="container mb-3">
   <div class="row mb-3">
   		<div class="col-12">
   			<h1>홈</h1>
   		</div>
   </div>
   <div class="row mb-3">
   		<div class="col-12">
   			<p>${message }</p>
   		</div>
   </div>
</div>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.2.2/dist/js/bootstrap.bundle.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.6.1/jquery.min.js"></script>
</body>
</html>

 

 

 

 

 

 


 

 

 

JSP의 속성과 스코프 

 

속성 

 

1)

public class User {

        // 멤버변수, 인스턴스변수, 필드, 프로퍼티, 속성 

        private String name;

}

 

2) 

<a href="home.jsp" class="btn btn-primary" />

href, class : 속성

 

3) 

PageContext

HttpServletReuest

HttpSession

ServletContext

위의 4가지 객체는 속성(데이터, 객체)을 저장할 수 있다. 

- 위 객체는 유지 시간과 범위가 다름

 

 

  • 속성은 객체(값)다.
  • 속성은 뷰에 표현할 정보다.
  • JSP는 속성(값, 객체)을 저장할 수 있는 4종류의 객체를 제공한다.

 

객체 범위 속성 범위, 유지시간
PageContext 페이지 객체 해당 jsp페이지에서만 꺼낼 수 있음
HttpServletRequest 요청 객체 내부이동한 다른 jsp에서도 꺼낼 수 있음
응답이 끝나면 사라짐 
HttpSession 세션 객체 해당 클라이언트만 꺼낼 수 있음
모든 jsp에서 꺼낼 수 있음
로그아웃하면 사라짐
ServletContext 애플리케이션 객체 모든 사용자, 모든 jsp에서 꺼낼 수 있음
톰캣이 꺼지면 사라짐

 

  • 속성관련 API
    • 속성의 저장 : void setAttribute(String name, Object value)
    • 속성의 조회 : Object getAttribute(String name)
    • 속성의 삭제 : void removeAttribute(String name)

 


 

스코프

  • 속성(값, 객체)를 공유할 수 있는 유효범위를 말한다.
    • 속성(값, 객체)을 위의 4 종류 객체 중 어디에 저장하느냐에 따라서 저장된 속성(값, 객체)에 대한 이용범위 혹은 생존시간이 달라진다.
    • 이것을 스코프라고 한다.
      • 스코프의 종류
        • Page Scope
          • PageContext에 저장되는 속성이 가지는 유효범위다.
          • 같은 JSP내에서만 속성을 공유할 수 있다.
        • Request Scope
          • HttpServletRequest에 저장되는 속성이 가지는 유효범위다.
          • 클라이언트의 요청을 처리하는 동안 속성을 공유할 수 있다.
          • foward, include 방식을 사용하는 경우 여러 jsp 페이지에서 요청객체에 저장된 속성을 공유할 수 있다.
          • Model2개발방식에서는 Model이 획득한 데이터를 View에 전달할 때 주로 사용되는 유효범위다.
          • 클라이언트의 요청 처리가 완료되면(응답이 완료되면) 요청객체가 소멸되기 때문에 저장된 속성도 사라진다.
          • 속성에 대한 관리가 필요없다.(메모리 문제를 신경쓸 필요없다.)
        • Session Scope
          • HttpSession에 저장되는 속성이 가지는 유효범위다.
          • 세션객체가 유지되는 동안은 세션객체에 저장된 속성(값, 객체)은 서로 다른 JSP 페이지에서도 공유할 수 있다.
          • 세션객체가 만료되는 순간(로그아웃, 브라우저를 닫는 경우) 저장된 속성도 사라진다.
          • 세션객체는 브라우저마다(클라이언트)마다 하나씩 생성되는 객체이기 때문에, 세션에 속성(값, 객체)으로 저장되는 것은 해당 클라이언트만 사용할 수 있다.
          • 세션객체 속성으로 저장되는 것들은 "개인적인 정보"만 저장된다.
        • Application Scope
          • ServletContext에 저장되는 속성이 가지는 유효범위다.
          • 웹서버가 실행되는 동안(웹 애플리케이션이 실행되는 동안) ServletContext객체에 저장된 속성(값, 객체)은 모든 JSP, 모든 서블릿, 모든 필터, 모든 사용자가 공유할 수 있다.
  • 속성과 스코프의 관계