웹 프로그래밍/Servlet & JSP

Servlet 쿠키(Cookie)로 상태정보 유지하기

kgvovc 2021. 3. 12. 15:52
반응형

쿠키(Cookie)

웹 서비스 중에는 클라이언트 단위로 상태정보를 유지해야 하는 상황이 많습니다. 클라이언트 단위로 상태정보를 유지하게 하려면 쿠키와 세션을 이용합니다.

 

쿠키와 세션은 다음과 같은 기준에 따라 선택하여 사용합니다.

  • 상태정보의 유지 기간이 브라우저가 종료될 때까지인가의 여부
  • 유지하려는 정보의 저장 위치(서버, 클라이언트)
  • 유지하려는 정보가 공개되어도 되는지의 여부

 

 

쿠키와 세션의 차이점

구분 쿠키 세션
저장 위치 클라이언트 서버
저장 데이터 타입 텍스트 객체
저장 데이터 크기 제한 있음 서버에서 수용할 수 있는 만큼

 

 

쿠키 속성

쿠키란, 서버가 클라이언트에 저장하는 정보로서 클라이언트 쪽에 필요한 정보를 저장해놓고 필요할 때 추출하는 것을 지원하는 기술입니다. 클라이언트와의 연결이 끊어져도 클라이언트마다 개별적으로 상태정보를 유지하고자 할 때 쿠키 기술을 활용할 수 있습니다.

 

클라이언트에 저장된 쿠키 정보는 이후 다시 서버에 방문할 때 자동으로 요청정보의 헤더 안에 포함되어 전달됩니다. 쿠키는 name과 value로 구성된 정보로서 사용 목적에 따라 적절한 name과 value를 지정하고, 필요에 따라서는 쿠키의 유지 시간, 유효 디렉터리, 유효 도메인 등의 속성을 함께 지정할 수 있습니다.

 

 

쿠키는 URL 이름과 만료 날짜, 유효 경로 그리고 보안 필드와 같은 여러 항목으로 나누어집니다. 이 정보는 실제로 요청정보의 헤더를 통해 전달되는데 쿠키 설정에 관한 내용을 정리하면 다음과 같습니다.

 

  • 쿠키를 설정할 때는 name=value 형식으로 구성되며, 쿠키 정보 외에도 expire, path, domain 그리고 secure 등 여러 속성을 선택적으로 추가하여 지정할 수 있다.
  • 쿠키의 name은 ASCII 문자만을 사용해야 하며 한 번 설정된 쿠키의 name은 수정할 수 없다.
  • "expire=날짜" 속성은 쿠키의 유지 시간이다. 유지 시간이 없는 쿠키는 쿠키를 설정받은 브라우저가 실행되는 동안만 유효하다.
  • "path=경로" 속성은 클라이언트에 저장된 쿠키가 전달되는 서버의 유효 디렉토리를 지정하는 속성이다. "path=/"은 서버의 모든 디렉토리라는 의미로 해석되어 적용된다. 이 속성이 생략되면 쿠키를 설정하는 파일이 존재하는 디렉토리가 유효 디렉토리가 된다.
  • "domain=서버정보" 속성은 클라이언트에 저장된 쿠키가 전달되는 유효 서버를 지정하는 속성이다. 생략되면 쿠키를 설정하는 서버가 유효 서버가 된다.
  • secure 속성이 설정되면 클라이언트에서 HTTPS나 SSL과 같은 보안 프로토콜로 요청할 때만 서버에 전송된다.

 

 

쿠키 생성

