Web에서는 브라우저에 약간의 데이터를 보관할 수 기능으로 "쿠키"를 활용된다. 또한 서버와 연결되어 있는 동안 항상 다양한 데이터를 유지하기 위해 '세션'이라는 기능도 포함되어 있다. 이러한 방법에 대해 설명한다.


쿠키의 기본 조작 및 사용

Web에서는 모든 데이터는 네트워크 건너편에 있다. 예전에는 로컬 환경에 많은 데이터를 둘 수 없었다. HTML5이 되어 여러가지로 저장할 수 있게 되면서 완전히 Web의 모습도 바뀌어 왔다. HTML5가 등장 할 때까지 브라우저에 데이터 등을 저장할 수 있는 기능이라고 하면 '쿠키(cookie)'뿐이었다.

쿠키를 이용하면 브라우저에 약간의 데이터를 저장하고 둘 수 있다. 이 쿠키의 역할은 그것뿐이다. "브라우저와 서버 사이에서 데이터를 주고 받는 정보를 교환할 수 있다"는 점에 있다. 각각의 클라이언트(Web 브라우저)에 따라 데이터를 보관하고 둘 수 있기에 여러가지로 활용을 할 수 있다. 예를 들어, ID를 할당하여 각 클라이언트를 식별하는데 사용할 수 있고, 마지막으로 액세스한 일시 등의 정보를 보관할 수도 있다.

최근 들어, HTML5가 로컬에 각종 정보를 저장할 수 있게 되어 왔지만, 이는 JavaScript를 통해서만 필요한 정보를 추출할 수 없기 때문에, 서버 사이드에서의 이용은 몹시 번거롭다. 또한 HTML5를 지원하지 않는 브라우저도 여전히 있다. 클라이언트와 서버 사이에서 원활하게 교환할 수 있는 "클라이언트 측에 저장할 수 있는 데이터"로는 쿠키의 필요성은 아직도 높다.

그럼 쿠키를 사용하는 방법에 대해 정리해 보자. 쿠키는 "Cookie"라는 클래스로 준비되어 있다. 이것은 다음과 같이 인스턴스를 만든다.

Cookie 변수 = new Cookie(이름, 값);

쿠키는 저장하는 값과 거기에 붙이는 이름이 세트로 되어 있다. 이렇게 하면 여러 값을 이름으로 정리하고 보관할 수 있도록 되어 있다. 그럼 이 쿠키는 어떻게 저장하고 검색할 수 있을까?

쿠키 저장하기

response.addCookie("Cookie");

쿠키 받아오기

Cookie[] 변수 = request.getCookies();

쿠키의 저장은 "response"라는 객체의 메소드를 호출한다. 이는 클라이언트에 대한 응답에 대한 정보를 관리하는 객체이다. 이 "addCookie" 메소드로 저장하는 Cookie 인스턴스를 인수로 지정하여 실행한다.

쿠키를 받아오기는 조금 귀찮다. JSP 기능에는 특정 쿠키만 얻어오는 기능은 없다. 준비되어 있는 것은 객체 request의 "getCookies"라는 메소드뿐이다. 이는 해당 사이트에 저장되어 있는 모든 쿠키를 Cookie 배열로 얻는다. 얻어온 Cookie는 저장되어 있는 이름과 값은 다음과 같이 꺼낼 수 있다.

쿠키 이름을 얻기

String 변수 = "Cookie".getName();

쿠키 값을 얻기

String 변수 = "Cookie".getValue();

따라서 조금 복잡하지만, getCookies에서 모든 쿠키를 얻어와서 반복하여 순차적으로 Cookie를 꺼내 그 이름을 getName에서 체크하는 형태로 필요한 쿠키를 찾아야 한다.

유효 기간을 설정하기

"Cookie".setMaxAge(초);

마지막으로 또 하나, 기억해야 할 것이 이것이다. 이는 쿠키가 저장되는 기간을 설정하는 것이다. 이렇게 하면 해당 쿠키가 언제까지 저장되는지를 설정할 수 있다. 이것으로 설정을하지 않으면 브라우저를 종료하거나 하면 그 시점에서 쿠키가 사라진다.

