[Servlet / JSP] 내장 객체의 영역(Scope)

suyeon·2022년 7월 4일
1

Servlet / JSP

목록 보기
6/9
post-thumbnail

1. 내장 객체의 영역

내장 객체의 영역은 각 객체가 저장되는 메모리의 유효기간

  • page 영역 : 동일한 페이지에서만 공유. 페이지를 벗어나면 소멸

  • request 영역 : 하나의 요청에 의해 호출된 페이지와 포워드(요청 전달)된 페이지까지 공유.
    새로운 페이지를 요청(페이지 이동)하면 소멸

  • session 영역 : 클라이언트가 처음 접속한 후 웹 브라우저를 닫을 때까지 공유.
    포워드는 페이지 이동 시에도 영역은 소멸되지 않음

  • application 영역 : 한 번 저장되면 웹 애플리케이션이 종료될 때까지 유지.
    즉, 서버가 셧다운되지 않는다면 언제까지든 공유되는 영역

[ 영역이 제공하는 주요 메서드 ]

void setAttribute(String name, Object value)

  • 각 영역에 속성을 저장
  • 첫 번째 인수는 속성명, 두 번째 인수는 저장할 값
  • 값의 타입은 Object이므로 모든 타입의 객체를 저장

Object getAttribute(String name)

  • 영역에 저장된 속성값을 얻어옴
  • Object로 자동 형변환되어 저장되므로 원래 타입으로 형변환 후 사용해야 함

void removeAttribute(String name)

  • 영역에 저장된 속성을 삭제
  • 삭제할 속성명이 존재하지 않더라도 에러는 발생하지 않음

2. 데이터 전송 객체(DTO) 준비

데이터 전송 객체(Data Transfer Object, DTO)

  • 데이터를 저장하거나 전송하는 데 쓰이는 객체로, 다른 로직 없이 순수하게 데이터만을 담고 있으며, 값 객체(Value Object)라고도 함
  • 자바빈즈(JavaBeans) 규약에 따라 작성 함

💬 자바빈즈(JavaBeans)

- 자바로 작성한 소프트웨어 컴포넌트로, 다음의 규약을 따르는 자바 클래스를 말한다.

[ 자바빈즈 규약 ]

1. 자바빈즈는 기본(default) 패키지 이외의 패키지에 속해야 함
2. 멤버 변수(속성)의 접근 지정자는 private으로 선언
3. 기본 생성자가 있어야 함
4. 멤버 변수에 접근할 수 있는 게터(getter)/세터(setter) 메서드가 필요
5. 게터/세터 메서드의 접근 지정자는 public으로 선언

[예제] ‘Person’ 이름의 DTO를 작성해보자.

  • DTO는 일반적인 자바 클래스이다. 즉, JSP 파일이 아니다.
  • src/main/java에서 자바 파일을 생성한다.

package common;           // 기본 패키지 이외의 패키지(규약 1번)

public class Person {
    private String name;  // private 멤버 변수(규약 2번)
    private int age;      // private 멤버 변수(규약 2번)

    public Person() {}    // 기본 생성자(규약 3번)
    public Person(String name, int age) {
        super();
        this.name = name;
        this.age = age;
    }

    // public 게터/세터 메서드들(규약 4번, 5번)
    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;
    }
}

3. page 영역

  • page 영역은 클라이언트의 요청을 처리하는 데 관여하는 JSP 페이지마다 하나씩 생성
  • 이때 각 JSP 페이지는 page 영역을 사용하기 위한 pageContext 객체를 할당
  • pageContext 객체 : 저장된 정보는 해당 페이지에서만 사용할 수 있고 페이지를 벗어나면 소멸됨

[예제] page 영역에 값을 저장하고 불러오자.