서블릿에서는 쿠키를 설정하고 전송하며 전송된 쿠키를 추출하는 기능을 API에서 지원하고 있어서 간단하게 처리할 수 있습니다.

 

  • 쿠키 생성: Cookie(String name, String value)

    쿠키를 생성하려면 javax.servlet.http.Cookie 객체를 생성합니다.

 

  • 쿠키 유효 시간 설정: setMaxAge(int expiry)

    클라이언트로 전송되는 쿠키의 유효 시간을 설정할 때는 Cookie 객체의 setMaxAge() 메소드를 사용합니다. 인자값으로 정수를 지정하며, 이 값은 Cookie의 유효 시간의 초(second)를 의미합니다.

    만약 정숫값을 0으로 지정하면 쿠키 삭제를 의미하고, 음수값을 지정하면 쿠키가 클라이언트로 전송된 후 브라우저가 종료되면 쿠키도 자동으로 삭제됩니다. 개발 시 setMaxAge() 메소드로 유효 시간을 지정하지 않은 쿠키도 음수값이 적용되어 브라우저가 전송받은 후 브라우저가 종료되면 쿠키도 함께 삭제됩니다.

 

  • 쿠키 경로 설정: setPath(String uri)

    현재 접속 중인 서버에서 이전에 클라이언트에게 전송한 쿠키가 있으면 기본적으로 요청정보 헤더 안에 쿠키가 포함되어 서버 쪽으로 전송됩니다. 서버의 모든 요청에 대하여 쿠키가 서버 쪽으로 전송되는 것이 아니라, 특정 경로의 요청에서만 쿠키를 전송하고자 할 때 setPath() 메소드를 사용하여 경로를 지정할 수 있습니다. setPath() 메소드의 인자값으로 경로를 지정하면, 지정된 경로와 그것의 하위 경로의 요청에 대해서만 클라이언트로부터 쿠키가 전송됩니다.

 

  • 쿠키 도메인 설정: setDomain(String domain)

    쿠키는 기본적으로 전송된 서버에서만 읽어 들여 사용할 수 있습니다. 그런데 어떤 웹 서비스는 하나의 서버에서만 전체 서비스를 하는 것이 아니라, 여러 대의 서버가 연결되어 서비스를 처리합니다. 이러한 웹 서비스에서는 쿠키의 도메인 설정을 통해 하나의 서버에서 클라이언트로 전송된 쿠키를 다른 서버에서 읽어 들여 사용할 수 있습니다. 이때 사용하는 메소드가 setDomain()입니다.

    setDomain() 메소드의 인자 값으로 서버의 도메인을 지정하는데 "www.edu.com"처럼 지정하면 정확히 일치하는 도메인에서만 쿠키를 읽어 들일 수 있고, ".edu.com"처럼 지정하면 it.edu.com이나 math.edu.com처럼 ".edu.com"이 포함된 모든 도메인 서버에서 쿠키를 읽어 들일 수 있습니다.

 

  • 쿠키 전송: addCookie(Cookie cookie)

    생성된 쿠키를 클라이언트로 보낼 때는 HttpServletResponse 객체의 addCookie() 메소드를 이용합니다. addCookie() 메소드의 인자값으로 전송할 Cookie 객체를 설정하면 쿠키에 설정된 내용으로 클라이언트 쪽으로 쿠키가 전송됩니다.

 

 

이제 앞에서 살펴본 메소드들을 사용해서 쿠키를 생성하여 클라이언트로 전송하는 예제를 작성해 보겠습니다. CookieTest1Servlet이라는 이름으로 새로운 서블릿 파일을 작성합니다.

 

//CookieTest1Servlet.java
package com.edu.test;

import java.io.*;
import javax.servlet.*;
import javax.servlet.annotation.*;
import javax.servlet.http.*;

@WebServlet("/cookie1")
public class CookieTest1Servlet extends HttpServlet{

	@Override
	protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
		resp.setContentType("text/html;charset=UTF-8");
		PrintWriter out = resp.getWriter();
		
		Cookie c1 = new Cookie("id", "guest");
		c1.setPath("/");
		resp.addCookie(c1);
		
		Cookie c2 = new Cookie("code", "0001");
		c2.setMaxAge(60*60*3);  // 3 hours
		c2.setPath("/"); 
		resp.addCookie(c2);
		
