Spring Framework/Servlet

[Servlet] 5. Cookie & Session

hyomee2 2024. 9. 2. 00:53

Cookie & Session 개요

1. HTTP 통신

(1) HTTP 통신 방법

- HTTP: 서버, 클라이언트 간의 요청과 응답으로 데이터를 주고받는 형식

- 서버는 클라이언트의 요청에 응답을 완료하면 연결을 끊는다. 즉, 한 번의 요청, 응답이 끝나면 연결을 끊는다.(stateless)

  -> 클라이언트는 요청을 또 보내고 싶으면 새로 연결해야 한다.

 

(2) HTTP 통신의 특징

1) 무연결

- HTTP는 요청할 때 잠깐, 응답할 때 잠깐만 연결하는 무연결의 특징을 갖는다.

- 기본적으로 HTTP는 TCP 프로토콜(=연결지향 프로토콜로 요청 시 서버가 수락해야 송수신 가능)에

  특화 기능을 추가해서 사용하는 것이다.

2) 무상태

- 서블릿 컨테이너 내에는 여러 개의 서블릿이 있는데,

  각각의 서블릿에서 상태값(속성, 변수값)을 다른 서블릿에서 공유해 쓸 수 없음을 의미한다.

 

(3) HTTP 통신의 문제점

- 연결이 끊어지면서 유지되어야 하는 정보가 사라지는 문제가 발생한다. 

  ex) 로그인 후의 로그인 계정 정보, 장바구니에 넣은 데이터 등

  ㄴ> 정보를 기억하고 싶기 때문에 해결책으로 나온게 쿠키와 세션이다. (쿠키는 클라이언트 측, 세션은 서버 측에 데이터를 보관)


Cookie

1. Cookie란?

: 클라이언트 측, 즉 사용자 컴퓨터에 데이터를 텍스트 파일 형태로 저장하는 기술로,

필요 시에 저장한 정보를 서버와 공유하여 정보를 유지한다.

- 패키지: jakarta.servlet.http.Cookie

- 서블릿에서 쿠키 정보를 설정해주면, 쿠키를 클라이언트가 가지고 있으면서 다른 서블릿에 요청할 때 담아 보낸다.

- 데이터는 map 형식으로 저장되고, 데이터의 크기, 개수에 제한이 있다.

- Cookie 유지 시간, 유효 디렉토리, 유효 도메인 등을 설정할 수 있다. 

 ㄴ> 즉, 호출(조회)한 데이터를 클라이언트 PC에 가지고 있도록 하는 것으로 cookie에 대한 보관 정보는 클라이언트가 주축이다.

- Cookie는 간단하게 이용할 수 있다는 장점이 있지만,

  공용 PC를 사용하거나 URL에 일부 데이터를 포함하는 경우 보안에 취약하다.

 

2. Cookie 속성 설정하기

1. name = value

- ASCII 문자만 사용 가능하며, 한 번 설정된 쿠키의 name은 수정할 수 없다.

- 쿠키의 이름에는 공백문자와 일부 특수문자(()=,"\?@:;)를 사용할 수 없다.

2. expire = '날짜'

- cookie의 유지 시간으로, 설정이 따로 없으면 브라우저 동작동안 유지한다.

3. path = '경로'

- cookie가 전달되는 서버의 유효 디렉토리를 지정하는 속성이다.

4. domain = '서버정보'

- cookie가 전달되는 유효 서버를 설정한다.

5. secure

- https나 ssl보안 프로토콜로 요청할 때만 서버에 전송한다.

 

3. Cookie 설정 및 전송하기

@WebServlet("/cookie")
public class CookieHandlerServlet extends HttpServlet {

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        String firstName = req.getParameter("firstName");
        String lastName = req.getParameter("lastName");
        System.out.println("firstName: " + firstName + " lastName: " + lastName);

        /*1. 쿠키 생성*/
        Cookie firstNameCookie = new Cookie("firstName", firstName);
        Cookie lastNameCookie = new Cookie("lastName", lastName);

        /*2. 쿠키 만료 시간 설정*/
        firstNameCookie.setMaxAge(60 * 60 * 24);  // 초 단위이며 하루를 만료시간으로 하는 예시
        lastNameCookie.setMaxAge(60 * 60 * 24);
        /*
        <Cookie 설정값 지정하기>
        setMaxAge(int expiry): 유효 시간 설정
        setPath(String url): 경로 설정(서버의 모든 요청이 아닌 특정 경로를 통한 요청으로 cookie를 사용하는 경우)
        setDomain(String domain): cookie 도메인 설정 및 cookie 생성(도메인 외의 도메인 설정 시 사용)
        */

        // 3. 응답 헤더에 쿠키를 담는다.
         resp.addCookie(firstNameCookie);
         resp.addCookie(lastNameCookie);

         // 4. 응답 처리
        resp.sendRedirect("redirect");
    }
}

 