쿠키를 사용해 본다.

그러면 실제로 쿠키를 사용하여 데이터를 클라이언트에 저장하고 그것을 꺼내는 작업을 해보기로 한다.

여기에서는 간단한 메시지를 로드 횟수를 쿠키에 저장하는 샘플을 생각해 보았다. 아래 예제가 해당 JSP 소스 코드이다.

<%@ page language="java" contentType="text/html; charset=utf-8"
    pageEncoding="utf-8"%>
<%@ page import="java.net.*"%>
<%
    // 엔코딩을 설정해 둔다.
    request.setCharacterEncoding("utf-8");
    response.setCharacterEncoding("utf-8");
    req = request;
    // 입력 필드 값을 얻는다.
    String input = request.getParameter("input");
    if (input == null)
        input = "";
    // messagecount의 쿠리를 얻는다.
    Cookie msg = getCookie("message");
    Cookie count = getCookie("count");
    // msgnull이라면 새로 쿠키를 만든다.
    if (msg == null) {
        input = URLEncoder.encode(input, "utf-8");
        msg = new Cookie("message", input);
        response.addCookie(msg);
    }
    // countnull이라면 새로 쿠키를 만든다.
    if (count == null) {
        count = new Cookie("count", "0");
        response.addCookie(count);
    } else { // null이 아니라면 count의 숫자를 1을 올려서 설정한다.
        String num = count.getValue();
        int n = Integer.parseInt(num);
        n++;
        count = new Cookie("count", String.valueOf(n));
        response.addCookie(count);
    }
    // 입력 필드에 뭔가 적혀 있다면 쿠키를 새로 설정하여 고친다.
    if (!input.equals("")) {
        input = URLEncoder.encode(input, "utf-8");
        msg = new Cookie("message", input);
        response.addCookie(msg);
        count = new Cookie("count", "1");
        response.addCookie(count);
    }
%>
<%!HttpServletRequest req;

    // 지정한 이름의 쿠키를 얻어 오는 메소드 정의
    Cookie getCookie(String s) {
        Cookie[] cookies = req.getCookies();
        Cookie res = null;
        if (cookies != null) {
            for (Cookie c : cookies) {
                if (s.equals(c.getName())) {
                    res = c;
                    break;
                }
            }
        }
        return res;
    }%>
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<title>Sample jsp</title>
<style>
h1 {
    font-size: 16pt;
    background: #AAFFAA;
    padding: 5px;
}
</style>
</head>
<body>
    <h1>Sample jsp page</h1>
    <p>이 페이지는 샘플입니다.</p>
    <p><%=count.getValue() + ": " + URLDecoder.decode(msg.getValue(), "utf-8")%></p>
    <form method="post" action="hello8.jsp">
        <table>
            <tr>
                <td>입력</td>
                <td><input type="text" id="input" name="input"></td>
            </tr>
            <tr>
                <td></td>
                <td><input type="submit" value="송신"></td>
            </tr>
        </table>
    </form>
</body>
</html>

이 JSP 페이지에 액세스하면 입력 필드가 하나의 양식이 나타난다. 여기에 텍스트를 써서 보내면, 그 메시지가 쿠키에 저장되어 액세스할 때 페이지에 표시된다. 여러번 다시 로드해 보면, 보낸 메시지를 제대로 기억되어 페이지에 표시되는 것을 알 수 있다.

또한 새로 고침할 때마다 카운터의 수가 증가한다. 마지막 카운터 수가 쿠키에 저장되어 있으며, 거기에 1 더한 값을 쿠키에 저장하고 다시 값을 업데이트 하도록 되어 있다. 이는 메시지를 전송하여 변경하면 초기화되고 처음부터 다시 카운트한다.

여기에 이름을 지정하고 Cookie를 가져 오는데 getCookie라는 메소드를 정의해 두었다. 이 메소드는 getCookies에서 모든 Cookie의 배열을 가져온다. 반복을 하여 이름을 확인하고 있다. 여기에서 주의해야 하는 것은 "암시 개체(HttpServletRequest)"를 다루는 것이다.