		Cookie c3 = new Cookie("subject", "java");
		c3.setMaxAge(60*60*24*10);  // 10 days
		c3.setPath("/");
		resp.addCookie(c3);
		
		out.print("쿠키 전송 완료");
		out.close();
		
	}
}

 

 

쿠키 추출

 

  • 쿠키 추출 : Cookie[] getCookies()

    클라이언트로 전송된 쿠키를 서버 쪽에서 읽어 들이려면 HttpServletRequest 객체의 getCookies() 메소드를 이용합니다.

 

  • 쿠키 검색: String getName()

    HttpServletRequest 객체의 getCookies() 메소드는 서버가 전송한 쿠키를 한꺼번에 읽어 들여 반환하므로 반환된 쿠키 중에서 원하는 쿠키를 찾는 작업을 해야 합니다. 쿠키를 검색할 때는 쿠키의 이름을 가지고 검색하며, 쿠키의 이름만을 추출할 때는 Cookie 객체의 getName() 메소드를 사용합니다.

 

  • 쿠키 값 추출: String getValue()

    읽어 들인 쿠키 중에서 원하는 쿠키를 이름으로 검색하여 찾은 다음에는 쿠키의 값을 추출하여 사용해야 합니다. 쿠키의 값을 추출할 때는 Cookie 객체의 getValue() 메소드를 사용합니다.

 

이제 앞에서 살펴본 메소드들을 이용하여 앞에서 클라이언트에 보낸 쿠키들을 읽어 들인 후 쿠키 이름과 쿠키 값을 추출하여 출력하는 예제를 작성해보겠습니다. CookieTest2Servlet이라는 이름의 새로운 서블릿을 작성합니다.

 

//CookieTest2Servlet.java

package com.edu.test;

import java.io.*;
import javax.servlet.*;
import javax.servlet.annotation.*;
import javax.servlet.http.*;

@WebServlet("/cookie2")
public class CookieTest2Servlet extends HttpServlet{

	@Override
	protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
		resp.setContentType("text/html;charset=UTF-8");
		PrintWriter out = resp.getWriter();
		
		Cookie[] list = req.getCookies();
		for(int i = 0; list != null && i < list.length; i++) {
			out.println(list[i].getName() + ": " + list[i].getValue());
		}
		out.close();
	}
}

 

앞에서 3개의 쿠키를 전송할 때 id=guest 쿠키는 유효 시간을 설정하지 않고 전송하였습니다. 유효 시간을 설정하지 않으면 브라우저가 가지고 있다가 브라우저가 종료될 때 삭제됩니다. 따라서 브라우저를 종료한 다음, cookie2를 다시 실행하면 id=guest 쿠키가 삭제되었음을 확인할 수 있습니다.

 

 

클라이언트 단위 정보 공유

쿠키를 이용하여 클라이언트가 현재 사이트에 몇 번째 방문인지, 즉 방문 횟수를 기록 및 출력하는 예제를 작성해 보겠습니다. CookieTest3Servlet이라는 이름의 새로운 서블릿을 생성합니다.

 

//CookieTest3Servlet.java

package com.edu.test;

import java.io.*;
import javax.servlet.*;
import javax.servlet.annotation.*;
import javax.servlet.http.*;

@WebServlet("/cookie3")
public class CookieTest3Servlet extends HttpServlet {

	@Override
	protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
		int cnt = 0;
		Cookie[] list = req.getCookies();
		for(int i = 0; list != null && i < list.length; i++) {
			if(list[i].getName().equals("count")) {
				cnt = Integer.parseInt(list[i].getValue());
				break;
			}
		}
		
		cnt++;
		Cookie c = new Cookie("count", cnt + "");
		c.setMaxAge(60 * 60 * 24 * 10);
		resp.addCookie(c);
		
		resp.setContentType("text/html;charset=UTF-8");
		PrintWriter out = resp.getWriter();
		out.print("<h1>방문 횟수: " +cnt);
		out.close();
	}
}
반응형