Servlet 세션(Session)으로 상태정보 유지하기
세션(Session)
HTTP 기반으로 동작하는 클라이언트가 서버에 정보를 요청할 때 생성되는 "상태정보"를 세션이라고 합니다. 세션은 HttpSession이라는 인터페이스 객체로 표현되며, HttpSession 객체는 HttpServletRequest의 getSession()이나 getSession(true) 메소드를 이용하여 생성할 수 있습니다.
HttpSession 객체가 생성될 때는 요청을 보내온 클라이언트 정보, 요청 시간 정보 등을 조합한 세션 ID가 부여되며, 이 세션 ID는 클라이언트 측에 쿠키 기술로 저장됩니다. HttpSession 객체는 서버에 생성되며, 클라이언트에는 세션 ID가 쿠키 기술로 저장되어 각 클라이언트에 대해 생성되는 HttpSession 객체를 클라이언트마다 개별적으로 유지 및 관리합니다.
클라이언트마다 개별적으로 생성되어 유지되는 HttpSession 객체는 요청을 보내온 클라이언트와 서버간에 일정 시간(최대 시간은 브라우저가 살아 있는 동안) 동안 각 클라이언트의 상태정보를 서버에 저장하여 유지하고자 하는 목적으로 사용되는 객체입니다. 그리고 클라이언트마다 상태정보를 일정 시간 동안 개별적으로 유지하여 사용하는 기술을 '세션 트래킹(session tracking)'이라고 합니다.
세션 트래킹 기능의 구현 순서는 다음과 같습니다.
1) 클라이언트를 위한 세션을 준비한다. 이전에 이 클라이언트를 위해 생성된 세션이 이미 존재하면 존재하는 세션을 추출하고, 그렇지 않으면 새로 생성한다. 세션이 새로 생성될 때는 고유한 ID가 하나 부여되며, 이 ID는 클라이언트에 쿠키 기술로 저장된다.
2) 유지하고자 하는 정보를 저장할 목적의 객체를 생성하여 세션에 등록한다.
3) 클라이언트가 요청을 전달할 때마다 세션에 등록된 정보를 담고 있는 객체를 추출하여 원하는 기능에 사용한다.
4) 세션이 더 이상 필요없는 시점에서 세션을 삭제한다.
HttpSession 생성
HttpSession 객체를 사용하고자 할 때는 개발자가 수동으로 생성하는 것이 아니라, 메소드를 이용하여 생성하거나 기존의 HttpSession 객체의 주솟값을 추출하여 사용합니다. HttpSession 객체를 얻으려면 HttpServletRequest 객체의 다음 메소드를 사용합니다.
HttpServletRequest의 getSession()
클라이언트가 가지고 있는 세션 ID와 동일한 세션 객체를 찾아서 주솟값을 반환합니다. 만일 세션이 존재하지 않으면 새로운 HttpSession 객체를 생성하여 반환합니다.
HttpServletRequest의 getSession(boolean create)
클라이언트가 가지고 있는 세션 ID와 동일한 세션 객체를 찾아서 주솟값을 반환합니다. 만일 세션이 존재하지 않으면, 매개변수 create의 값이 true인지 false인지에 따라서 다르게 동작합니다. true이면 getSession() 메소드와 마찬가지로 새로운 HttpSession 객체를 생성하여 반환합니다. 그러나 false이면 새로운 HttpSession 객체를 생성하지 않고 null을 반환합니다.
HttpSession 메소드
HttpServletRequest의 getSession() 또는 getSession(boolean) 메소드를 사용하여 HttpSession 객체를 얻어낸 후 다음과 같은 HttpSessio 객체에서 가지고 있는 메소드를 이용하여 클라이언트 단위로 정보를 유지하는 작업을 합니다. 즉 세션 트래킹 기술을 활용합니다.
접근자 & 반환형 | 메소드 | 기능 |
---|---|---|
public Object | getAttribute(String name) | HttpSession 객체에 등록된 정보 중 getAttribute() 메소드의 인자값으로 지정된 데이터의 값을 반환한다. |
public Enumeration | getAttributeNames() | HttpSession 객체에 등록되어 있는 모든 정보의 이름만을 반환한다. |
public String | getId() | HttpSession 객체에 지정된 세션 ID를 반환한다. |
public long | getCreationTime() | HttpSession 객체가 생성된 시간을 밀리초 단위로 반환한다. |
public long | getLastAccessedTime() | 클라이언트 요청이 마지막으로 시도된 시간을 밀리초 단위로 반환한다. |
public int | getMaxInactiveInterval() | 클라이언트의 요청이 없을 때 서버가 현재의 세션을 언제까지 유지할지를 초 단위로 반환한다. 기본 유효 시간은 30분으로 지정되어 있다. |
public void | invalidate() | 현재의 세션을 삭제한다. |
public boolean | isNew() | 서버 측에서 새로운 HttpSession 객체를 생성한 경우에는 true를 반환하고, 기존 세션이 유지되고 있는 경우라면 false를 반환한다. |
public void | setAttribute(String name, Object value) | HttpSession 객체에 name으로 지정된 이름으로 value 값을 등록한다. |
public void | removeAttribute(String name) | HttpSession 객체에서 name으로 지정된 객체를 삭제한다. |
public void | setMaxInactiveInterval(int second) | HttpSession 객체의 유지 시간을 설정한다. 지정된 시간이 지나면 HttpSession 객체는 자동 삭제된다. |
앞에서 살펴본 HttpSession 객체에 관한 메소드를 테스트하는 예제를 작성해보겠습니다. SessionTestServlet이라는 이름으로 새로운 서블릿 파일을 작성합니다.
// SessionTestServlet.java
package com.edu.test;
import java.io.*;
import javax.servlet.*;
import javax.servlet.annotation.*;
import javax.servlet.http.*;
@WebServlet("/sessionTest")
public class SessionTestServlet extends HttpServlet{
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
resp.setContentType("text/html;charset=UTF-8");
PrintWriter out = resp.getWriter();
String param = req.getParameter("p");
String msg = null;
HttpSession session = null;
if(param.equals("create")) {
session = req.getSession();
if(session.isNew()) {
msg = "새로운 세션 객체가 생성됨";
} else {
msg = "기존의 세션 객체가 리턴됨";
}
} else if(param.equals("delete")) {
session = req.getSession(false);
if(session != null) {
session.invalidate();
msg = "세션 객체 삭제 작업 완료";
} else {
msg = "삭제할 세션 존재하지 않음";
}
} else if(param.equals("add")) {
session = req.getSession(true);
session.setAttribute("msg", "메시지입니다");
msg = "세션 객체에 데이터 등록 완료";
} else if(param.equals("get")) {
session = req.getSession(false);
if(session != null) {
String str = (String) session.getAttribute("msg");
msg = str;
} else {
msg = "데이터를 추출할 세션 객체 존재하지 않음";
}
} else if (param.equals("remove")) {
session = req.getSession(false);
if(session != null) {
session.removeAttribute("msg");
msg = "세션 객체 데이터 삭제 완료";
} else {
msg = "데이터를 삭제할 세션 객체 존재하지 않음";
}
} else if (param.equals("replace")) {
session = req.getSession();
session.setAttribute("msg", "새로운 메시지입니다");
msg = "세션 객체에 데이터 등록 완료";
}
out.print("처리 결과: " + msg);
out.close();
}
}
클라이언트 단위 정보 공유
세션 트래킹 기술을 활용해서 구현해야 하는 대표적인 작업이 로그인/로그아웃 기능입니다. HttpSession 객체를 이용하여 로그인과 로그아웃 처리 예제를 작성해보겠습니다.
//logInOut.html
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>회원 인증</title>
</head>
<body>
<form action="logProc" method="post">
ID: <input type="text" name="id"><br>
비밀번호: <input type="password" name="pwd"><br>
<input type="submit" value="로그인">
</form>
<a href="logProc">로그아웃</a>
</body>
</html>
//LogInOutServlet.java
package com.edu.test;
import java.io.*;
import javax.servlet.*;
import javax.servlet.annotation.*;
import javax.servlet.http.*;
@WebServlet("/logProc")
public class LogInOutServlet extends HttpServlet{
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
resp.setContentType("text/html;charset=UTF-8");
PrintWriter out = resp.getWriter();
String id = req.getParameter("id");
String pwd = req.getParameter("pwd");
if(id.isEmpty() || pwd.isEmpty()) {
out.print("ID 또는 비밀번호를 입력해주세요");
return;
}
HttpSession session = req.getSession();
if(session.isNew() || session.getAttribute("id") == null) {
session.setAttribute("id", id);
out.print("로그인을 완료하였습니다.");
} else {
out.print("현재 로그인 상태입니다.");
}
}
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
resp.setContentType("text/html;charset=UTF-8");
PrintWriter out = resp.getWriter();
HttpSession session = req.getSession(false);
if(session != null && session.getAttribute("id") != null) {
session.invalidate();
out.print("로그아웃 작업 완료");
} else {
out.print("현재 로그인 상태가 아님");
}
out.close();
}
}