관리 메뉴

거니의 velog

(21) 쿠키와 세션 알아보기 1 본문

Java_Servlet

(21) 쿠키와 세션 알아보기 1

Unlimited00 2023. 8. 28. 20:35

1. 웹 페이지 연결 기능

* 보통 웹 프로그램에서 사용되는 정보는 서블릿의 비즈니스 로직 처리 기능을 이용해 데이터베이스에서 가져온다. 그러나 동시 사용자 수가 많아지면 데이터베이스 연동 속도도 영향을 받게 되므로 정보의 종류에 따라 어떤 정보들은 클라이언트 PC나 서버의 메모리에 저장해두고 사용하면 좀 더 프로그램을 빠르게 실행시킬 수 있다. 이번에는 그 방법과 함께 서블릿이 로그인 시 사용자의 로그인 상태를 일정하게 유지시키는 기능에 대해 살펴보자.


(1) 세션 트래킹

* 온라인 쇼핑몰을 이용하다 보면 메인 페이지에서 미리 로그인한 후 다른 웹 페이지에서 상품에 관한 댓글을 달거나 게시판에 상품평을 달곤 한다. 글쓰기창에서는 따로 로그인하지 않아도 된다. 그러나 메인 페이지에서 미리 로그인하지 않고 새 글을 작성하려면 '로그인 후 이용하라'는 메시지가 나타난다. 그러면 사용자는 로그인한 후 글쓰기창으로 이동하게 된다.

* 쇼핑몰을 이용하는 일반 사용자들은 로그인 상태를 각각의 웹 페이지들이 자동적으로 알고 있을 것이라 생각한다. 그러나 실제 HTTP 프로토콜 방식으로 통신하는 웹 페이지들은 서로 어떤 정보도 공유하지 않는다.

* 사용자 입장에서 웹 페이지 사이의 상태나 정보를 공유하려면 프로그래머가 세션 트래킹(Session Tracking)이라는 웹 페이지 연결 기능을 구현해야 한다.

* 다음은 HTTP 프로토콜로 각각의 웹 페이지를 요청해서 클라이언트의 브라우저에 표시해 주는 과정을 나타낸 것이다.

* HTTP 프로토콜은 서버-클라이언트 통신 시 stateless 방식으로 통신한다. 즉, 브라우저에서 새 웹 페이지를 열면 기존의 웹 페이지나 서블릿에 관한 어떤 연결 정보도 새 웹 페이지에서는 알 수 없다.

* 정리하면 HTTP 프로토콜은 각 웹 페이지의 상태나 정보를 다른 페이지들과 공유하지 않는 stateless 방식으로 통신을 한다. 따라서 웹 페이지나 서블릿끼리 상태나 정보를 공유하려면 웹 페이지 연결 기능, 즉 세션 트래킹을 이용해야 한다.

* 웹 페이지를 연동하는 방법은 다음과 같다.

- <hidden> 태그 : HTML의 <hidden> 태그를 이용해 웹 페이지들 사이의 정보를 공유한다.

- URL Rewriting : GET 방식으로 URL 뒤에 정보를 붙여서 다른 페이지로 전송한다.

- 쿠키 : 클라이언트 PC의 Cookie 파일에 정보를 저장한 후 웹 페이지들이 공유한다.

- 세션 : 서버 메모리에 정보를 저장한 후 웹 페이지들이 공유한다.

2. <hidden> 태그와 URL Rewriting 이용해 웹 페이지 연동하기

* <hidden> 태그는 브라우저에는 표시되지 않지만 미리 저장된 정보를 서블릿으로 전송할 수 있다. 지금부터 <hidden> 태그를 이용해 클라이언트의 데이터를 서버에 보내는 예제를 수행해 보자.


(1) <hidden> 태그를 이용한 세션 트래킹 실습

1. 새 프로젝트 pro09를 만들고 sec01.ex01 패키지를 생성한 후 다음과 같이 LoginServlet 클래스 파일과 login.html을 준비한다.

2. login.html을 다음과 같이 작성한다. 로그인창에서 ID와 비밀번호를 입력하면 미리 <hidden> 태그에 저장된 주소, 이메일, 휴대폰 번호를 서블릿으로 전송한다.

