서블릿&JSP/뉴렉쳐 서블릿&JSP강의

[서블릿/jsp] 상태 유지

째로스 2023. 6. 18. 11:11

1. 상태 유지 방법

 

2. Application 객체를 이용한 상태 유지 방법

책갈피처럼 서블릿을 이어갈 수 있는 상태 값을 저장시켜주는 공간이다.

예로 A라는 서블릿이 실행되고 B라는 서블릿이 실행되도록 한다.

 

1) 코드

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
	<form action="calc2" method="post">
		<div>
			<label>입력 : </label>
			<input type="text" name="v"/>
		</div>
		<div>
			<input type="submit" name="operator" value="+"/>
			<input type="submit" name="operator" value="-"/>
			<input type="submit" name="operator" value="="/>
		</div>
		<div>
			결과 : 0
		</div>
	</form>

</body>
</html>
@WebServlet("/calc2")
public class Calc2 extends HttpServlet{
	@Override
	protected void service(HttpServletRequest request
			, HttpServletResponse response) throws ServletException, IOException {
		//어플리케이션 저장소에 서블릿 값을 저장
		ServletContext application = request.getServletContext();
		
		//UTF-8 형식으로 전송하는 것
		response.setCharacterEncoding("UTF-8");
		//웹 브라우저에 UTF-8 형식으로 읽으라고 하는 것
		response.setContentType("text/html; charset=UTF-8");
		//웹 브라우저가 보낸 입력 값을 UTF-8 형식으로 읽어오는 것(필터로 적용시킬 것임)
		//request.setCharacterEncoding("UTF-8");
		
		PrintWriter out = response.getWriter();
		
		String v_ = request.getParameter("v");
		String op = request.getParameter("operator");
		
		int v = 0;
		if(!v_.contentEquals(""))	v = Integer.parseInt(v_);
		
		//값 계산
		if(op.equals("=")) {
			int x = (Integer)application.getAttribute("value"); //앞선 서블릿에서의 값
			int y = v;  // 현재 서블릿에서의 값
			String operator = (String)application.getAttribute("op"); // 앞서 입력한 연산자
			
			int result = 0;
			if(operator.equals("+"))
				result = x+y;
			else
				result = x-y;
			
			out.printf("결과 : %d\n",result);
		}
		else //값 저장
		{
			application.setAttribute("value", v);
			application.setAttribute("op", op);
		}
	}
}

서블릿 콘텍스트인 application을 통해 값을 set하거나 get하며 상태를 저장시키고 있다.

 

2) 실행 결과

더하기 버튼을 누르면 흰 페이지로 가게 되는데, 이 때 value와 op가 setAttribute되어 서블릿 컨텍스트에 저장된다.

다시 뒤로 가기 버튼을 누르고 4 입력 후 등호 버튼을 클릭하면

위와 같은 결과가 출력된다.

 

2. Session 객체로 상태 값 저장하기

 

Session : 현재 접속한 사용자를 의미한다. 따라서 현재 접속자마다 세션 공간이 개별적으로 부여된다.

 

Application 객체는 해당 상태를 애플리케이션 전역에서 사용할 수 있음을 의미하고,

Session 객체는 세션 범주 내에서 사용할 수 있음을 의미한다.

@WebServlet("/calc2")
public class Calc2 extends HttpServlet{
	@Override
	protected void service(HttpServletRequest request
			, HttpServletResponse response) throws ServletException, IOException {
		//어플리케이션 저장소에 서블릿 값을 저장
		ServletContext application = request.getServletContext();
		
		//세션을 이용하여 상태유지
		HttpSession session = request.getSession();
		
		//UTF-8 형식으로 전송하는 것
		response.setCharacterEncoding("UTF-8");
		//웹 브라우저에 UTF-8 형식으로 읽으라고 하는 것
		response.setContentType("text/html; charset=UTF-8");
		//웹 브라우저가 보낸 입력 값을 UTF-8 형식으로 읽어오는 것(필터로 적용시킬 것임)
		//request.setCharacterEncoding("UTF-8");
		
		PrintWriter out = response.getWriter();
		
		String v_ = request.getParameter("v");
		String op = request.getParameter("operator");
		
		int v = 0;
		if(!v_.contentEquals(""))	v = Integer.parseInt(v_);
		
		//값 계산
		if(op.equals("=")) {
			int x = (Integer)session.getAttribute("value"); //앞선 서블릿에서의 값
			int y = v;  // 현재 서블릿에서의 값
			String operator = (String)session.getAttribute("op"); // 앞서 입력한 연산자
			
			int result = 0;
			if(operator.equals("+"))
				result = x+y;
			else
				result = x-y;
			
			out.printf("결과 : %d\n",result);
		}
		else //값 저장
		{
			session.setAttribute("value", v);
			session.setAttribute("op", op);
		}
	}
}

 

 