// 1. common.Person 임포트
<%@ page import="common.Person"%> 
<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%
// 2. 속성 저장
pageContext.setAttribute("pageInteger", 1000);
pageContext.setAttribute("pageString", "페이지 영역의 문자열");
pageContext.setAttribute("pagePerson", new Person("한석봉", 99)); 
%>
<html>
<head><title>page 영역</title></head>
<body>
    <h2>page 영역의 속성값 읽기</h2>
    <%
    // 3. 속성 읽기
    int pInteger = (Integer)(pageContext.getAttribute("pageInteger"));
    Person pPerson = (Person)(pageContext.getAttribute("pagePerson"));
    %>
    <ul>
        <li>Integer 객체 : <%= pInteger %></li>
        <li>String 객체 : <%= pageContext.getAttribute("pageString") %></li> // 4.
        <li>Person 객체 : <%= pPerson.getName() %>, <%= pPerson.getAge() %></li> // 5.
    </ul>

    <h2>include된 파일에서 page 영역 읽어오기</h2>
    <%@ include file="PageInclude.jsp" %> // 6.
       
    <h2>페이지 이동 후 page 영역 읽어오기</h2>
    <a href="PageLocation.jsp">PageLocation.jsp 바로가기</a> // 7.
</body>
</html>

① 먼저 코드에서 외부 클래스인 common.Person을 사용하기 위해 임포트

pageContext 객체를 통해 page 영역에 속성값을 저장. 객체가 아닌 기본 타입 값들은 해당 래퍼 클래스로 오토박싱된 후 저장

③ 저장한 속성들을 다시 읽어오기. 단, 모든 속성이 Object 타입으로 저장되어 있으므로 다시 원래의 타입으로 형변환

④ 지정한 객체가 문자열이거나 기본 타입의 래퍼(wrapper) 클래스라면 별도의 형변환 없이 출력해도 됨

⑤ Person 객체에서 멤버 변수의 값을 읽어오는 예. Person은 DTO라서 멤버 변수가 private으로 선언되었으므로 게터 메서드를 이용

include 지시어로 다른 JSP 파일을 포함. ‘포함’ 관계이므로 ‘같은 페이지’

⑦ 에서 <a> 태그로 감싼 링크를 클릭하면 ‘다른 페이지’로 ‘이동’. 따라서 이전 페이지에서 만든 page 영역은 소멸

PageInclude.jsp 파일

<%@ page import="common.Person"%>
<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<h4>Include 페이지</h4>
<%
int pInteger2 = (Integer)(pageContext.getAttribute("pageInteger"));
Person pPerson2 = (Person)(pageContext.getAttribute("pagePerson"));
%>
<ul>
    <li>Integer 객체 : <%= pInteger2 %></li>
    <li>String 객체 : <%= pageContext.getAttribute("pageString") %></li>
    <li>Person 객체 : <%= pPerson2.getName() %>, <%= pPerson2.getAge() %></li>
</ul>

PageLocation.jsp 파일

<%@ page import="common.Person"%>
<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<html>
<head><title>page 영역</title></head>
<body>
    <h2>이동 후 page 영역의 속성값 읽기</h2>
    <%
    Object pInteger = pageContext.getAttribute("pageInteger");
    Object pString = pageContext.getAttribute("pageString");
    Object pPerson = pageContext.getAttribute("pagePerson"); 
    %>
    <ul>
        <li>Integer 객체 : <%= (pInteger == null) ? "값 없음" : pInteger %></li>
        <li>String 객체 : <%= (pString == null) ? "값 없음" : pString %></li>
        <li>Person 객체 : <%= (pPerson == null) ? "값 없음" : ((Person)pPerson).getName() %></li>
    </ul>
</body>
</html>
  • 이번에는 형변환을 하지 않음
  • 가져오려는 속성이 존재하지 않는다면 getAttribute( ) 메서드가 null을 반환하고, null을 int 타입 변수에 담으려 시도하면 NullPointerException이 발생하기 때문

  • <a> 태그를 통한 이동은 새로운 페이지를 요청하는 것이다. 즉, 서로 다른 페이지이므로 page 영역은 공유되지 않는다.

4. request 영역

  • request 영역은 하나의 요청에 대한 응답이 완료될 때 소멸하게 되므로 page 영역보다는 접근 범위가 조금 더 넓음
  • 단, 페이지 이동 시에는 소멸된다.
  • 하지만, forward로 다른 페이지로 이동하는 경우에는 살아있다.

[예제] 최초 페이지