이 getCookie 메소드는 req.getCookies();으로 Cookie 배열을 얻고 있지만,이 req는 암시 개체가 아니다. 사실은 이런 메소드 정의에서 암시 객체는 사용할 수 없다. 암시 객체는 <% %>에 작성된 코드(일반적으로 "스크립트"라고 한다) 내에서만 사용할 수 있다. 따라서 여기에서는 미리 글로벌 변수를 준비해 두어 이에 request를 할당하여 사용하고 있다.

또 다른 하나의 주의할 점은 "쿠키에 저장 텍스트"에 대해서 이다. 쿠키에 저장할 수 있는 것은 일반 아스키 텍스트(영숫자)뿐이다. 한글 등은 그대로 보관 할 수 없다.

그러 한글은 어떻게 보관해야 하나? 이는 "아스키 텍스트로 표현할 수 있는 형태로 인코딩하는"것이다. "URL 인코딩"라는 것으로, 이 형식으로 텍스트를 변환하여 저장하고 꺼낸 다시 디코딩하여 텍스트를 되돌린다.

텍스트를 URL 인코딩

String 변수 = URLEncoder.encode(값, 인코딩 이름);

텍스트를 URL 디코딩

String 변수 = URLDecoder.decode(값, 인코딩 이름);

URLEncoder.encode는 텍스트를 URL 인코딩한 것을 돌려준다. 그리고 URLDecoder.decode는 인코딩된 텍스트를 원본 텍스트에 되돌린 것을 돌려준다. 이것을 이용하여 텍스트를 인코딩하여 쿠키에 저장하고, 쿠키에서 꺼낸 후 다시 디코딩하여 사용하는 방식으로, 한글도 문제없이 쿠키에 보관할 수 있다.

이 밖에 요청과 응답에 각각의 텍스트 인코딩을 설정하는데 다음과 같은 문장을 처음에 실행하고 있다.

request.setCharacterEncoding("utf-8");
response.setCharacterEncoding("utf-8");

인코딩을 제대로 설정해 두는 것으로, 한글의 문자 깨짐 등을 예방하는데 도움이 된다. 이전 폼의 전송 등의 경우에는 하지 않았지만, 한글을 사용하는 경우 이러한 설정을 해두는 편이 좋다. 여기서는 이를 기억해 두도록 하자.




세션의 기본 조작 및 사용

쿠키는 작은 값을 보관하는 데 도움이 되지만, 그 이상의 것은 할 수 없다. 복잡한 값이나 큰 데이터를 보관 해 두는 것은 적합하지 않다. 그러나 액세스하여 각각의 사용자에 대해 어떤 값을 저장해서 두고 싶은 경우는 많다. 예를 들어 장바구니 같은 것이라면, 장바구니에 담은 상품 정보를 보관하고 둘 수 있다면 대단히 도움이 된다.

이렇게 "각 사용자마다 쿠키보다 복잡하고 큰 데이터를 보관"을 하는 경우에 사용되는 것이 "세션(session)"이다. 세션이란 서버와 클라이언트(브라우저) 사이의 연결되어 있는 동안 계속 유지 기능이다. 이 세션에 값을 저장하여 두는 것으로, 각 클라이언트에 데이터를 저장하고 관리할 수 있다.

세션은 "HttpSession"라는 클래스로 준비되어 있다. 이는 사실 인스턴스를 만들거나 할 필요가 없다. 처음부터 "session"이라는 암시 개체가 포함되어 있으며,이를 이용하여 세션을 조작할 수 있도록 되어 있다.

세션 값 저장 및 얻기

이 세션에는 값을 저장하거나 얻어오는 메소드가 준비되어 있다.

값을 저장

session.setAttribute(이름, 값);

값을 얻기

Object 변수 = session.getAttribute(이름);

세션 값은 어트리뷰트(속성)라는 형태로 구성되도록 되어 있다. setAttribute으로 속성을 설정하고 getAttribute로 지정된 속성을 얻어오는 것이다. 다만,이 "속성"이라고 하는 개념은 아무래도 감이 오지 않는 사람도 많을 것이다. 그래서 더 간단하게 "값을 설정하고 얻어오는" 형태의 메소드도 준비되어 있다.

