KH 정보교육원 [ Java ]

KH 94일차 - 세션 (**)

monimoni 2022. 7. 12. 15:21

1.    세션 관리 ( Session Tracking )

-      세션의 정의는 서버와 클라이언트 간의 지속적인 연결을 의미한다.

-      연결을 통하여 클라이언트는 지속적으로 서버에 특정 동작을 요청할 수 있으며, 서버는 실행결과를 클라이언트에 응답할 수 있다.

-      세션관리는 일반적으로 HttpSession 클래스나 Cookie 클래스를 통하여 한다.

 

2.    HttpSession 클래스를 이용한 세션처리

-      세션이란 사용자의 상태정보를 서버에서 관리하는 메커니즘을 의미한다.

-      세션의 정보는 클라이언트가 서버에 접속해서 종료할 때까지 유지된다.

-      서버의 부하가 클 수 있기에, 주로 time-out을 지정하여 일정시간 동안 요청이 없으면 서버는 세션의 정보를 유지하지 않고 제거해버린다.

-      클라이언트가 서블릿에 요청을 하면, 서블릿에서는 getSession( ) 메소드를 사용하여 session영역을 생성하고, 세션ID를 생성하여 session영역에 저장한다.


[ 1. web.xml파일에 세션 Time Out 지정 방법 - 전체적인 프로젝트에 적용 ]

 

<session-config>
      <session-timeout>30</session-timeout>
</session-config>

[ 2. 세션 ID ] (*)

 

package org.zerock.myapp;

import java.io.IOException;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

import lombok.NoArgsConstructor;
import lombok.extern.log4j.Log4j2;


@Log4j2
@NoArgsConstructor

@WebServlet("/SessionIdTest")
public class SessionIdTestServlet extends HttpServlet {
	private static final long serialVersionUID = 1L;
       
	@Override
	protected void service(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException {
		
		log.trace("service(req, res) invoked.");
		
		// 웹 브라우저가 최초로 request message를 보내오면,
		// Servlet Container는 이 웹 브라우저에 이름(= 세션 ID)을 지어서
		// Response Message의 헤더에 담아서 전송한다.
		
		// 웹 브라우저는 응답헤더에서 자기의 이름(= 세션 ID)를 추출해서 메모리에 저장
		// 이후, 두번째 이상 요청 부터는 항상 자기의 이름을(= 세션 ID)을 함께 담아서 요청을 보낸다.
		
		// =======================================================
		
		// HttpSession 객체 => 추상적인 세션을 객체로 만든 것이다. 
		// request 객체로부터 획득이 가능하다.
		
		// 1. Session 생성
		
		HttpSession sess = req.getSession();			// 기존 세션이 있으면 돌려주고, 없으면 만들어달라
		// HttpSession sess1 = req.getSession(true);	// 기존 세션이 있으면 돌려주고, 없으면 만들어달라
		// HttpSession sess2 = req.getSession(false);	// 기존 세션이 있으면 돌려주고, 없으면 만들지 말라!! ( 잘 사용하지 X )
		
		// =======================================================
		
		// 2. Session Id 얻어내기
		
		String sessionId = sess.getId();
		log.info("\t + 1. sessionId :{}", sessionId);
		// sessionId는 유니크한 전역적인 Id로 생성된다.
		
		// =======================================================
		
		// 3. Session Scope 파괴
		
		sess.invalidate(); // 이 세션Id로 식별되는 Session Scope 공유데이터 영역 파괴
		log.info("\t + Session Scope Destroyed Definitely.");
		
		// 파괴되었기에, 새로고침을 하면 새로운 Session Id가 생성되게 된다.
		// 파괴되지 않았다면, 새로고침을 해도 원래 기존의 Session Id가 나온다.
		
		// =======================================================
		
		// 4. Session Scope 파괴 후 다시 Session Id 생성
		
		sess = req.getSession();
		sessionId = sess.getId();
		log.info("\t + 2. sessionId :{}", sessionId);
		
		// 서로 다른 Session Id가 출력되고 있음을 알 수 있다.
		
		// =======================================================
		
	} // service

} // end class

[ 3. 세션을 이용한 장바구니 만들기 ] (***)

 

[ 3 - 1. 장바구니 생성 및 품목 넣기 ]

 

package org.zerock.myapp;

import java.io.IOException;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.List;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

import lombok.Cleanup;
import lombok.NoArgsConstructor;
import lombok.extern.log4j.Log4j2;


@Log4j2
@NoArgsConstructor

@WebServlet("/CartSave") // 장바구니에 담기
public class CartSaveServlet extends HttpServlet {
	private static final long serialVersionUID = 1L;
       