<!DOCTYPE html>
<html>
	<head>
		<meta charset="UTF-8" />
		<title>로그인창</title>
	</head>
	<body>
		<form name="frmLogin" method="post" action="login" enctype="UTF-8">
			<label>아이디 : </label>
			<input type="text" name="user_id" />
			<br />
			
			<label>비밀번호 : </label>
			<input type="password" name="user_pw" />
			<br />
			
			<input type="submit" value="로그인" />
			<input type="reset" value="다시 입력" />
			<input type="hidden" name="user_address" value="서울시 성북구" />
			<input type="hidden" name="user_email" value="test@gmail.com" />
			<input type="hidden" name="user_hp" value="010-1111-2222" /> <!-- <hidden> 태그의 value 속성에 주소, 이메일, 전화번호를 저장한 후 서블릿으로 전송합니다. -->
		</form>
	</body>
</html>

3. LoginServlet 클래스를 다음과 같이 작성한다. getParameter() 메서드를 이용해 전송된 회원 정보를 가져온 후 브라우저로 다시 출력한다.

package sec01.ex01;

import java.io.IOException;
import java.io.PrintWriter;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

@WebServlet("/login")
public class LoginServlet extends HttpServlet {
	public void init() {
		System.out.println("init 메서드 호출");
	}

	protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		request.setCharacterEncoding("utf-8");
		response.setContentType("text/html;charset=utf-8");
		PrintWriter out = response.getWriter();
		String user_id = request.getParameter("user_id");
		String user_pw = request.getParameter("user_pw");
		String user_address = request.getParameter("user_address");
		String user_email = request.getParameter("user_email");
		String user_hp = request.getParameter("user_hp"); // <hidden> 태그로 전송된 값을 getParameter() 메서드를 이용해 가져온다.

		String data = "안녕하세요!<br> 로그인하셨습니다.<br><br>";
		data += "<html><body>";
		data += "아이디 : " + user_id;
		data += "<br>";
		data += "비밀번호 : " + user_pw;
		data += "<br>";
		data += "주소 : " + user_address;
		data += "<br>";
		data += "email : " + user_email;
		data += "<br>";
		data += "휴대 전화 : " + user_hp;
		data += "</html></body>";
		out.print(data);
	}

	public void destroy() {
		System.out.println("destroy 메서드 호출");
	}
}

4. http://localhost:8090/pro09/login.html 로 요청하고 ID와 비밀번호를 입력한 후 서블릿으로 전송한다.

5. <hidden> 태그로 전송된 데이터도 출력한다.


(2) URL Rewriting을 이용한 세션 트래킹 연습

* 이번에는 URL Rewriting을 이용해 로그인창에서 입력 받은 ID와 비밀번호를 다른 서블릿으로 전송하여 로그인 상태를 확인해 보자.

1. 새로운 패키지를 만들고 LoginServlet, SecondServlet 클래스 파일을 준비한다.

2. LoginServlet 클래스를 다음과 같이 작성한다. 로그인창에서 입력 받은 ID와 비밀번호를 <a> 태그의 두 번째 서블릿으로 보내기를 클릭하면 로그인창에서 입력한 ID와 비밀번호 그리고 다른 정보들을 GET 방식을 이용해 두 번째 서블릿으로 전송한다.

package sec01.ex02;

import java.io.IOException;
import java.io.PrintWriter;
import java.net.URLEncoder;

import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

@WebServlet("/login")
public class LoginServlet extends HttpServlet {
	
	public void init(ServletConfig config) throws ServletException {
		System.out.println("init 메서드 호출");
	}

	protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		request.setCharacterEncoding("utf-8");
		response.setContentType("text/html;charset=utf-8");
		PrintWriter out = response.getWriter();
		String user_id = request.getParameter("user_id");
		String user_pw = request.getParameter("user_pw");
		String user_address = request.getParameter("user_address");
		String user_email = request.getParameter("user_email");
		String user_hp = request.getParameter("user_hp");

		String data = "안녕하세요!<br> 로그인하셨습니다.<br><br>";
		data += "<html><body>";
		data += "아이디 : " + user_id;
		data += "<br>";
		data += "비밀번호 : " + user_pw;
		data += "<br>";
		data += "주소 : " + user_address;
		data += "<br>";
		data += "email : " + user_email;
		data += "<br>";
		data += "휴대 전화 : " + user_hp;
		data += "<br>";
		out.print(data);

