-
학원 day77. JSP의 속성과 스코프, EL, JSTL기록 2022. 12. 22. 18:05
톰캣 내부
자바웹에서는 속성을 저장할 수 있는 곳이 4군데가 있다.
HttpServletRequest는요청이 오면 만들어지고 응답가면 사라지는 일회성 객체이다.
PageContext는 home.jsp에서만 쓸 수 있다. 메소드 안에서만 사용할 수 있다.
HttpSession은 특정 클라이언트와 특정 브라우저와 연결된 객체
ServletContext는 웹어플리케이션 당 하나씩 만들어지는 객체
forward는 전달받은 request객체를 다른 jsp나 서블릿으로 이동시켜준다.
jsp 안에서는 자신이 사용할 수 있는 객체를 미리 획득하고 있다.
서블릿(FrontController)은 요청접수, 컨트롤러 실행, 내부이동 3가지를 한다.
Controller는 요청처리, 업무로직 메소드 호출, JSP에서 표현할 데이터 저장, 뷰페이지 반환
JSP는 데이터 표현을 한다.
데이터는 request.setAttribute("message", "안녕");으로 메소드 실행할 때 request객체 속성에 담긴다.
데이터표현
<p>${message} </p>
message라는 속성명으로 저장된 속성값을 표현한다.
* 왜 하필 request객체에 데이터를 저장하는가?
- 요청객체를 다른 jsp로 전달가능하다.
- 요청객체는 응답이 완료되면 폐기된다.
- 메모리 부담이 적다.
- 모델 2방식에서는 컨트롤러가 요청객체를 이용해서 뷰에게 데이터를 전달한다.
스코프가 다르다는 건 저장된 속성을 공유할 수 있는 유효범위가 각각 다른 것을 의미한다.
PageContext 스코프 < Request 스코프 < Session 스코프 < ServletContext인 어플리케이션 스코프 순으로 유효범위가 넓어진다.
객체 범위 속성 PageContext 페이지 객체 HttpServletRequest 요청 객체 HttpSession 세션 객체 ServletContext 애플리케이션 객체 속성은 HttpServletRequest와 PageContext는 금방 사라지는 곳이니까 속성을 막 담아도 된다.
다른 곳에는 협의를 통해서 결정해야 한다.
+)
질문 : 세션객체에서 로그아웃하면 세션객체가 사라지니까 사용자정보도 사라지고, 로그인하기 전까지는 세션객체도 존재하지 않고 사용자정보도 없는게 맞나요?
답변 : 세션객체는 생성시점을 컨트롤하는게 가능하다. 디폴트는 JSP에 접속만해도 세션객체가 만들어진다.
session = "false"라고 하면 jsp에서 자동으로 세션을 생성하지 않는다.
지금 우리 개발 환경에서는 로그인하기 전이어도 세션객체 자체는 존재하고, 사용자정보는 들어있지 않다.
요청파라미터는 2가지 방식(쿼리스트링, 폼 입력값)으로만 저장가능하다.
* 속성 관련 주요 API (객체에 상관없이 속성관련 API는 똑같다. )
void setAttribute(String name, Object value) : 지정된 이름으로 속성을 저장한다.
Object getAttribute(String name) : 지정된 이름의 속성을 조회한다.
void removeAttribute(String name) : 지정된 이름의 속성을 제거한다.
* 요청파라미터 관련 주요 API
String getParameter(String name) : 지정된 이름으로 요청파라미터값을 조회한다.
String[] getParameterValues(String name) : 지정된 이름으로 여러개의 요청파라미터값을 조회한다.
* 초기화파라미터 관련 주요 API
String getInitParameter(String name) : 지정된 이름으로 초기화파라미터값을 조회한다.
EL (Expression Language)
내장객체사용예설명param${param.파라미터명}요청파라미터값을 조회할 수 있다.request.getParameter("파라미터명")와 동일하다.paramValues${paramValues.파라미터명}요청파라미터값을 조회할 수 있다.
동일한 매개변수 이름에 대한 여러 값을 가져오기 위해 사용된다.request.getParameterValues("파라미터명")와 동일한다.pageScope${pageScope.속성명}PageContext에 저장된 속성(값, 객체)을 조회한다.requestScope${requestScope.속성명}HttpServletRequest에 저장된 속성을 조회한다.sessionScope${sessionScope.속성명}HttpSession에 저장된 속성을 조회한다.applicationScope${applicationScope.속성명}ServletContext에 저장된 속성을 조회한다.initParam${initParam.초기화파라미터명}초기화파라미터값을 조회한다.header${header.요청헤더명}요청헤더정보에서 헤더명에 해당하는 값을 조회한다.cookie${cookie.쿠키명}쿠키명에 해당하는 쿠키값을 조회한다.pageContext${pageContext.getter메소드이름}PageContext의 getXXX()메소드의 실행결과를 조회한다.JSP의 기본객체를 제공받을 수 있다.JSP에서는 JSP의 요청파라미터, 속성, 스코프, 요청헤더정보, 초기화파라미터 정보에 접근할 수 있도록 해당 값을 제공하는 EL내장객체를 제공한다.
EL의 내장객체는 내부적으로 Map객체이다.
요청파라미터는 param을 적으면 가져올 수 있다.
속성 이외에는 param 등 EL내장객체 이름을 적어야 한다.
안적으면 무조건 속성을 찾는 거다. (page->request->session->servletContext순으로 같은 name으로 저장되어 있는 것을 찾는다.) 찾았는데도 값이 없으면 null이 출력되는게 아니라 공백(빈문자열)이 출력된다.
* ServletContext의 EL 내장객체 이름은 contextParam이 아닌 InitParam이다.
( InitParam인 이유는 꺼내는 메소드이름이 InitParam이라서 그렇다! )
+)
질문 : 톰캣에 ServletConfig객체는 없나요?
답변 : 존재한다. 하지만 잘 쓰이지 않고 사용하는 EL이 없기 때문에 생략했다.
연산자
empty 연산자가 true를 반환하는 경우
- 값이 null인 경우
- "" 빈문자열인 경우
- 배열이나 콜렉션의 길이가 0인 경우
- 비어있는 맵인 경우
web.xml <?xml version="1.0" encoding="UTF-8"?> <web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="https://jakarta.ee/xml/ns/jakartaee" xmlns:web="http://xmlns.jcp.org/xml/ns/javaee" xsi:schemaLocation="https://jakarta.ee/xml/ns/jakartaee https://jakarta.ee/xml/ns/jakartaee/web-app_5_0.xsd http://xmlns.jcp.org/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" id="WebApp_ID" version="5.0"> <!-- FrontController 서블릿을 웹서버(톰캣)에 배포하기 - FrontController 서블릿은 *.hta 형식의 HTTP 요청이 접수되면 실행되도록 배포한다. --> <servlet> <servlet-name>fc</servlet-name> <servlet-class>com.sample.model2.FrontController</servlet-class> </servlet> <servlet-mapping> <servlet-name>fc</servlet-name> <url-pattern>*.hta</url-pattern> </servlet-mapping> </web-app>
FrontController.java package com.sample.model2; import java.io.IOException; import java.util.HashMap; import java.util.Map; import com.sample.controllers.CoreController; import com.sample.controllers.FmtController; import com.sample.controllers.HomeController; import com.sample.controllers.ListController; 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/> * 서블릿 객체가 생성되면 톰캣이 딱 한번 호출해서 서블릿을 초기화시킨다<br/> * 초기화는 필요한 객체를 미리 만들어두는것, 멤버변수에 값을 저장하는 것이 초기화에 해당한다. * FrontController를 사용할 준비를 하는 것이다. 클라이언트가 요청이 올 때마다 맞는 Controller를 찾아서 실행시켜야 하는데 그 Controller를 미리 만들어놓는 것이다. */ public void init(ServletConfig config) throws ServletException { controllerMap.put("/home.hta", new HomeController()); controllerMap.put("/register-form.hta", new RegisterFormController()); controllerMap.put("/login-form.hta", new LoginFormController()); controllerMap.put("/list.hta", new ListController()); controllerMap.put("/core.hta", new CoreController()); controllerMap.put("/fmt.hta", new FmtController()); } // 서비스라는 메소드는 요청이 올 때마다 실행된다. @Override protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { System.out.println("### FrontController의 service(request, response) 메소드 실행."); ///////////////////////////////////////////////////////////////////////////// // 요청 URI 분석하기 ///////////////////////////////////////////////////////////////////////////// // ContextPath(웹애플리케이션을 구분하는 고유한 경로다. 보통은 프로젝트명과 동일하다.) 조회하기 String contextPath = request.getContextPath(); // 요청 URI 조회하기 String requestURI = request.getRequestURI(); // contextPath 지우고 조회하기 (model2는 똑같으니까) requestURI = requestURI.replace(contextPath, ""); System.out.println("### 요청 URI: " + requestURI); ///////////////////////////////////////////////////////////////////////////// // 컨트롤러 실행하기 (요청URI에 맞는 특정한 자바클래스가 실행되게 함) ///////////////////////////////////////////////////////////////////////////// try { // HashMap객체에 저장된 Controller 인터페이스 구현객체 꺼내기 Controller controller = controllerMap.get(requestURI); // 조회된 컨트롤러 객체의 execute(request, response); String viewName = controller.execute(request, response); viewName = "/WEB-INF/views/" + viewName; // 지정된 뷰페이지(viewName)으로 클라이언트의 요청을 이동시킨다. // 즉, JSP를 실행시키는 것이다. // 내부로 이동, 클라이언트의 요청을 파견, 실제 표현은 다른애가 할 거라는걸 알려줌 RequestDispatcher requestDispatcher = request.getRequestDispatcher(viewName); // 내가 추가한 주석 // include는 다른 jsp를 실행시켰다가 되돌아오게 하는 것임. // forward는 다른 jsp로 영원히 보내버리는 것임. 내부이동은 자신이 전달받은 request와 response객체를 반드시 전달해줘야 한다. // 요청객체와 응답객체 없이 jsp를 실행할 수 없다. jsp를 직접 호출한게 아니라 서블릿을 호출한건데 jsp가 데이터를 표현해주는 역할을 하니까 jsp를 실행시켜야 하는데 요청객체와 응답객체를 전달해줘야 한다. // 컨트롤러 실행한 다음에 jsp가 실행된다. requestDispatcher.forward(request, response); } catch (Exception e) { throw new ServletException(e); } } }
HomeController.java package com.sample.controllers; import java.util.HashMap; import java.util.Map; import com.sample.model2.Controller; import com.sample.vo.Product; 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) 실행."); // 요청객체의 속성에 값 혹은 객체 저장하기 request.setAttribute("message", "안녕하세요"); Product product = new Product(100, "갤럭시폴더 4", "삼성전자", 2000000, 1750000, 3); // 요청객체의 속성에 값 혹은 객체 저장하기 request.setAttribute("bestGoods", product); String[] keywords = {"크리스마스", "연말", "선물", "산타"}; // 요청객체의 속성에 값 혹은 객체 저장하기 request.setAttribute("keywords", keywords); Map<String, Object> map = new HashMap<>(); map.put("name", "홍길동"); map.put("kor", 100); map.put("eng", 80); map.put("math", 90); // 요청객체의 속성에 값 혹은 객체 저장하기 request.setAttribute("student", map); // 요청처리를 마치고 home.jsp로 이동시킨다. // 요청처리가 완료되면, 데이터를 표현할 뷰페이지이름을 반환한다. //return "/WEB-INF/views/home.jsp"; 이렇게 적어도 되지만 /WEB-INF/views/까지는 공통이기 때문에 이동시켜주는 FrontController에 적어줘도 된다. return "home.jsp"; } }
home.jsp <%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> <!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> <!-- pageContext 객체의 속성을 추가한다. 속성명 : "menu" 속성값 : "home" --> <c:set var="menu" value="home" /> <div class="container"> <div class="row"> <div class="col-12"> <%@ include file="common/header.jsp" %> </div> </div> <div class="row mb-3"> <div class="col-12"> <h1>홈</h1> </div> </div> <div class="row mb-3"> <div class="row col-12"> <a href="list.hta?page=1">1</a> <a href="list.hta?page=2">2</a> <a href="list.hta?page=3">3</a> <a href="list.hta?page=4">4</a> </div> </div> <div class="row mb-3"> <div class="col-12"> <p>${requestScope.message }</p> <p>${message }</p> </div> </div> <div class="row mb-3"> <div class="col-12"> <p> <span class="badge bg-secondary">${keywords[0] }</span> <span class="badge bg-secondary">${keywords[1] }</span> <span class="badge bg-secondary">${keywords[2] }</span> <span class="badge bg-secondary">${keywords[3] }</span> </p> </div> </div> <div class="row mb-3"> <div class="col-12"> <table class="table"> <thead> <tr> <th>이름</th> <th>국어점수</th> <th>영어점수</th> <th>수학점수</th> <th>총점</th> <th>평균점수</th> <th>합격여부</th> </tr> </thead> <tbody> <tr> <td>${student.name }</td> <td>${student.kor }</td> <td>${student.eng }</td> <td>${student.math }</td> <td>${student.kor + student.eng + student.math }</td> <td>${(student.kor + student.eng + student.math)/3 }</td> <td>${(student.kor + student.eng + student.math)/3 >=60 ? "합격" : "불합격" }</td> </tr> </tbody> </table> </div> <table class="table"> <colgroup> <col width="15%"> <col width="35%"> <col width="15%"> <col width="35%"> </colgroup> <tbody> <tr> <th>상품번호</th><td>${bestGoods.no }</td> <th>재고수량</th><td>${bestGoods.stock } 개</td> </tr> <tr> <th>상품명</th><td>${bestGoods.name }</td> <th>제조회사</th><td>${bestGoods.maker }</td> </tr> <tr> <th>가격</th><td>${bestGoods.price } 원</td> <th>할인가격</th><td>${bestGoods.discountPrice } 원</td> </tr> </tbody> </table> <div> <a href="cart.hta?no=${bestGoods.no }" class="btn btn-primary btx-sm">장바구니 추가</a> <a href="order.hta?no=${bestGoods.no }" class="btn btn-success btx-sm">바로구매</a> </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>
EL표현식 안에서는 쌍따옴표 안에서 홑따옴표로 적어도 쌍따옴표로 적어도 된다.
조건문처리, 반복처리 등 EL로 못했던 것을 JSTL로 표현할 수 있다.
JSTL(JSP Standard Tag Library)
- JSP 표준태그 라이브러리
- 스크립틀릿을 사용해서 작성했던 자바코드를 대체할 수 있다.
- 변수 선언/삭제, 값 출력, 제어문 처리, 반복문 처리, 숫자나 날짜에 대한 포맷팅, 국제화처리, URL처리
- 사용법
- JSTL 파일을 다운받아서 WEB-INF/lib에 복사한다.
- JSP 파일에 사용할 태그라이브러리를 지시어를 사용해서 정의한다.
<%@ taglib prefix="별칭" uri="태그라이브러리식별자" %> <별칭:태그명 value="${EL표현식}" />
page 지시어(디렉티브)
<%@ page %>
contentType="text/html; charset=UTF-8"
include 지시어
<%@ include file="abc.jsp" %>
다른 JSP파일을 포함시킨다.
taglib지시어
<%@ taglib prefix="별칭" uri="태그라이브러리 식별자" %>
jsp 페이지에서 사용할 태그라이브러리를 정의한다.
JSTL 태그라이브러리의 태그 종류
Core 태그
- 가장 많이 사용되는 태그 라이브러리다.
- 변수/출력/제어문/반복문/URL 처리를 지원한다.
- JSP에 아래의 지시어를 정의한다.
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
- <c:out> 태그
- 값을 출력한다.
- <%=값 %>, ${표현식} 과 동일한 작업을 수행한다.
- XSS(Cross Site Scripting) 취약점 공격에 대한 가장 기본적인 방어을 지원한다.
- XSS는 악의적인 사용자가 공격하려는 사이트에 스크립트를 넣어서 쿠키나 세션아이디와 같은 정보를 취득하는 것을 말한다.
- 주로 공개된 게시판의 게시글에 html과 script 코드를 입력해서 해당 스크립트를 실행시키는 것이다.
- 사이트 이용자가 작성하는 컨텐츠는 반드시 <c:out />태그를 사용해서 출력하자.
- 사용법
<c:out value="${표현식}" />
- 주요 속성
- value
- 출력할 값을 지정한다.
- 생략할 수 없음
<c:out value="${book.title}" />
- escapeXml
- 특수문자(< > " ' &)를 변환할지 여부를 지정한다.
- 기본값은 true다.
- 생략가능
- default
- value에서 지정한 값이 null인 경우 표현할 값을 지정한다.
- 생략가능
- value
- <c:if> 태그
- if문과 동일한 역할을 수행한다.
- else에 해당하는 태그는 없다.
- 사용법
<c:if test="${표현식}"> HTML 컨텐츠 </c:if> <!-- test에서 제시한 조건이 true면 HTML 컨텐츠가 화면에 출력된다. --->
- 주요 속성
- test
- 검사조건을 정의한다.
- 결과값이 boolean 타입이어야 한다.
- 생략할 수 없다.
- test
model1방식과 model2방식 - <c:choose> <c:when> <c:otherwise> 태그
- if ~ else if ~ else if ~ else 와 동일한 역할을 수행한다.
- 사용법
<c:choose> <c:when test="${조건식1}"> HTML 컨텐츠 <!-- test에서 제시한 조건식1이 true면 HTML 컨텐츠가 화면에 출력된다. ---> </c:when> <c:when test="${조건식2}"> HTML 컨텐츠 <!-- test에서 제시한 조건식2이 true면 HTML 컨텐츠가 화면에 출력된다. ---> </c:when> <c:when test="${조건식3}"> HTML 컨텐츠 <!-- test에서 제시한 조건식3이 true면 HTML 컨텐츠가 화면에 출력된다. ---> </c:when> <c:otherwise> HTML 컨텐츠 <!-- test에서 제시한 조건식1, 조건식2, 조건식3이 전부 false면 HTML 컨텐츠가 화면에 출력된다. ---> </c:otherwise> </c:choose>
- <c:when>과 <c:otherwise>는 반드시 <c:choose>안에 위치해야 한다.
- <c:when>은 조건식을 다르게 해서 여러 번 정의할 수 있다. test라는 필수 속성이 있다.
- <c:otherwise>는 생략할 수 있고, 맨 마지막 <c:when> 다음에 정의해야 한다.
- <c:when>으로 제시한 조건식이 true가 되면 남아있는 <c:when>은 검사하지 않는다.
- <c:otherwise>는 <c:when>으로 제시한 조건이 모두 false로 판정될 때만 실행된다.
fmt 태그 (formating의 약자)
- 숫자나 날짜에 대한 포맷팅을 지원한다. (지역에 맞게 숫자나 날짜를 바꿔주는 걸 포맷팅이라고 함)
- 국제화처리를 지원한다. (트위터 등 전세계적으로 사용하는 웹사이트 메뉴가 내가 원하는 언어로 나오도록 처음부터 내가 언어를 설정해 주는 것을 국제화처리라고 한다.)
- JSP에 아래의 지시어를 정의한다.
<%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %>
'기록' 카테고리의 다른 글
학원 day79. Redirect, 세션 (0) 2022.12.26 학원 day78. JSTL(2) (0) 2022.12.23 학원 day76. MVC 패턴 (0) 2022.12.21 학원. 세미프로젝트 (0) 2022.12.21 학원 day71. (0) 2022.12.14