	@Override
	protected void service(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException {
		
		log.trace("service(req, res) invoked.");
		
		// 웹 브라우저가 최초로 request message를 보내오면,
		// Servlet Container는 이 웹 브라우저에 이름(= 세션 ID)을 지어서
		// Response Message의 헤더에 담아서 전송한다.
		
		// 웹 브라우저는 응답헤더에서 자기의 이름(= 세션 ID)를 추출해서 메모리에 저장
		// 이후, 두번째 이상 요청 부터는 항상 자기의 이름을(= 세션 ID)을 함께 담아서 요청을 보낸다.
		
		// =======================================================
		
		// HttpSession 객체 => 추상적인 세션을 객체로 만든 것이다. 
		// request 객체로부터 획득이 가능하다.
		
		// =======================================================
		
		// 1. 장바구니에 저장할 상품항목을 전송 파라미터로 획득
		
		req.setCharacterEncoding("UTF-8");
		String product = req.getParameter("product");
		
		log.info("\t + product :{}",product);
		
		// =======================================================
		
		// 2. 장바구니 생성 및 수신된 상품을 장바구니에 추가
		
		HttpSession sess = req.getSession();			// 기존 세션이 있으면 돌려주고, 없으면 만들어달라
		// HttpSession sess1 = req.getSession(true);	// 기존 세션이 있으면 돌려주고, 없으면 만들어달라
		// HttpSession sess2 = req.getSession(false);	// 기존 세션이 있으면 돌려주고, 없으면 만들지 말라!! ( 잘 사용하지 X )
		
		// sess.getAttribute("장바구니 이름지정");
		
		@SuppressWarnings("unchecked")
		List<String> list = (List<String>) sess.getAttribute("basket");
		// @SuppressWarnings는 너무 강하게 체크하지 말라는 이야기이다.
		
		if( list != null ) { 					// 1. 장바구니가 있어 얻어냄
			
			list.add(product);					// 1-1. 장바구니가 있으니, 장바구니에 상품 추가
			
		} else {								// 2. 장바구니가 Session Scope에 없다.
			
			list = new ArrayList<>();			// 2-1. 장바구니가 없으니 장바구니를 만들고
			list.add(product);					// 2-2. 상품을 추가한 후에
			
			sess.setAttribute("basket", list);	// 2-3. Session Scope에 올린다.
			
		} // if - else
		
		log.info("\t + list : {}", list);
		
		// =======================================================
		
		// 3. 응답문서 준비
		
		res.setContentType("text/html; charset=utf8");
		
		@Cleanup
		PrintWriter out = res.getWriter();
		
		out.println("<h1>장바구니에 담기 성공</h1>");
		out.println("<a href='/CartSave'>장바구니 보기</a>");
		
		out.flush();
		
		// =======================================================
		
	} // service

} // end class

 

[ 3 - 2. 장바구니 조회하기 ]

 

package org.zerock.myapp;

import java.io.IOException;
import java.io.PrintWriter;
import java.util.List;
import java.util.Objects;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

import lombok.Cleanup;
import lombok.NoArgsConstructor;
import lombok.extern.log4j.Log4j2;


@Log4j2
@NoArgsConstructor

@WebServlet("/CartBasket")
public class CartBasketServlet extends HttpServlet {
	private static final long serialVersionUID = 1L;

	@SuppressWarnings("unchecked")
	@Override
	protected void service(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException {
		
		log.trace("service(req, res) invoked.");
		
		List<String> list = null;
		
		try {
			
			HttpSession sess = req.getSession(false);
			log.info("\t + sess : {}", sess);
			// 세션이 죽지 않았기에, 그대로 세션Id가 동일하게 나온다.
			// 기존 세션이 없으면 장바구니도 없기에, false를 주어 출력하지 않게 하였다.
			
			Objects.requireNonNull(sess);
			// Null인지 체크
			
			// =====================================
			
			list = (List<String>) sess.getAttribute("basket");
			// Session Scope에 올려져 있기에 sess에서 얻어야 한다.
			
			Objects.requireNonNull(list);
			// Null인지 체크
			
			list.forEach(log::info);
			// 널이 아니라면 하나씩 찍어라
			
			// =====================================
			
		} catch(Exception e) {
			throw new ServletException(e);
		} // try - catch
		
		// =====================================
		
		res.setContentType("text/html; charset=utf8");
		
		@Cleanup
		PrintWriter out = res.getWriter();
		
		out.println("<h1>장바구니에 내용</h1>");
		out.println("<ol>");
		
		list.forEach(s -> {
			out.println("\t <li>" + s + "</li>");
		}); // for each
		
		out.println("</ol>");
		
		out.println("<a href='/CartDelete'>장바구니 비우기</a>");
		
		out.flush();
		
	} // service

} // end class

 

[ 3 - 3. 장바구니 비우기 ]

 

package org.zerock.myapp;

import java.io.IOException;
import java.util.Objects;

import javax.servlet.RequestDispatcher;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

import lombok.NoArgsConstructor;
import lombok.extern.log4j.Log4j2;


@Log4j2
@NoArgsConstructor

@WebServlet("/CartDelete")
public class CartDeleteServlet extends HttpServlet {
	private static final long serialVersionUID = 1L;

