개발을 하던 도중 다음과 같은 에러를 만났다.
response.sendRedirect("");
response.addCookie(cookie);
//쿠키가 전달되지 않음
response.addCookie(cookie);
response.sendRedirect("");
//쿠키가 제대로 전달됨
sendRedirect와 addCookie 순서에 따라 쿠키가 저장에 영향을 주는지 원인을 찾아보았다.
addCookie()의 메소드는 사실 단순히 Header를 변경해주는 것과 같다.
@Override
public void addCookie(final Cookie cookie) {
// Ignore any call from an included servlet
if (included || isCommitted()) {
return;
}
cookies.add(cookie);
String header = generateCookieString(cookie);
// if we reached here, no exception, cookie is valid
addHeader("Set-Cookie", header, getContext().getCookieProcessor().getCharset());
}
//그래서 addCookie()메소드 대신 이렇게 쓸 수 있다.
response.setHeader("Set-Cookie", "cookieName=cookieValue");
Response의 sendRedirect메소드
public void sendRedirect(String location, int status) throws IOException {
if (isCommitted()) {
throw new IllegalStateException(sm.getString("coyoteResponse.sendRedirect.ise"));
}
// Ignore any call from an included servlet
if (included) {
return;
}
// Clear any data content that has been buffered
resetBuffer(true);
// Generate a temporary redirect to the specified location
try {
Context context = getContext();
// If no ROOT context is defined, the context can be null.
// In this case, the default Tomcat values are assumed, but without
// reference to org.apache.catalina.STRICT_SERVLET_COMPLIANCE.
String locationUri;
// Relative redirects require HTTP/1.1 or later
if (getRequest().getCoyoteRequest().getSupportsRelativeRedirects() &&
(context == null || context.getUseRelativeRedirects())) {
locationUri = location;
} else {
locationUri = toAbsolute(location);
}
setStatus(status);
setHeader("Location", locationUri);
if (context != null && context.getSendRedirectBody()) {
PrintWriter writer = getWriter();
writer.print(sm.getString("coyoteResponse.sendRedirect.note", Escape.htmlElementContent(locationUri)));
flushBuffer();
}
} catch (IllegalArgumentException e) {
log.warn(sm.getString("response.sendRedirectFail", location), e);
setStatus(SC_NOT_FOUND);
}
// Cause the response to be finished (from the application perspective)
setSuspended(true);
}
메소드를 보면 중간에 resetBuffer()를 통해 버퍼를 비운다.
그리고 writer.print( ~~~~~) 메소드를 통해 버퍼를 채운 후
flushBuffer()를 통해 방금 채운 버퍼만 전송한다.
이때 cookie랑 redirect에 관한 내용은 header에 포함되어 있기때문에 버퍼의 내용과
상관없이 전달된다.
문제는 마지막에 setSuspended(true)이다.
이게 true로 세팅되는 순간 response는 끝났다.
알다시피 response.sendRedirecct() 메소드 이후에도 자바코드는 실행되지만
더 이상의 브라우저에 데이터가 보내지지는 않는다.
그래서 처음 문제 상황을 보면 왜 sendRedirect()의 순서가 중요한지 알 수 있다.
response.sendRedirect(""); //이후의 자바코드자체는 실행되지만, response가 더 이상 브라우저에 응답하지는 않음
response.addCookie(cookie);
//쿠키가 전달되지 않음
response.addCookie(cookie);
response.sendRedirect(""); //쿠키 세팅한 상태로 브라우저에 응답. 쿠키가 제대로 전달됨
//쿠키가 제대로 전달됨