같은 브라우저는 같은 세션으로 인식, 다른 브라우저는 다른 세션으로 인식한다.

하나의 브라우저를 여러개 띄워도 여러 프로세스가 동작하는게 아니라 

하나의 프로세스가 여러 개의 스레드를 실행시키는 것이기 때문이다.

여러 개의 스레드는 자원을 모두 같이 공유하기 떄문에 같은 사용자, 같은 세션으로 인식한다.

 

 

서버는 세션 아이디(SID)를 사용자에게 할당해주고, 캐비넷과 비슷한 개념의 저장소 공간을 할당해준다.

사용자는 해당 저장소 공간을 활용하여 값을 저장시킬 수 있다.

브라우저 종료 시 할당받은 세션아이디 반환한다.

 

 

3. Cookie를 이용해 상태값 유지하기

Cookie : 상태 저장을 서버가 아닌 클라이언트가 하도록 도움

Applicaion Context와 Session은 저장소에 서버에 있었던 것에 반해, Cookie는 저장소에 클라이언트에 있다.

getCookies를 통해 서버가 클라이언트로부터 쿠키들을 받아오고,

addCookie를 통해 서버가 클라이언트에 쿠키를 부여한다.

이를 적용한 코드는 아래와 같다.

@WebServlet("/calc2")
public class Calc2 extends HttpServlet{
	@Override
	protected void service(HttpServletRequest request
			, HttpServletResponse response) throws ServletException, IOException {
		//쿠키들을 배열로 읽어옴
		Cookie[] cookies = request.getCookies();
		
		//UTF-8 형식으로 전송하는 것
		response.setCharacterEncoding("UTF-8");
		//웹 브라우저에 UTF-8 형식으로 읽으라고 하는 것
		response.setContentType("text/html; charset=UTF-8");
		//웹 브라우저가 보낸 입력 값을 UTF-8 형식으로 읽어오는 것(필터로 적용시킬 것임)
		//request.setCharacterEncoding("UTF-8");
		
		PrintWriter out = response.getWriter();
		
		String v_ = request.getParameter("v");
		String op = request.getParameter("operator");
		
		int v = 0;
		if(!v_.contentEquals(""))	v = Integer.parseInt(v_);
		
		//값 계산
		if(op.equals("=")) {
			int x=0;
			
			for(Cookie c : cookies) {
				if(c.getName().equals("value")) {
					x=Integer.parseInt(c.getValue());
					break;
				}
			}
			
			int y = v;  // 현재 서블릿에서의 값
			String operator="";
			
			for(Cookie c : cookies) {
				if(c.getName().equals("op")) {
					operator=c.getValue();
					break;
				}
			}
			
			int result = 0;
			if(operator.equals("+"))
				result = x+y;
			else
				result = x-y;
			
			out.printf("결과 : %d\n",result);
		}
		else //값 저장
		{
			//쿠키를 생성하고 클라이언트에게 전송해준다.
			//쿠키안에는 문자열만 저장시킬 수 있다.
			Cookie valueCookie = new Cookie("value", String.valueOf(v));
			Cookie opCookie = new Cookie("op", op);
			response.addCookie(valueCookie);
			response.addCookie(opCookie);
		}
	}
}

 

- Cookie의 path 옵션

