근 한 달만에 이 카테고리에 글을 적는 듯 하다.
이번 시간엔 내장 객체의 영역, 무엇이 영역인지, 어느 정도의 영역을 가지는지 등을 알아볼 것이다.
[목표]
내장 객체에서의 영역
영역에 해당하는 개념 4가지
각 영역의 특징 및 사용 예제 알아보기
1. 내장 객체의 영역이란?
한 줄로 표현하면 각 객체가 저장되는 메모리의 유효기간이라고 할 수 있다.
자바에서 지역변수 개념, for문 내 변수선언과 유사하다.
메소드 내에서 선언된 변수는 그 메소드를 벗어나면 소멸되며 for문에서 선언된 변수도 for문을 벗어나면 소멸된다.
하지만 JSP는 페이지 단위로 구성되며 A에서 선언한 변수를 B로 넘겨받아야 하기에 JAVA와는 다르게 접근해야 한다.
이를 위해 JSP에서는 영역을 통해 내장 객체에 저장된 값들을 공유할 수 있도록 하였다.
2. 영역에 해당하는 개념 4가지
(1) Page
동일한 페이지에서만 공유되며 페이지를 벗어나면 소멸된다.
(2) Request
하나의 요청에 의해 호출된 페이지와 전달된 페이지까지 공유됩니다.
(3) Session
클라이언트가 처음 접속 후 웹 브라우저를 닫을 때까지 공유됩니다.
(4) Application
한 번 저장되면 웹 어플리케이션이 종료될 때까지 유지됩니다.
(5) 범위의 크기 순서
: Application > Session > Request > page
(6) API 사용법
//영역이 제공하는 주요 메소드들
// 1)
void setAttribute(String name, Object value)
// 2)
Object getAttribute(String name)
// 3)
void removeAttribute(String name)
1) setAttribute
각 영역에 속성 저장.
(속성명, 저장할 값)
값의 타입은 Object이므로 모든 타입의 객체를 저장할 수 있다.
2) getAttribute
영역에 저장된 값 받아옴.
Object로 자동 형 변환되어 저장되므로 원래 타입으로 캐스팅 후 사용해야 함. (★)
3) removeAttribute
영역에 저장된 속성 삭제
삭제할 속성명이 존재하지 않아도 에러는 발생하지 않는다.
(7) 추가개념: 데이터 전송객체 (DTO, Data Transfer Object)
데이터 전송 객체란?
데이터를 전송하거나 저장하는 데 쓰이는 객체로 순수하게 데이터만 담고 있다.
데이터만 담고 있는 객체라 하여 VO(Value Object)라는 명칭을 갖고 있기도 하다.
DTO는 자바빈즈(JavaBeans)의 규약에 따라 작성한다.
자바빈즈란 자바로 작성한 SW Component로 다음 5가지의 규약이 있다.
자바빈즈는 default 패키지 이외의 패키지에 속해야 한다. (이를 위해 VO 패키지를 하나 만들었다)
멤버 변수의 접근 지정자는 private으로 선언한다.
기본생성자가 있어야 한다.
멤버 변수에 접근할 수 있는 getter/setter 메소드가 있어야 한다.
getter/setter 메소드의 접근 지정자는 public으로 한다.
지금 보고 있는 책에서는 안 하지만 spring에 가니 lombok이란 친구가
어노테이션 하나만 붙여주면 3번부턴 알아서 다 해주더라
3. 각 영역의 예제 살펴보기
(1) DTO 작성
DTO는 Java 파일이므로 Java Resources 하위의 src에 생성한다.
이 때 1번 규약을 지키기 위해 따로 패키지를 만들어 그 안에 넣도록 한다.
//규약 1번
package vo;
public class PersonVO {
//규약 2번 private
private String name;
private int age;
//규약 3번 default 생성자
public PersonVO() {
}
public PersonVO(String name, int age) {
this.name = name;
this.age = age;
}
//규약 4, 5번 : getter/setter, public
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
vo 패키지 안에 PersonVO.java 파일 생성
(2) page 영역
page 영역은 클라이언트의 요청을 처리하는 데 쓰이는 JSP 페이지마다 하나씩 생성된다.
(로그인 페이지, 회원가입페이지, 약관동의페이지, 회원입력 페이지 등등)
이 때 각 page는 page 영역을 사용하기 위한 pageContext 객체를 할당 받는다.
pageContext 객체는 2장에서 설명하지 않은 내장 객체로,
이 객체에 저장된 정보는 해당 페이지에서만 사용 가능하고 페이지를 벗어나면 소멸된다.
하지만 include 지시어로 포함한 파일은 하나의 페이지로 통합되므로 page 영역이 공유된다.
여기서 살펴볼 예제는 page에 값을 저장하고 불러오는 예이며 작성한 DTO를 사용한다.
총 제작된 jsp 파일 갯수: 3개
PageContextMain.jsp
PageInclude.jsp
PageLocation.jsp
<!-- PageContextMain.jsp -->
<%@ page import="vo.PersonVO" %>
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%
pageContext.setAttribute("pageInteger", 1000);
pageContext.setAttribute("pageString", "문자열");
pageContext.setAttribute("pagePerson", new PersonVO("이순신", 54));
%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>내장 객체의 영역 - PAGE</title>
</head>
<body>
<h2>PAGE 속성값 읽기</h2>
<%
int pInt = (Integer) (pageContext.getAttribute("pageInteger"));
String pString = pageContext.getAttribute("pageString").toString();
PersonVO person = (PersonVO) (pageContext.getAttribute("pagePerson"));
%>
<ul>
<li>Integer객체: <%=pInt %></li>
<li>String객체: <%=pString %></li>
<li>PersonVO객체: <%=person.getName() %>, <%=person.getAge() %></li>
</ul>
<hr>
<h2>include 사용하여 page 읽어오기</h2>
<%@ include file="PageInclude.jsp" %>
<hr>
<h2>Page 이동 후 page 영역 읽어오기</h2>
<a href="PageLocation.jsp">PageLocation.jsp로 바로가기</a>
</body>
</html>
<!-- PageInclude.jsp -->
<%@page import="vo.PersonVO"%>
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<h2>Include Page</h2>
<%
int pInt2 = (Integer) (pageContext.getAttribute("pageInteger"));
String pString2 = pageContext.getAttribute("pageString").toString();
PersonVO person2 = (PersonVO) (pageContext.getAttribute("pagePerson"));
%>
<ul>
<li>Integer객체: <%=pInt2 %></li>
<li>String객체: <%=pString2 %></li>
<li>PersonVO객체: <%=person2.getName() %>, <%=person2.getAge() %></li>
</ul>
<!-- PageLocation.jsp -->
<%@page import="vo.PersonVO"%>
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
<%
Object pInt = (pageContext.getAttribute("pageInteger"));
Object pString = pageContext.getAttribute("pageString");
Object person = (pageContext.getAttribute("pagePerson"));
%>
<ul>
<li>Integer객체: <%=(pInt==null) ? "값 없음": pInt%></li>
<li>String객체: <%=(pString==null) ? "값 없음": pString %></li>
<li>PersonVO객체: <%=(person==null) ? "값 없음": ((PersonVO)person).getName() %></li>
</ul>
</body>
</html>
[결과화면]
PageContextMain.jsp(맨 위), IncludePage.jsp(중간), PageLocation.jsp(맨 아래)
PageLocation.jsp 실행 화면
[알게 된 사실]
page 공유에서 include는 결국 삽입되어지는 내용이기에 결과가 Main과 같이 나온다는 것을 볼 수 있으나
PageLocation처럼 이동된 후에는 값 없음으로 나오는 것을 볼 수 있다.
같은 페이지에 속한 것과 그렇지 않은 차이에 대해 알 수 있었다.
(3) request 영역
클라이언트가 요청할 때마다 새로운 request 객체가 생성되고
같은 요청을 처리하는 데 사용하는 JSP 페이지가 공유한다.
따라서 request 영역에 저장된 정보는 현재 페이지와 포워드되는 페이지까지 공유할 수 있다.
단, 위와 같이 페이지 이동 시에는 소멸되어 사용할 수 없게 된다.
request는 요청에 대한 응답이 완료될 때 소멸하므로 page보다 조금 더 넓다고 볼 수 있다.
[총 제작된 파일 갯수: 2개]
RequestMain.jsp
RequestForward.jsp
<!-- RequestMain.jsp -->
<%@page import="vo.PersonVO"%>
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%
request.setAttribute("requestString", "문자열");
request.setAttribute("requestPerson", new PersonVO("2000년생", 22));
%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>내장 객체의 영역 - Request</title>
</head>
<body>
<h2>속성값 삭제</h2>
<%
//없는 것을 삭제하려 해도 에러는 발생하지 않음.
request.removeAttribute("requestString");
request.removeAttribute("requestInteger");
%>
<hr>
<h2>속성값 읽기</h2>
<%
PersonVO person = (PersonVO) request.getAttribute("requestPerson");
%>
<ul>
<li>String 객체: <%=request.getAttribute("requestString") %></li>
<li>Person 객체: <%=person.getName() %>, <%=person.getAge() %></li>
</ul>
<hr>
<h2>포워드된 페이지에서 request 속성값 읽기</h2>
<%
request.getRequestDispatcher("RequestForward.jsp?paramHan=한글¶mEng=English").forward(request, response);
%>
</body>
</html>
<!-- RequestForward.jsp -->
<%@page import="vo.PersonVO"%>
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>포워드된 페이지</title>
</head>
<body>
<%
PersonVO person = (PersonVO) request.getAttribute("requestPerson");
%>
<ul>
<li>String 객체: <%=request.getAttribute("requestString") %></li>
<li>Person 객체: <%=person.getName() %>, <%=person.getAge() %></li>
</ul>
<h2>매개변수로 전달된 값 출력하기</h2>
<%
//인코딩설정, because of paramHan,, maybe
request.setCharacterEncoding("UTF-8");
out.println(request.getParameter("paramHan"));
out.println(request.getParameter("paramEng"));
%>
</body>
</html>
포워드 된 페이지, 하지만 주소는 RequestMain.jsp
[알게 된 사실]
1) RequestMain.jsp에서 removeAttribute를 할 때 없는 변수를 삭제하겠다고 해도 에러 발생하지 않음.
2) forward문이 작성되어져 있으면 main을 실행해도 forward 페이지가 출력되어짐.
3) 전달되는 변수에 한글이 있으면 setCharacterEncoding을 사용하여 "UTF-8"을 지정해줘야 함.
(4) session 영역
클라이언트가 웹 브라우저를 최초로 열고난 후 닫을 때까지 요청되는 모든 페이지는 session 객체를 공유할 수 있다.
세션이란 클라이언트가 서버에 접속해 있는 상태 혹은 단위를 말하며,
로그인 상태를 유지하는 처리에 주로 사용된다.
포털 사이트에서 웹 브라우저를 닫으면 자동으로 로그아웃이 되는 이유가 바로 session의 특성 때문이다.
총 제작된 파일 갯수: 2개
SessionMain.jsp
SessionLocation.jsp
<!-- SessionMain.jsp -->
<%@page import="java.util.ArrayList"%>
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%
ArrayList<String> list = new ArrayList<String>();
list.add("리스트");
list.add("컬렉션");
session.setAttribute("list", list);
%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>내장 객체의 영역 - session</title>
</head>
<body>
<h2>페이지 이동 후 session 영역의 속성 읽기</h2>
<a href="SessionLocation.jsp">SessionLocation.jsp 바로가기</a>
</body>
</html>
<!-- SessionLocation.jsp -->
<%@page import="java.util.ArrayList"%>
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>session 이동</title>
</head>
<body>
<h2>페이지 이동 후 session 속성 읽기</h2>
<%
ArrayList<String> list = (ArrayList<String>)session.getAttribute("list");
for(String str : list)
out.print(str + "<br/>");
%>
</body>
</html>
SessionMain.jsp to SessionLocation.jsp
SessionLocation.jsp 파일
[알게 된 사실]
1) session을 완전히 종료하고 싶으면 해당하는 탭만 닫는 것이 아니라 브라우저 전체를 닫아야 한다.
2) ArrayList<String>은 java.util 패키지의 것을 import하여 사용한다.
(5) Application 영역
웹 어플리케이션은 단 하나의 application 객체만 생성하고
클라이언트가 요청하는 모든 페이지가 applicaion 객체를 공유한다.
application은 웹 서버가 시작할 때 만들어져서 웹 서버를 내릴 때 삭제된다.
그러므로 application에 한 번 저장된 정보는 페이지를 이동하거나 브라우저를 닫았다 다시 접속해도 삭제되지 않는다.
총 제작된 파일 갯수: 2개
ApplicationMain.jsp
ApplicationResult.jsp
<!-- ApplicationMain.jsp -->
<%@page import="vo.PersonVO"%>
<%@page import="java.util.Map"%>
<%@page import="java.util.HashMap"%>
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>내장 객체의 영역 - Application</title>
</head>
<body>
<h2>Application 영역의 공유</h2>
<%
Map<String, PersonVO> map = new HashMap<>();
map.put("p1", new PersonVO("한여름", 34));
map.put("p2", new PersonVO("한겨울", 11));
application.setAttribute("map", map);
%>
application 영역에 속성이 저장되었습니다.
<a href="ApplicationResult.jsp">바로보기</a>
</body>
</html>
<!-- ApplicationResult.jsp -->
<%@page import="java.util.Set"%>
<%@page import="vo.PersonVO"%>
<%@page import="java.util.Map"%>
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Application 영역</title>
</head>
<body>
<h2>appication 속성 읽기</h2>
<%
Map<String, PersonVO> map = (Map<String, PersonVO>)application.getAttribute("map");
Set<String> keys = map.keySet();
for (String key : keys) {
PersonVO person = map.get(key);
out.print(String.format("이름: %s, 나이: %d<br/>", person.getName(), person.getAge()));
}
%>
</body>
</html>
ApplicationMain.jsp에서 속성 저장
ApplicationResult.jsp만 열었을 때 저장되어져 있는 값들이 보임
(6) 마치며
1) page: 동일한 페이지에서만 공유 / 벗어나면 소멸
2) request: 하나의 요청에 의해 호출 및 포워드된 페이지까지 공유 / 페이지 이동하면 소멸
3) session: 클라이언트가 처음 접속한 후 웹 브라우저를 닫을 때까지 공유 / 포워드, 페이지 이동시에도 소멸 X
4) application: 한 번 저장되면 서버가 셧다운되지 않는 한 계속 공유