세션(Session) : 추상적인 연결, 계속 유지되는 연결
HTTP protocol (통신규약) :
-이 방법을 많이 사용함 왜? 쿠키차단기능때문에
-WAS server에 Session Scope영역에 저장된다.
Session
클라이언트와의 추상적인 지속적인 연결은 Session scope 공유영역으로 구현한다.
대부분의 WAS는 30분을 기본으로 미사용 Session scope를 파괴한다.
1) 유지시간의 변경
-> 이것은 web.xml 에서 조절가능하다.(서버프로그램의 데이터 사용량을 조절)
-> session.setMaxInactiveInterval(Millisecond) 메소드를 사용
2) 즉시 파괴
-> session.invalidate() 메소드사용
알아본 점
-(0)HTTPSession 객체를 이용 .getSession()를 통한 반환값 이용한다.
-(1)sess.invalidate()메소드로 즉시삭제
-(2)requireNonNull Null일 때 예외가 발생하도록
-(3)request.getSession(false)은 Session 없을경우 생성하지 않고 null을 반환
@WebServlet("/CartSave")
public class CartSaveServlet extends HttpServlet {
private static final long serialVersionUID = 1L;
@Override
protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
log.trace("service invoked");
// -------------------------------
// Step.1 장바구니에 저장할 상품항목을 전송파라미터로 획득
// -------------------------------
request.setCharacterEncoding("utf8");
String product = request.getParameter("product");
log.info("\t + product: {}", product);
// -----------------------------------
// Step.2 장바구니 생성 및 수신된 상품을 장바구니에 추가
// ------------------------------------
HttpSession sess = request.getSession();
@SuppressWarnings("unchecked")
List<String> list = (List<String>) sess.getAttribute("basket");
if(list == null) { // Session 장바구니가 없으면
list = new ArrayList<>(); // 장바구니를 만들고
sess.setAttribute("basket", list); // 그 안에 등록한다.
}// if -else
list.add(product);
log.info("\t + list: {}", list);
// =========================
// 응답화면 생성 및 전송
// =========================
response.setContentType("text/html; charset=utf8");
@Cleanup
PrintWriter out = response.getWriter();
out.println("<h1>장바구니 담기 성공</h1>");
out.println("<a href='/CartBasket'>장바구니 보기</a>");
out.flush();
}// service
}// end class
@SuppressWarnings("unchecked")
@Override
protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
log.trace("service invoked");
List<String> list = null;
try {
HttpSession sess = request.getSession(false); // 현재 브라우저의 대한 세션이 있으면 주고 없으면 만들지마라
log.info("\t+ sess : {} ", sess);
Objects.requireNonNull(sess);
list = (List<String>) sess.getAttribute("basket");
Objects.requireNonNull(list);
list.forEach(log::info);
}catch(Exception e) {
throw new ServletException(e);
}// try- catch
// =============
response.setContentType("text/html; charset=utf8");
@Cleanup
PrintWriter out = response.getWriter();
out.println("<h1>장바구니 내용</h1>");
out.println("<ol>");
list.forEach(s ->{
out.println("\t<li>"+s+"</li>");
}); // foreach
out.println("</ol>");
out.println("<a href='/CartDelete'>장바구니 비우기 </a>");
out.flush();
}// service
}// end class
@WebServlet("/CartDelete")
public class CartDeleteServlet extends HttpServlet {
private static final long serialVersionUID = 1L;
@Override
protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
log.trace("service invoked");
// 장바구니 비우기 : 현재 브라우저의 세션아이디(이름)로
//식별되는 Session Scope 공유영역 파괴! + 세션아이디 무효화
HttpSession sess = request.getSession(false);
try {
Objects.requireNonNull(sess);
sess.invalidate(); // Session ID 무효화 + Session Scope 영역 파괴!
// ===============
// 마지막 응답 페이지는 요청포워딩(Request Forwarding)을 통해,
// 아직 배우지는 않았지만, JSP에서 생성하도록 함
// ===============
RequestDispatcher dispatcher = request.getRequestDispatcher("/WEB-INF/views/cartdelete.jsp");
} catch (Exception e) {
throw new ServletException(e);
}
// =========================
// 응답화면 생성 및 전송
// =========================
response.setContentType("text/html; charset=utf8");
@Cleanup
PrintWriter out = response.getWriter();
out.println("<h1>장바구니 비우기성공</h1>");
out.flush();
}// service
}// end class
-서버가 클라이언트한테 주는 작은 데이터를 의미 브라우저가 관리하는 쿠키파일에 저장하는 형식
-쿠키란 백엔드 서버에서 최초요청을 받았을 때 웹브라우저에 주는 쿠키가 있다. 이름은 JSESIONID , 값은 무작위 문자열로 받는다. 이 값을 세션 ID라고 부른다.
-최초요청의 받은 브라우저와 서버간의 명칭과 약속
*웹브라우저는, 특정 웹사이트 주소로, "최초요청"을 보낸결과
자기의 이름으로 "세션아이디"를 쿠키로 받게 되고, 이를
쿠키파일로 저장(웹사이트 주소마다 다른파일로 저장)- 이후, 두번째 이상 요청부터는, 동일한 브라우저라면 요청을 보낼 때마다, 반드시, 해당 웹사이트 주소로 보관된 쿠키파 이 존재한다면, 이 쿠키파일을 읽어서, 다시 서버로 전송 (요청문서의 헤더(Cookie)에 저장되어 전송됨) - 어느때까지 요청헤더에 쿠키값을 저장해서 전송하는가!? 이 웹브라우저가 죽을 때까지.... 브라우저가 죽으면 왜 파괴되는지 이해하라 브라우저가 파괴되고 다시 시작하면 다른 아이디를 부여받는다. (실험결과: 브라우저에서 페이지 창을 여러개 띄웠는데 다 똑같은 ID를 갖는다.)
-알아본 점
(1)Cookie 객체의 생성자를 이용한다. / new Cookie("이름","값") -> 맵형태로 생성
(2)getCookies() 메소드로 쿠키 객체를 가져온다.
(3)Cookie 객체에는 .getName()과 .getValue를 사용해서 이름과 값을 얻음
(4)setMaxAge(seconds) 메소드로 만료기간을 조정해서 삭제한다.
@WebServlet("/CartSaveCookie")
public class CartSaveCookieServlet extends HttpServlet {
private static final long serialVersionUID = 1L;
@Override
protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
log.trace("service invoked");
// -------------------------------
// Step.1 화면에서 전송한, 장바구니에 저장할 품목 (전송파라미터) 획득
// -------------------------------
request.setCharacterEncoding("utf8");
String product = request.getParameter("product");
log.info("\t + product: {}", product);
// -----------------------------------
// Step.2 요청메시지(Request Message) 의 헤더(이름: Cookie)에 포함되어 있는, 모든 쿠키 획득
// ------------------------------------
Cookie[] cookies = request.getCookies(); // 쿠키타입의 객체를 원소로 하는 객체배열 반환
// -----------------------------------
// Step.3 우리가 직접 새로운 쿠키를 생성하자!!
// ------------------------------------
Cookie cookie = null;
if(cookies == null || cookies.length == 0) { // 위 Step.2에서 얻는 쿠키배열이 무효하다면
cookie = new Cookie("product",product); // 새로운 쿠키객체 생성
} else { // 위 Step.2에서 얻은 쿠키배열이 유효하다면...
cookie = new Cookie("product"+(cookies.length +1), product); // 새로운 쿠키객체 생성
}// if-else
// -----------------------------------
// Step.4 우리가 생성한 쿠키의 만료기간 설정
// ------------------------------------
cookie.setMaxAge(60*60); // in seconds, 1시간동안 유지하라! (어디? 브라우저에서)
// -----------------------------------
// Step.5 응답메시지의 헤더에, 우리가 위 step.3 에서 생성한 쿠키객체를 저장
// ------------------------------------
response.addCookie(cookie);
// =========================
// 응답화면 생성 및 전송
// =========================
response.setContentType("text/html; charset=utf8");
@Cleanup
PrintWriter out = response.getWriter();
out.println("<h1>Product 추가</h1>");
out.println("<a href='/CartBasketCookie'>장바구니 보기</a>");
out.flush();
}// service
}// end class
@WebServlet("/CartBasketCookie")
public class CartBasketCookieServlet extends HttpServlet {
private static final long serialVersionUID = 1L;
@SuppressWarnings("unchecked")
@Override
protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
log.trace("service invoked");
// Step.1
// 나의 모든 장바구니 항목을 담고있는 쿠키목록을 웹브라우저가
// request message의 헤더(이름: Cookie)에 담아서 보내오게 되어있다. (웹브라우저 기본동작)
Cookie[] basket = request.getCookies();
log.info("* basket : {} ", Arrays.toString(basket));
// Step.2 -----------------------
// 응답문서 생성 using Step.1에서 얻은 장바구니 목록을 이용
response.setContentType("text/html; charset=utf8");
@Cleanup
PrintWriter out = response.getWriter();
if(basket ==null) {
out.println("장바구니에 아무것도 없다이");
} else {
for(Cookie cookie : basket) {
String name = cookie.getName();
String value = cookie.getValue();
out.println("<h1>"+name+" : " + value+"</h1>");
}//for
}// if - else
out.println("<a href = 'product.html'>상품선택 페이지</a>");
out.println("<a href = '/CartDeleteCookie'>장바구니 비우기</a>");
out.flush();
}// service
}// end class
@WebServlet("/CartDeleteCookie")
public class CartDeleteCookieServlet extends HttpServlet {
private static final long serialVersionUID = 1L;
// 클라이언트 브라우저에 쿠키파일로 저장되어있는 모든 장바구니 데이터를 삭제
// How? 각 쿠키의 만료 기간을 조작(중요!)
@Override
protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
log.trace("service invoked");
// ============================================
// Step.1 - 웹 브라우저가 보낸 request 메시지의 헤더에 있는 모든 쿠키를 배열로 획득
// ============================================
Cookie[] basket = request.getCookies();
// ============================================
// Step.2 - 각 쿠키의 만료기간을 1초 (0초는 허용안함)으로 바꾸어서 , 바로 만료하게 만든다.
// ============================================
if(basket !=null) { //장바구니가 있다면..
for(Cookie cookie:basket) {
cookie.setMaxAge(1); // in seconds, 만료기간 = 1초로 변경 (즉, 바로 삭제시켜라! 란 의미가된다.)
//1초의 만료기간으로 변경된 쿠킫글을 다시 브라우저로 보내면
// 웹브라우저는 이 사이트 주소에 대해 파일을 보관중이던, 모든 쿠키의 만료기간이 1초로 단축되었기때문에
// 1초후에 모든 쿠키(=작은데이터, 이름 = 값 형태) 파괴시켜버림
response.addCookie(cookie); // 시간만료가 설정된 쿠키를 브라우저로 전송
}//for
}//if
// =========================
// 응답화면 생성 및 전송
// =========================
response.setContentType("text/html; charset=utf8");
@Cleanup
PrintWriter out = response.getWriter();
out.println("<h1>장바구니 비우기성공</h1>");
out.flush();
}// service
}// end class
결과
브라우저 쪽에 쿠키 객체를 저장
쿠키차단 기능을 고려해가면서 브라우저와 서버의 메모리를 적절히 분할해가면서 사용하면 좋을듯 싶다.
Servlet 3.0부터 어노테이션 지원으로 단순하게 업로드 다운로드는 어렵지는 않다.
단 원본파일이 아닌 날짜 별로 폴더안에 구분하고 UUID기반으로 저장해야함
(HTML의 양식태그에서 post방식으로 넘어오는 데이터에 part별로 구분되는 것을 알아야함)