JSP/Servlet - 포워딩 액션태그, 자바빈, 쿠키 !

jsp:forward 액션태그

클라이언트의 요청을 받아 다른 페이지로 이동시키는 방법은 2가지가 있다.
reponse의 리다이렉트 함수를 사용하는 것 과 dispatcher를 통해 포워딩 시키는 것.


int age = 20;
request.setAttribute("tel", "123-12345");

<jsp:forward>를 사용하면 포워딩 시키는 방법을 더욱 간단히 할 수 있다.

<a href="ex03.jsp?name=admin">jsp:forward</a> 아래의 a태그를 누르면 ex03.jsp로 이동한다 했을 때

ex03.jsp에서 ex03_02.jsp로 포워딩 시킬려면 다음 코딩을 추가해야한다.

<%
String path = "ex03_02.jsp?name=admin";
RequestDispatcher dispatcher = request.getRequestDispatcher(path);
dispatcher.forward(request, response);
%>

위의 3줄 코드를 아래 jsp:forward액션태그 한줄로 요약할 수 있다.
<jsp:forward page="ex03_02.jsp"></jsp:forward>

또한 포워딩될 path로 파라미터를 넘기려면 "ex03_02.jsp?name=admin"형식을 사용했다.

?를 사용해 넘길 수 있지만 jsp:param태그를 사용해 파라미터를 넘길 수 도 있다.

<!-- ex03.jsp -->
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
</head>
<body>
<jsp:forward page="ex03_02.jsp?name=admin">
	<jsp:param value="seoul" name="addr"/>
	<jsp:param value="<%= age %>" name="age"/>
</jsp:forward>
</body>
</html>
<!-- ex03_02.jsp -->
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
</head>
<body>
<h3>name: ${ param.name }</h3>
<h3>addr: ${ param.addr }</h3>
<h3>age: ${ param.age }</h3>
</body>
</html>

<jsp:param/>를 통해 파라미터를 넘길 수 있다.

<jsp:forward>액션태그를 사용해 포워드 하거나 RequestDispatcher클래스를 사용해 포워드 하거나 선택사항 이지만 직관적인 코드를 위해 <jsp:forward>액션태그를 사용하도록 하자.

jsp:useBean 액션태그, 자바빈(Java Beans)

자바빈은 JSP기반의 웹 어플리케이션에서 정보를 표현할 때 사용되는 객체이다.

지금까지 객체 내용을 출력하기 위해 DTO객체를 넘겼는데 DTO에 포함되는 개념이라 생각하면 된다.

즉 자바빈은 DTO의 안쪽 개념

https://kouzie.github.io/jdbc/JDBC.-3일차/#

자바빈 역시 규약이 있는데 다음고 같다.

  • 자바빈 클래스 정의할 때에는 public접근 지정자로 정의해야 한다.
  • 값을 저장하는 필드는 private, 필드에 해당하는 getter, setter메서드 정의가 필요하다.(get,set메서드 이름은 필드명, 첫문자는 대문자로)(중요)
  • 생성자는 기본 생성자만 정의 가능하다.
  • 자바빈은 기본(default)패키지 이외의 특정 패키지에 속해 있어야 한다.

사실 io로 자바빈 객체를 넘길 일이 없기 때문에 Serializable 구현하지 않아도 상관없지만 규약이기 때문에 구현하도록 하자.

굳이 자바빈 객체를 만들어 사용하는 이유는
<jsp:useBean>와 함께 사용하면 규칙적인 반복되는 코딩을 모두 제거 할 수 있다.

아래 form태그를 전송하면 아래와 같이 DTO객체를 생성해서 데이터를 받아야 한다.