4. 전송한 cookie 활용하기

@WebServlet("/redirect")
public class RedirectServlet extends HttpServlet {

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {

        /* redirect 처리 시 이전 req 정보는 존재하지 않는다. 새로운 req 객체이므로 값이 없어 null이 반환 된다. */
        String firstName = req.getParameter("firstName");
        String lastName = req.getParameter("lastName");

        System.out.println("firstName: " + firstName + ", lastName: " + lastName);

        // 저장 된 쿠키 값을 꺼내서 확인(배열 형태로 읽어온다.)
        Cookie[] cookies = req.getCookies();
        for(Cookie cookie : cookies) {
            if(cookie.getName().equals("firstName")) {
                firstName = cookie.getValue();
            } else if(cookie.getName().equals("lastName")) {
                lastName = cookie.getValue();
            }
        }

        resp.setContentType("text/html");
        PrintWriter out = resp.getWriter();
        out.println("<h1>your first name is " + firstName + " and last name is " + lastName + "</h1>");
        out.flush();
        out.close();
    }
}

 

* 참고

<!--index.jsp-->
<%@ page contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" %>
<!DOCTYPE html>
<html>
<head>
    <title>Cookie</title>
</head>
<body>
<h1>Cookie Handling</h1>
<form action="cookie" method="post">
    <table>
        <tr>
            <td>firstName : </td>
            <td><input type="text" name="firstName"></td>
        </tr>
        <tr>
            <td>lastName : </td>
            <td><input type="text" name="lastName"></td>
        </tr>
        <tr>
            <td colspan="2"><button>전송</button></td>
        </tr>
    </table>
</form>
</body>
</html>

5. cookie 확인하기

Chrome의 경우,

브라우저에서 '개발자도구(F12) -> Application -> Cookies' 에 가면 확인할 수 있다.

저장경로는 'C:\Users\user\AppData\Local\Google\Chrome\UserData\Default\Cache' 이다.


Session

1. Session이란?

: 클라이언트 측에 데이터를 저장하는 cookie와 달리 서버에 데이터를 저장하는 기술로, 클라이언트에는 session ID를 부여한다.

- Cookie의 보안상 단점과 지원하는 브라우저 문제 등으로 인해 상태를 유지해야 하는 메커니즘에 적합하지 않은 경우

session 인스턴스를 이용해서 상태를 유지하는 메커니즘을 제공한다.

- 클라이언트가 요청 시 session ID를 보내면

일치하는 session 정보(객체)를 컨테이너가 생성하여 그 객체의 데이터를 사용할 수 있다.

ㄴ> 즉, 브라우저마다 제공하는 HashMap으로 서버에서 클라이언트에게 제공한다.(session ID가 key, 데이터가 value)

      Session에 값을 넣으면 모든 서블릿에서 해당 session ID로 고유 공간을 찾아 값을 공유해 사용할 수 있다.

- 패키지: jakarta.servlet.http.HttpSession

- 데이터를 서버에서 관리하므로 보안상 안전하고, 브라우저가 종료되면 세션도 함께 소멸한다.

- 만일 클라이언트가 보낸 session ID가 없으면 새로 세션 객체를 생성한다.

 

2. 데이터 상태 저장 영역

- Page scope: 하나의 servlet, 하나의 class에서만 공유 가능

- Request scope: forward에 한정해 공유할 수 있는 범위

- Session scope: redirect 방식에서도 활용할 수 있는 범위

  (ex. 로그인 정보 - 사용자가 사용하는 모든 페이지에서 사용자의 정보를 가지고 있어야 하기 때문)

- Application scope: 브라우저별 정보보다 넓은 범위

 

3. session 이용하기