	@Override
	protected void service(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException {
		
		log.trace("service(req, res) invoked.");
		
		// 장바구니 비우기 : 현재 브라우저의 세션 아이디(이름)로 식별되는 Session Scope 공유영역 파괴
		
		HttpSession sess = req.getSession(false);
		// false로 준 이유는, 세션이 없다면 장바구니가 없다는 의미이기에 수행될 필요가 없기 때문이다.
		
		try {
			
			Objects.requireNonNull(sess);
			// 세션이 유효하다면,
			
			sess.invalidate();
			// Session Id 무효화 + Session Scope 영역 파괴
			
			// ===========================================
			
			// 마지막 응답 페이지는 요청 포워딩을 통해 ( Request Forwarding)
			// JSP에서 생성
			
			RequestDispatcher dispatcher = req.getRequestDispatcher("/WEB-INF/views/cartdelete.jsp");
			dispatcher.forward(req, res);
			
		} catch(Exception e) {
			throw new ServletException(e);
		} // try - catch
		
	}// service

} // end class

 

[ 3 - 4. 장바구니 비울 때의 화면 만들기 ]

 

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>cartdelete</title>
</head>
<body>

    <h1>cartdelete.jsp</h1>
    <hr>

    <h2>장바구니가 비워졌습니다.</h2>

</body>
</html>

[ 4. jsp파일로 요청 포워딩하기 ] (***)

 

[ 4 - 1. DTO 객체 생성 ]

 

package org.zerock.myapp.domain;

import lombok.Data;

@Data
public class Person {
	
	private String name;
	private int age;

} // end class

 

[ 4 - 2. A Servlet 생성 -> B Servlet으로 요청 포워딩 ]

 

package org.zerock.myapp.servlet;

import java.io.IOException;

import javax.servlet.RequestDispatcher;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import lombok.NoArgsConstructor;
import lombok.extern.log4j.Log4j2;


@Log4j2
@NoArgsConstructor

@WebServlet("/A")
public class AServlet extends HttpServlet {
	private static final long serialVersionUID = 1L;

	@Override
	protected void service(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException {
		
		log.trace("service(req, res) invoked.");
		
		log.info("A Servlet - 요청 잘 받았습니다.");
		log.info("A Servlet - 저는 여기까지만 처리합니다.");
		
		// B서블릿으로 위임시킨다.
		req.getRequestDispatcher("/B").forward(req, res);
		
	} // service

} // end class

 

[ 4 - 2. B Servlet 생성 -> C Servlet으로 요청 포워딩 ]

 

package org.zerock.myapp.servlet;

import java.io.IOException;

import javax.servlet.RequestDispatcher;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import lombok.NoArgsConstructor;
import lombok.extern.log4j.Log4j2;


@Log4j2
@NoArgsConstructor

@WebServlet("/B")
public class BServlet extends HttpServlet {
	private static final long serialVersionUID = 1L;

	@Override
	protected void service(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException {
		
		log.trace("service(req, res) invoked.");
		
		log.info("B Servlet - 요청 잘 받았습니다.");
		log.info("B Servlet - 저는 여기까지만 처리합니다.");
		
		req.getRequestDispatcher("/C").forward(req, res);
		
	} // service

} // end class

 

[ 4 - 3. C Servlet 생성 및 Person 객체 생성 및 Request Scope에 올리기 -> jsp 파일로 요청 포워딩 ]

 

package org.zerock.myapp.servlet;

import java.io.IOException;

import javax.servlet.RequestDispatcher;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.zerock.myapp.domain.Person;

import lombok.NoArgsConstructor;
import lombok.extern.log4j.Log4j2;


@Log4j2
@NoArgsConstructor

@WebServlet("/C")
public class CServlet extends HttpServlet {
	private static final long serialVersionUID = 1L;

	@Override
	protected void service(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException {
		
		log.trace("service(req, res) invoked.");
		
		log.info("C Servlet - 요청 잘 받았습니다.");
		log.info("C Servlet - 저는 여기까지만 처리합니다.");
		
		// =====================================
		
		// 비지니스 로직 수행결과 데이터인 Person 객체를 생성하여
		// Request Scope 공유 데이터 영역에 올려 놓는다.
		
		Person person = new Person();
		person.setName("hehehehe");
		person.setAge(23);
		
		req.setAttribute("__MODEL__", person);
		
		// =====================================
		
		req.getRequestDispatcher("/WEB-INF/views/response.jsp").forward(req, res);
		
	} // service

} // end class

 

[ 4 - 4. jsp 파일 생성 ] (***)

 

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>response</title>
</head>
<body>

    <h1>response.jsp</h1>
    <hr>

    <h2>모델 데이터 출력</h2>

    <ul>
        <li>${__MODEL__.name}</li>
        <li>${__MODEL__.age}</li>
    </ul>

</body>
</html>

 

 

 

728x90