		user_address = URLEncoder.encode(user_address, "utf-8"); // GET 방식으로 한글을 전송하기 위해 인코딩한다.
		out.print("<a href='/pro09/second?user_id=" + user_id 
				             + "&user_pw=" + user_pw 
				             + "&user_address=" + user_address
				             + "'>두 번째 서블릿으로 보내기</a>"); // <a> 태그를 이용해 링크 클릭 시 서블릿 /second로 다시 로그인 정보를 전송한다.
		data = "</body></html>";
		out.print(data);
	}
	
	public void destroy(){
	    System.out.println("destroy 메서드 호출");
	}

}

3. SecondServlet 클래스를 다음과 같이 작성한다. 첫 번째 서블릿에서 전송한 데이터 중 ID와 비밀번호를 가져왔으면 이미 첫 번째 서블릿에서 로그인한 것이므로 로그인 상태를 유지하도록 해준다.

package sec01.ex02;

import java.io.IOException;
import java.io.PrintWriter;

import javax.servlet.Servlet;
import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

@WebServlet("/second")
public class SecondServlet extends HttpServlet {
	
	public void init(ServletConfig config) throws ServletException {
		System.out.println("init 메서드 호출");
	}

	protected void doGet(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException {
		request.setCharacterEncoding("utf-8");
		response.setContentType("text/html;charset=utf-8");
		PrintWriter out = response.getWriter();
		String user_id = request.getParameter("user_id");
		String user_pw = request.getParameter("user_pw");
		String user_address = request.getParameter("user_address"); // 첫 번째 서블릿에서 전송한 로그인 정보를 가져온다.

		out.println("<html><body>");
		if (user_id != null && user_id.length() != 0) {
			out.println("이미 로그인 상태입니다!<br><br>");
			out.println("첫 번째 서블릿에서 넘겨준 아이디: " + user_id + "<br>");
			out.println("첫 번째 서블릿에서 넘겨준 비밀번호: " + user_pw + "<br>");
			out.println("첫 번째 서블릿에서 넘겨준 주소: " + user_address + "<br>");
			out.println("</body></html>"); // 첫 번째 서블릿의 ID 정보를 이용해 로그인 상태를 유지한다.
		} else {
			out.println("로그인 하지 않았습니다.<br><br>");
			out.println("다시 로그입하세요!!<br>");
			out.println("<a href='/pro09/login.html'>로그인창으로 이동하기 </>"); // 로그인 창을 거치지 않고 바로 요청한 경우에는 로그인 창으로 다시 이동하도록 안내한다.
		}
	}
	
	public void destroy() {
		System.out.println("destroy 메서드 호출");
	}

}

4. <hidden> 태그와 URL Rewriting 방식으로 데이터를 전송한 결과를 보면?
    http://localhost:8090/pro09/login.html 로 요청한 후 ID와 비밀번호를 입력하고 첫 번째 서블릿으로 전송한다.

5. 첫 번째 서블릿에서 전달받은 로그인 정보를 출력한 후 두 번째 서블릿으로 보내기를 클릭한다.

6. 두 번째 서블릿에서 현재 로그인 상태와 회원 정보를 출력한다.

7. 만약 브라우저에서 로그인창을 거치지 않고 바로 서블릿 /second를 요청하면 "로그인 하지 않았습니다." 라는 상태 안내 문구와 함께 "로그인 창으로 이동하기"가 표시된다.

- http://localhost:8090/pro09/second

* 지금까지 <hidden> 태그와 GET 방식으로 웹 페이지들을 연동하는 방법을 알아보았다. 그런데 이 방법은 여러 가지 단점이 있다. 일단 웹 페이지가 많아지면 일일이 로그인 상태를 확인하기 위해 로그인 정보를 다른 웹 페이지로 전송해야 한다는 것이다. 그리고 ID와 비밀번호를 GET 방식으로 전송하므로 브라우저에 노출되어 보안상으로도 좋지 않다. 또한 전송할 수 있는 데이터 용량에는 한계가 있다. 따라서 이 방식은 웹 페이지 사이에 간단한 정보 정도를 공유할 때만 사용하는 것이 좋다.