@WebServlet("/session")
public class SessionHandlerServlet extends HttpServlet {

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {

        /* session 생성하기 - session은 직접 생성할 수 없고,
        request 객체의 getSession()을 이용해 반환받을 수 있다.*/
        HttpSession session = req.getSession();

        /* session은 브라우저 당 한 개의 고유 아이디를 가지고 하나의 인스턴스를 이용한다.
        * 반복 요청(새로고침) 시 동일 session id를 반환한다.
        * 단, 브라우저가 종료 되면 해당 세션이 종료 되므로 그 이후의 요청은 다른 session id를 반환한다.
        * session id는 cookie로 보관되고 있다.*/
        System.out.println(session.getId());

        /*session 유지시간 설정*/
        System.out.println(session.getMaxInactiveInterval());   // 세션의 기본 유지시간은 30분
        session.setMaxInactiveInterval(60 * 10);                // 필요에 따라 늘이거나 줄일 수 있다 (10분)
        System.out.println(session.getMaxInactiveInterval());

        /* session은 redirect 해도 값을 유지할 수 있으므로 request 보다 더 넓은 범위의 공유 영역이라고 할 수 있다. */
        String firstName = req.getParameter("firstName");
        String lastName = req.getParameter("lastName");

        /*session 속성값 설정*/
        session.setAttribute("firstName", firstName);
        session.setAttribute("lastName", lastName);

        resp.sendRedirect("redirect");
    }
}
@WebServlet("/redirect")
public class RedirectServlet extends HttpServlet {

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {

        /* session id를 통해 session 인스턴스를 가져온다. */
        HttpSession session = req.getSession();
        System.out.println("redirect session id : " + session.getId());

        /* 세션 인스턴스에 저장 된 attribute는 다른 요청에서 꺼내서 쓸 수 있다. */
        String firstName = (String) session.getAttribute("firstName");
        String lastName = (String) session.getAttribute("lastName");
        System.out.println(firstName + " " + lastName);

        resp.setContentType("text/html");
        PrintWriter out = resp.getWriter();
        out.println("<h1>your first name is " + firstName + " and last name is " + lastName + "</h1>");
        out.flush();
        out.close();
    }
}

 

@WebServlet("/delete")
public class DeleteSessionDataServlet extends HttpServlet {

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        HttpSession session = request.getSession();
        Enumeration<String> keys = session.getAttributeNames();
        while (keys.hasMoreElements()) {
            String key = keys.nextElement();
            System.out.println(key + " = " + session.getAttribute(key));
        }

        /* session 데이터를 제거하기
        * 1. 설정 만료 시간이 지나면 session 자동 만료
        * 2. removeAttribute()로 속성 제거 (특정값만 지우고 싶을 때)
        * 3. invalidate()로 session의 모든 데이터 제거(session 무효화, 강제 만료)*/

        System.out.println("=================================");
        session.removeAttribute("firstName");
        keys = session.getAttributeNames();
        while (keys.hasMoreElements()) {
            String key = keys.nextElement();
            System.out.println(key + " = " + session.getAttribute(key));
        }

        System.out.println("=================================");
        session.invalidate();
        keys = session.getAttributeNames();
        while(keys.hasMoreElements()){
            String key = keys.nextElement();
            System.out.println(key + " = " + session.getAttribute(key));
        }
    }
}

 

* 참고

<!--index.jsp-->
<%@ page contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" %>
<!DOCTYPE html>
<html>
<head>
    <title>Session</title>
</head>
<body>
<h1>Session Handling</h1>
<form action="session" method="post">
    <table>
        <tr>
            <td>firstName : </td>
            <td><input type="text" name="firstName"></td>
        </tr>
        <tr>
            <td>lastName : </td>
            <td><input type="text" name="lastName"></td>
        </tr>
        <tr>
            <td colspan="2"><button>전송</button></td>
        </tr>
    </table>
</form>

<hr>

<a href="delete">세션 데이터 지우기</a>


</body>
</html>

4. Session Method

method 명 내용
setAttribute(String, object) request객체에 전달하고 싶은 값을 String 이름-Object 값으로 설정
getAttribute(String) 매개변수와 동일한 객체의 속성 값 가져옴
getAttributeNames() 객체에 등록되어 있는 모든 속성의 이름만 반환
removeAttribute(String) request객체에 저장된 매개변수와 동일한 속성 값 삭제
getId() SessionID값 가져옴
getCreationTime() Session객체가 생성된 시간 반환 (msec)
getMaxInactiveInterval() client 요청이 없을 때, 서버가 현재 Session을 언제까지 유지할지 초 단위로 반환
(default = 30분)
getLastAccessedTime() client 요청이 마지막으로 시도된 시간 반환 (msec)
isNew() 새로 생성된 Session이면 true, 아니면 false 반환
invalidate() 현재 Session 삭제
setMaxInactiveInterval(int) 객체 유지 시간을 설정하고, 지정 시간 지나면 객체 자동 삭제