<form action="ex06_ok.jsp">
<table border="1">
	<tr>
		<td>아이디</td>
		<td><input type="text" name="id" value="admin"/></td>
	</tr>
	<tr>
		<td>이름</td>
		<td><input type="text" name="name" value="홍길동"/></td>
	</tr>
	<tr>
		<td>비밀번호</td>
		<td><input type="password" name="password" value="1q2w3e4r"/></td>
	</tr>
	<tr>
		<td>이메일</td>
		<td><input type="text" name="email" value="admin@naver.com"/></td>
	</tr>
	<tr>
		<td colspan="2" align="center">
			<input type="reset" />
			<input type="submit" />
		</td>
	</tr>
</table>
</form>

먼저 DTO객체를 정의해보자(DTO를 따로 만드는 이유는 jsp, 서블릿 객체간 원할한 데이터 이동을 위해서…)

public class MemberInfo {
private String id;
private String password;
private String name;
private Date registDate;
private String email;

public String getId() {
	return id;
}
public void setId(String id) {
	this.id = id;
}
...
...
<%
MemberInfo info = new MemberInfo();
info.setId("admin");
info.setName("홍길동");
info.setEmail("admin@naver.com");
info.setPassword("1q2w3e4r");
info.setRegistDate(new Date());
%>

info객체 생성후 set...()메서드로 DTO객체를 초기화 한다.

지금은 필드가 5개 뿐이라 5줄로 끝나지만 만약 필드 개수가 수십개라면 매우 귀찮은 작업이 될 것 이다.

자바빈 객체를 사용하면 이런 규칙적 반복작업을 생략할 수 있다.

먼저 MemberInfo를 자바빈 형식으로 변환해주자.

public class MemberInfo {
private String id;
private String password;
private String name;
private Date registDate;
private String email;

public MemberInfo() {
}
public String getId() {
	return id;
}
public void setId(String id) {
	this.id = id;
}
...
...

사실 자바빈이건 DTO건 별 다를게 없다.

get, set 메서드 정의 패턴만 주의하여 작성하도록 하자.
또한 기본생성자의 유무는 상관없지만 그외의 생성자는 정의하면 오류난다.

이제 jsp:useBean액션태그를 DTO객체의 초기화 과정을 생략해보자.

<jsp:useBean id="info" class="days08.MemberInfo" scope="page"></jsp:useBean>
<jsp:setProperty property="*" name="info"/>

두줄로 끝났다!
필드 수에 비례하여 자바빈의 효율성 또한 증가한다.

주의 사항: class명 입력시 패키지명 까지 포함된 full class name을 작성해야 한다.

property="*" 이 속성으로 모든 필드를 초기화 하는게 수상하다….
사실 자바빈을 사용하려면 input태그에서 넘어오는 파라미터의 name값이 자바빈 객체의 필드명과 일치해야 한다.

처음 정의만 잘 해준다면 액션태그를 사용해서 직관적인 코드를 작성 가능하다.

<jsp:setProperty>으로 필드값들을 설정했으면
<jsp:getProperty>으로 설정된 필드값을 출력할 수 있다.

아이디: <jsp:getProperty property="id" name="info"/><br>
이름: <jsp:getProperty property="name" name="info"/><br>
비밀번호: <jsp:getProperty property="password" name="info"/><br>
이메일: <jsp:getProperty property="email" name="info"/><br>
등록일: <jsp:getProperty property="registDate" name="info"/><br>

DTO객체는 서블릿 객체간 포워딩을 할 수 있어야 하는데 request.setAttribute()메서드로는 <jsp:useBean>액션태그로 초기화된 자바빈 객체를 포워딩 시킬수 없다.

<jsp:useBean id="" class="" scope="request">
scope속성을 page에서 request로 변경해주자.

page속성값은 해당 페이지에서만 유지되지만 request속성값을 사용하면 클라이언트의 요청이 반환될 때 까지 자바빈 객체가 유지된다.

포워딩 하는쪽과 포워딩 받는 객체 모두 아래와 같이 <jsp:useBean>액션태그를 정의하자.

<jsp:useBean id="info" class="days08.MemberInfo" scope="request"></jsp:useBean>

스프링 프레임웤 같은 MVC패턴 웹 어플리케이션을 만들게 되면서 jsp:useBean액션 태그 사용률이 감소하게 된다.
클래스에선 액션태그를 사용할 수 없음으로 requset.getParameter같은 메서드와 EL을 사용하게 되면서 자연스럽게 자바빈을 비롯한 관련 액션태그의 사용빈도가 줄게 되었다.

JSP와 쿠키

Javascript에서 쿠키 만들 때에는 클라이언트에서 쿠키를 생성하고 저장하고 다 했다.

https://kouzie.github.io/html/WEB-JavaScript-8일차/#쿠키

JSP에선 쿠키를 만들라는 명령과 쿠키에 저장할 데이터만 http의 응답 헤더에 저장해 넘겨주면
클라이언트가 명령에 따라 생성하고 저장한다.

image18

서버사이드 언어인 jsp로 쿠키값을 접근하다 보니 쿠키가 꼭 서버에 저장되듯이 느껴지는데 쿠키는 클라이언트 저장공간에 있다.

쿠키가 요청을 할 때 마다 쿠키를 http헤더에 포함시켜 보내기 때문에 서버에서 접근 가능한것!

<%
String name = "name";
String value = "홍길동";

Cookie cookie = new Cookie(name, URLEncoder.encode(value, "UTF-8"));
response.addCookie(cookie);
%>

<%= cookie.getName() %> : <%= cookie.getValue() %>

출력값
name : %ED%99%8D%EA%B8%B8%EB%8F%99

위의 예제는 클라이언트에게 쿠키가 전달됨과 같이 쿠키값을 출력한 것이다.

javax.servlet.http.Cookie클래스의 생성자는 namenameValue만 초기화 가능하다.

쿠키의 다른속성들인 유효시간, 도메인, 경로가 모두 생략했는데 서버에선 다음과 같이 초기화 된다.

<%= cookie.getDomain() %> <br>
<%= cookie.getPath() %> <br>
<%= cookie.getMaxAge() %> <br>

출력값

null
null
-1 

반면 클라이언트에겍 전달된 쿠키의 값은 다음과 같다.

Name Value Domain Path Expires/Max-Age
name %ED%99%8D%EA%B8%B8%EB%8F%99 localhost /jspPro/days08 N/A

유효시간이 음수일 경우 페이지 닫을 때 쿠키가 사라진다.

도메인은 현재 접속페이지의 도메인인 localhost로 자동 설정,
경로는 현재 contextPage 설정되었다.

쿠키의 도메인 같은 도메인을 사용하는 서버가 여러 개 일 때, 예를 들어 www.naver.com, mail.naver.com, kr.naver.comsetDomain을 아래와 같이 설정하면 모든 도메인에서 쿠키를 사용 가능하다.
cookie.setDomain(".naver.com");

쿠키값 읽기 - request.getCookies()

서버에서 쿠키설정을 response객체를 사용해 응답 헤더에 저장해서 전송하면
클라이언트는 각종작업을 하고 결과값을 쿠키에 담아 request객체를 사용해 요청 요청 헤더에 저장해 전송한다.

서버는 클라이언트로 부터 받은 쿠키를 request.getCookies()메서드를 사용해 읽기, 수정, 삭제가 가능하다.

<%@page import="java.net.URLDecoder"%>
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
</head>
<body>
쿠키 목록 <br>
<%
Cookie[] cookies = request.getCookies();
if(cookies != null && cookies.length > 0)
{
	for(int i=0; i<cookies.length; i++)
	{
		out.print(cookies[i].getName());
		out.print("=");
		out.print(URLDecoder.decode(cookies[i].getValue()));
		out.print("<br>");
	}
}
else
{
%>
	쿠키가 존재하지 않습니다.
<% 		
}
%>
</body>
</html>

출력값

쿠키 목록 
name=홍길동
JSESSIONID=FD665EE30020432B7F0B0940148DAC42

JSESSIONID은 톰켓에서 자동 설정된 쿠키값

쿠키 값 변경 및 쿠키 삭제

이미 존재하는 쿠키값을 변경하거나 삭제하려면 똑같은 이름의 쿠키를 만든후 클라이언트에게 다시 생성하라고 하면 된다.

생성자 혹은 setValue()메서드를 사용해 새로 만들어진 같은 이름의 쿠키값을 requuestaddCookie()하면 된다.

삭제의 경우 setMaxAge(0)을 통해 유효기간을 0으로 설정한다.

어차피 클라이언트에게 명령이 도착하자 마자 쿠키가 삭제될 것임으로 value값은 생략하거나 쓰레기값을 넣어도 상관 없다.

음수는 웹브라우저가 모두 닫힐 때 까지 유지됨으로 0으로 설정하는것을 주의하자.

쿠키 관리를 위한 클래스를 정의

public class Cookies {
	private Map<String, Cookie> cookieMap = new HashMap<>();
	