<%@ page import="common.Person"%>
<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<% // 1.
request.setAttribute("requestString", "request 영역의 문자열");
request.setAttribute("requestPerson", new Person("안중근", 31)); 
%>
<html>
<head><title>request 영역</title></head>
<body>
    <h2>request 영역의 속성값 삭제하기</h2>
    <%
        request.removeAttribute("requestString"); // 2.
        request.removeAttribute("requestInteger"); // 에러 없음 3.
    %>
    <h2>request 영역의 속성값 읽기</h2> // 4.
    <%
    Person rPerson = (Person)(request.getAttribute("requestPerson"));
    %>
    <ul>
        <li>String 객체 : <%= request.getAttribute("requestString") %></li>
        <li>Person 객체 : <%= rPerson.getName() %>, <%= rPerson.getAge() %></li>
    </ul>
    <h2>포워드된 페이지에서 request 영역 속성값 읽기</h2>
    <%
    request.getRequestDispatcher("RequestForward.jsp?paramHan=한글&paramEng=English")  
        .forward(request, response); // 5.
    %>
</body>
</html>

① request 영역에 String 객체와 Person 객체를 저장

②와 ③ request 영역에 저장된 속성을 삭제

  • ② “requestString”은 당연히 정상적으로 삭제되는데,
  • ③ 두 번째 줄에서는 존재하지 않는 “requestInteger”를 삭제하려 시도 > 에러 발생 X

④ 속성값을 읽어와서 출력

⑤ request 내장 객체를 통해 실제로 포워드를 수행하는 코드

⑤번 코드를 다음과 같이 작성할 수도 있음

<%
RequestDispatcher rd = request.getRequestDispatcher(
						"RequestForward.jsp?paramHan=한글&paramEng=English");
rd.forward(request, response);
%>
  • getRequestDispatcher( )의 반환 타입이 RequestDispatcher이며, 이 객체가 요청을 다른 페이지로 넘겨주는 기능을 수행

💬포워드

현재 페이지로 돌아온 요청을 다음 페이지로 보내는 기능

포워드 되는 JSP 페이지

<%@ page import="common.Person"%>
<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<html>
<head><title>request 영역</title></head>
<body>
    <h2>포워드로 전달된 페이지</h2>
    <h4>RequestMain 파일의 리퀘스트 영역 속성 읽기</h4>
    <%  
    Person pPerson = (Person)(request.getAttribute("requestPerson")); // 1.
    %>
    <ul>
        <li>String 객체 : <%= request.getAttribute("requestString") %></li>
        <li>Person 객체 : <%= pPerson.getName() %>, <%= pPerson.getAge() %></li> // 2.
    </ul>
    <h4>매개변수로 전달된 값 출력하기</h4>
    <%
        request.setCharacterEncoding("UTF-8"); // 3.
        out.println(request.getParameter("paramHan")); // 4.
        out.println(request.getParameter("paramEng"));
    %>
</body>
</html>

①과 ② request 영역에 저장된 속성들을 읽어와서 출력

③ 인코딩 방식을 UTF-8로 변경

④ 포워드하면서 쿼리스트링으로 전달한 매개변수의 값을 출력

  • request 영역에 저장된 속성값은 현재 페이지와 포워드된 페이지까지 공유된다.

5. session 영역

세션(session)

  • 클라이언트가 서버에 접속해 있는 상태 혹은 단위, 주로 회원인증 후 로그인 상태를 유지하는 처리에 사용

  • 클라이언트가 웹 브라우저를 최초로 열고난 후 닫을 때까지 요청되는 모든 페이지는 session 객체를 공유

[예제] 최초 페이지

<%@ page import="java.util.ArrayList"%>
<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%
ArrayList<String> lists = new ArrayList<String>();
lists.add("리스트");
lists.add("컬렉션");
session.setAttribute("lists", lists); // 1.
%>       
<html>
<head><title>session 영역</title></head>
<body>
    <h2>페이지 이동 후 session 영역의 속성 읽기</h2>
    <a href="SessionLocation.jsp">SessionLocation.jsp 바로가기</a> // 2.
</body>
</html>