Cookie의 사용 범주를 특정 서블릿 클래스에만 적용하도록 할 수 있다.

코드에서의 사용 방법은 아래와 같다.

@WebServlet("/calc2")
public class Calc2 extends HttpServlet{
	@Override
	protected void service(HttpServletRequest request
			, HttpServletResponse response) throws ServletException, IOException {
		//쿠키들을 배열로 읽어옴
		Cookie[] cookies = request.getCookies();
		
		//UTF-8 형식으로 전송하는 것
		response.setCharacterEncoding("UTF-8");
		//웹 브라우저에 UTF-8 형식으로 읽으라고 하는 것
		response.setContentType("text/html; charset=UTF-8");
		//웹 브라우저가 보낸 입력 값을 UTF-8 형식으로 읽어오는 것(필터로 적용시킬 것임)
		//request.setCharacterEncoding("UTF-8");
		
		PrintWriter out = response.getWriter();
		
		String v_ = request.getParameter("v");
		String op = request.getParameter("operator");
		
		int v = 0;
		if(!v_.contentEquals(""))	v = Integer.parseInt(v_);
		
		//값 계산
		if(op.equals("=")) {
			int x=0;
			
			for(Cookie c : cookies) {
				if(c.getName().equals("value")) {
					x=Integer.parseInt(c.getValue());
					break;
				}
			}
			
			int y = v;  // 현재 서블릿에서의 값
			String operator="";
			
			for(Cookie c : cookies) {
				if(c.getName().equals("op")) {
					operator=c.getValue();
					break;
				}
			}
			
			int result = 0;
			if(operator.equals("+"))
				result = x+y;
			else
				result = x-y;
			
			out.printf("결과 : %d\n",result);
		}
		else //값 저장
		{
			//쿠키를 생성하고 클라이언트에게 전송해준다.
			//쿠키안에는 문자열만 저장시킬 수 있다.
			Cookie valueCookie = new Cookie("value", String.valueOf(v));
			Cookie opCookie = new Cookie("op", op);
			
			valueCookie.setPath("/calc2");
			opCookie.setPath("/calc2");
			
			response.addCookie(valueCookie);
			response.addCookie(opCookie);
		}
	}
}

위 코드에서 valueCookie.setPath와 opCookie.setPath를 /cal2로 설정했다.

따라서 해당 서블릿을 사용할 때만 이 쿠키를 사용할 수 있게 된다.

만약 경로를 지정해주지 않는다면 루트가 자동지정되어 해당 폴더 모든 페이지에 쿠키가 전달된다.

 

- Cookie의 maxAge옵션(생명주기)

Cookie valueCookie = new Cookie("value", String.valueOf(v));
Cookie opCookie = new Cookie("op", op);

valueCookie.setPath("/calc2");
//setMaxAge는 초단위로 아래는 24시간을 의미한다.
valueCookie.setMaxAge(24*60*60);
opCookie.setPath("/calc2");

response.addCookie(valueCookie);
response.addCookie(opCookie);

valueCookie.setMaxAge(24*60*60);을 통해 valueCookie의 생명주기가 24시간이 된다.

setMaxAge 매개변수의 시간 단위는 초이기 때문이다.

opCookie처럼 setMaxAge를 설정해주지 않으면 브라우저가 닫힘과 동시에 쿠키값이 사라진다.

 

반면에 특정 명령을 수행했을 경우 쿠키가 사라지길 바란다면 아래와 같은 코드를 삽입한다.

// 이걸 입력해야 쿠키가 바로 소멸된다.
if(operator!=null && operator.equals("C"))
    expCookie.setMaxAge(0);

 

4. Application / Session / Cookie 의 차이점

 1) Application Context

 2) Session

 3) Cookie

application과 session 방식은 서버 내 메모리를 사용하기 때문에 사용하기 부담스럽다.

따라서 대용량 저장소 및 특정 서블릿에서 잠깐 필요한 저장내용은 Cookie를 사용하는 것이 바람직하다.

또한 보유 기간이 길어야 하는 경우에도 Cookie가 바람직하다.