* 모델 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, 모든 서블릿, 모든 필터, 모든 사용자가 공유할 수 있다.
- Page Scope
- 스코프의 종류
- 속성과 스코프의 관계
'수업내용 > Spring' 카테고리의 다른 글
[2022.12.28.수] model2로 할 일(Todo) 실습 (0) | 2022.12.29 |
---|---|
[2022.12.27.화] model2로 댓글(comment) 실습 (0) | 2022.12.29 |
[2022.12.26.월] model2로 사용자(user), 게시물(post) 실습 (0) | 2022.12.29 |
[2022.12.23.금] EL, JSTL (2) (0) | 2022.12.29 |
[2022.12.22.목] EL, JSTL (0) | 2022.12.29 |