값을 저장하기

session.putValue(이름, 값);

값을 얻기

Object 변수 = session.getValue(이름);

이 두 가지 방법은 사실 "완전히 동일한" 것이다. setAttribute와 putValue는 똑같은 동작을 하고 있으며, getAttribute와 getValue도 마찬가지이다. setAttribute에서 설정한 값을 getValue에서 꺼내는 것도 물론 가능하고, 그 반대도 마찬가지이다. 그래서 어떤거라도 선호하는 것을 사용하면 된다.

세션 접속 시간 및 해제

이 세션은 접속를 시작했을 때 시작해서, 접속해 있는 동안 계속 유지된다. 일정 시간 이상 접속을 하지 않거나 하면 세션도 종료되고 세션에 저장되어 있던 정보도 모두 삭제된다. 브라우저를 종료되었을 경우에는 따로 서버에 통보되지 않기에 실제로는 세션은 살아 있다. 이는 일정 시간 후에 사라지게 된다.

세션의 시작 시간과 경관 시간, 또는 세션 종료 등에 관한 메소드도 HttpSession에 준비되어 있다. 다음에 정리해 둔다.

세션을 시작하는 시간을 얻는다

long 변수 = session.getCreationTime();

마지막 액세스 시간을 얻기

long 변수 = session.getLastAccessedTime();

세션을 해제하기

session.invalidate();

getCreationTime과 getLastAccessedTime는 PC의 기준이 되는 날짜(1970년 1월 1일 자정)부터의 경과 밀리 초 단위로 얻는다. 또한 invalidate는 세션을 무효화하기 위한 것이며, 호출 이후에는 session 메소드 등은 모두 사용할 수 없게된다.

세션 유지시간을 지정

세션의 유지시간은 WAS의 기본 설정값으로 지정이 된다. 임의의 값으로 세션 유지시간을 늘리는 방법에 대해 알아보겠다.

코드상에서 유지시간 지정하기

session.setMaxInactiveInterval(초단위);
  • 세션 유지시간이 1시간인 경우 "3600"을 입력한다.

WEB-INF/web.xml에 유지시간 지정하기

<session-config>
    <session-timeout>분단위</session-timeout>
</session-config>
  • 세션 유지시간이 1시간인 경우 "60"을 입력한다.

위 두가지 방법은 세션에 대해 유지시간을 지정하는 모두 동일한 방식이다. 유지시간을 지정하지 않고 계속 유지 시키려면 0을 입력한다. 세션 유지시간은 해당 세션을 생성한 사용자의 브라우저 요청이 있을때마다 새롭게 세션시간을 갱신하게 된다. 세션 유지시간을 길게 하는것은 보안상 좋지 않으니, 적절하게 이용하자.

세션을 이용해 보기

그럼 실제로 세션을 사용하여 보기로 하자. 앞의 예제와 동일하게 하면 세션을 제대로 알기 힘들기에 이번에는 보낸 메시지를 점점 축적해 나가는 것을 만들어 보자.

아래와 같이 예제를 만들어 보자.

<%@ page language="java" contentType="text/html; charset=utf-8"
    pageEncoding="utf-8"%>
<%@ page import="java.util.*"%>
<%
    request.setCharacterEncoding("utf-8");
    response.setCharacterEncoding("utf-8");

    String flg = request.getParameter("check");
    if (flg != null) {
        session.invalidate();
        session = request.getSession();
    }

    long create = session.getCreationTime();
    long last = session.getLastAccessedTime();
    long time = (last - create) / 1000;
    if (time < 0)
        time = 0;

    String input = request.getParameter("input");
    if (input == null)
        input = "";

    ArrayList<String> msgs = (ArrayList<String>) session.getValue("messages");
    if (msgs == null)
        msgs = new ArrayList<String>();

    if (!input.equals(""))
        msgs.add(0, input);

    session.putValue("messages", msgs);