① ArrayList 컬렉션을 통째로 session 영역에 저장
② session 영역이 이동된 페이지에서도 공유되는지 확인하기 위한 링크

링크를 클릭해 이동된 페이지

<%@ page import="java.util.ArrayList"%>
<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<html>
<head><title>session 영역</title></head>
<body>
    <h2>페이지 이동 후 session 영역의 속성 읽기</h2>
    <%
    ArrayList<String> lists = (ArrayList<String>)session.getAttribute("lists"); 
    for (String str : lists)
        out.print(str + "<br/>");
    %>    
</body>
</html>

  • 페이지가 이동되었지만 session 영역에 저장된 속성값은 정상적으로 출력
  • session 영역의 속성값을 삭제하고 싶다면 웹 브라우저를 완전히 닫았다가 다시 열면 됨
    - 탭만 닫아서는 session이 삭제되지 않고, 반드시 웹 브라우저 전체를 닫아야 함

  • 웹 브라우저를 닫으면 session 객체가 삭제되고, 웹 브라우저를 다시 실행하면 그때 새로운 session 객체가 생성
  • getAttribute(“lists”)로 속성값을 읽어오려 하면 null을 반환하여 NullPointerException이 발생

6. application 영역

  • 웹 애플리케이션은 단 하나의 application 객체만 생성하고, 클라이언트가 요청하는 모든 페이지가 application 객체를 공유
  • application 객체는 웹 서버를 시작할 때 만들어지며, 웹 서버를 내릴 때 삭제됨
  • 따라서 application 영역에 한 번 저장된 정보는 페이지를 이동하거나, 웹 브라우저를 닫았다가 새롭게 접속해도 삭제되지 않음

[예제] 최초 페이지

<%@ page import="java.util.HashMap"%>
<%@ page import="common.Person"%>
<%@ page import="java.util.Map"%>
<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<html>
<head><title>application 영역</title></head>
<body>
    <h2>application 영역의 공유</h2>
    <%
    Map<String, Person> maps = new HashMap<>(); // 1.
    maps.put("actor1", new Person("이수일", 30));
    maps.put("actor2", new Person("심순애", 28));
    application.setAttribute("maps", maps); // 2.
    %>
    application 영역에 속성이 저장되었습니다.
</body>
</html>

① HashMap 컬렉션을 생성한 후 두 개의 Person 객체를 저장

② 컬렉션 채로 application 영역에 저장

결과 페이지

<%@ page import="java.util.Set"%>
<%@ page import="common.Person"%>
<%@ page import="java.util.Map"%>
<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<html>
<head><title>application 영역</title></head>
<body>
    <h2>application 영역의 속성 읽기</h2>
    <%
    Map<String, Person> maps
            = (Map<String, Person>)application.getAttribute("maps"); // 1.
    Set<String> keys = maps.keySet(); // 2.
    for (String key : keys) { // 3.
        Person person = maps.get(key);
        out.print(String.format("이름 : %s, 나이 : %d<br/>", 
                person.getName(), person.getAge()));
    }  
    %>
</body>
</html>

① 메인 페이지에서 application 영역에 저장한 “maps” 속성값을 읽어서 원래 형태인 Map<String, Person> 타입 변수에 저장

  • Map 컬렉션에 담긴 데이터를 확인하려면 먼저 키(key)들을 알아야 함

② keySet( )으로 얻어옴

③ 확장 for문에서 모든 키에 해당하는 값들을 하나씩 꺼내 출력

  • Map에 저장된 객체를 꺼낼 때는 get( )을 사용

  • session을 삭제했을 때 처럼 '모든 웹 브라우저를 닫고' 실행하여도, 여전히 정상적으로 출력된다. 이 속성값은 웹 서버가 종료되지 않는 한 계속 유지된다.

  • 이클립스에서 웹 서버를 재시동하면, 에러 페이지가 뜬다.

⭐ 기억

생명주기

  • pageContext (한페이지) < request (한페이지~forward) < session (접속중) < application (사이트 동작중)


참고 자료
🔗 성낙형의 JSP 자바 웹 프로그래밍

0개의 댓글