	public Map<String, Cookie> getCookieMap() 
	{
		return cookieMap;
	}

	public Cookies(HttpServletRequest request)
	{
		//request객체를 사용해 모든 쿠키를 Map콜렉션에 저장
		Cookie[] cookies = request.getCookies();
		if(cookies != null)
		{
			for (int i = 0; i < cookies.length; i++)
				cookieMap.put(cookies[i].getName(), cookies[i]);
		}
	}

	public Cookie getCookie(String name)
	{
		return cookieMap.get(name);
	}
	
	public String getValue(String name) throws IOException 
	{
		Cookie cookie = cookieMap.get(name);
		if(cookie == null)
			return null;
		return URLDecoder.decode(cookie.getValue(), "utf-8");
	}
	
	public boolean exists(String name) //쿠키 존재 유무 반환
	{
		return cookieMap.get(name) != null;
	}

	public static Cookie createCookie(String name, String value) throws IOException 
	{
		// 생성자1
		return new Cookie(name, URLEncoder.encode(value, "utf-8"));
	}
	
	public static Cookie createCookie(String name, String value, String path, int maxAge) throws IOException 
	{
		// 생성자2
		Cookie cookie = new Cookie(name, URLEncoder.encode(value, "utf-8"));
		cookie.setPath(path);
		cookie.setMaxAge(maxAge);
		return cookie;
	}