%>
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<title>Sample jsp</title>
<style>
h1 {
    font-size: 16pt;
    background: #AAFFAA;
    padding: 5px;
}
</style>
</head>
<body>
    <h1>Sample jsp page</h1>
    <p>이 페이지는 샘플입니다.</p>
    <table>
        <form method="post" action="helo.jsp">
            <tr>
                <td>입력</td>
                <td><input type="text" id="input" name="input"></td>
            </tr>
            <tr>
                <td></td>
                <td><input type="checkbox" id="check" name="check"> <label for="check">초기화</label></td>
            </tr>
            <tr>
                <td></td>
                <td><input type="submit" value="송신"></td>
            </tr>
        </form>
    </table>
    <hr>
    <p>(<%=time%> sec.)</p>
    <ol>
        <%
            for (int i = 0; i < msgs.size(); i++) {
        %>
        <li><%=msgs.get(i)%></li>
        <%
            }
        %>
    </ol>
</body>
</html>

액세스하면 입력 필드가 있는 화면이 나타난다. 거기에 뭔가 써서 보내면 그것이 아래에 표시한다. 보낼 때마다 그것이 목록의 맨 위에 추가된다. 보낼 때에 "초기화"의 체크 박스를 ON으로 해두면 세션을 초기화하고 새 메시지를 추가한다.

※ GAE에서 세션 사용 주의 사항

이 세션 이용의 샘플을 Google App Engine에서 동작시킬 경우, 주의해야 것은 "GAE에서 세션하기 위해서는 따로 ON으로 설정해야 한다"는 점이다. 프로젝트에 "appengine-web.xml"파일을 연다. 이는 GAE의 여러 설정을 담은 XML 파일이다. 이 가운데에 <appengine-web-app> 태그가 있다. 이 태그에 최초 설정이 작성되어 있다. 이 태그 내에

<sessions-enabled>true</sessions-enabled>

이러한 태그를 작성한다. 이제 세션을 사용할 수 있게 되었다. 이것을 잊으면 세션 기능은 움직이지 않기 때문에 주의하도록 하자.

이번은 ArrayList를 세션에 저장하고 있다. 메시지가 전송될 때마다 이 ArrayList에 추가된다.

ArrayList<String> msgs = (ArrayList<String>)session.getValue("messages");
if (msgs == null)
    msgs = new ArrayList<String>();

먼저 getValue으로 "messages"로 저장해 둔 ArrayList를 가져온다. 이것이 null 인 경우, 새로운 ArrayList 인스턴스를 만든다.

이렇게 ArrayList가 준비되면 전송된 텍스트를 맨 앞에 add하고 다시 세션에 보관한다.

session.putValue("messages", msgs);

샘플에는 세션을 시작한 이후로 경과 시간(초)을 표시한다. 이는 미리 경과 시간(초)을 계산하여 변수에 담아두고, 그것을 <%= %> 태그로 표시하고 있을 뿐이다.

long create = session.getCreationTime();
long last = session.getLastAccessedTime();
long time = (last - create) / 1000;
if (time < 0)
    time = 0;

getCreationTime과 getLastAccessedTime을 각각 변수로 얻어내어, 마지막 액세스 시간을 시작 시간에서 값을 빼고, 1000으로 나누면 경과한 초수를 알 수 있다. 의외로 간단하다. 마지막으로, "초기화" 체크가 ON일 때의 처리도 보도록 하자.

String flg = request.getParameter("check");
if (flg != null) {
    session.invalidate();
    session = request.getSession();
}

체크 박스는 체크가 ON이면 값이 전송되지만, OFF라면 값 자체가 보내지지 않는다. 따라서 request.getParameter( "check")으로 얻어낸 값이 null이면 OFF, null가 아니면 ON이라고 판단 할 수 있는 것이다.

ON인 경우에는 invalidate에서 세션을 삭제한다. 그 후으로는 다시 세션을 시작하고 전송된 값을 보관 해 두지 않으면 안된다. 그래서 request.getSession()라는 것으로, HttpSession을 다시 가져오고 있다. 암묵 객체인 session은 request의 "getSession"으로 세션을 얻어 올 수 있다.

우선 이것으로 세션 값의 보관 및 세션을 종료한 후에 재시작 등의 기본 조작을 할 수있게 되었다!

+ Recent posts