	public static Cookie createCookie(String name, String value, String domain, String path, int maxAge) throws IOException
	{
		// 생성자3
		Cookie cookie = new Cookie(name, URLEncoder.encode(value, "utf-8"));
		cookie.setDomain(domain);
		cookie.setPath(path);
		cookie.setMaxAge(maxAge);
		return cookie;
	}
}

예제: https://github.com/Kouzie/Kouzie.github.io/tree/master/_posts/JSP/jsp-cookie예제

위처럼 쿠키객체를 별도로 정의해서 사용하는 것 도 가능하지만 EL태그를 사용해서 쿠키를 사용하면 더 간단하다

<%@page import="com.util.Cookies"%>
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%
	Cookie cookie = Cookies.createCookie("user", "ex02");
	response.addCookie(cookie);
	
	cookie = Cookies.createCookie("id", "admin");
	response.addCookie(cookie);
	
	cookie = Cookies.createCookie("name", "park");
	response.addCookie(cookie);
%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>cookie</title>
</head>
<body>
name: ${ cookie.name.value } <br>
id: ${ cookie["id"]["value"] } <br>
name: ${ cookie.user.maxAge } <br>
<c:forEach items="${ cookie }" var="ck">
	<li>${ ck.key } : ${ ck.value.value }</li>
</c:forEach>
</body>
</html>

출력값

name: park 
id: admin 
name: -1
●	name : park
●	JSESSIONID : 531D9890CDAC56E02676B58CA6CD9268
●	id : admin
●	user : ex02

카테고리:

업데이트: