부스트코스 강의를 듣고 정리한 내용.
Redirect
- http 프로토콜로 정해진 규칙
- 서버는 클라이언트로부터 요청을 받은 후, 클라이언트에게 특정 URL로 이동하라고 요청할 수 있다. 이를 리다이렉트라고 한다.
- 서버에서는 클라이언트에게 응답으로 302 상태코드와 함께 이동할 URL 정보를 헤더에 담아 전송한다. 클라이언트는 서버로부터 받은 상태값이 302명 Location 헤더값으로 재요청을 보내게 된다. 이때 브라우조의 주소창은 전송받은 URL로 바뀌게 된다.
- 서블릿이나 JSP는 redirect하기 위해 HttpServletResponse가 가지고 있는 sendRedirect() 메소드 사용
웹 브라우저가 redirect01.jsp를 요청하면 redirect02.jsp로 리다이렉팅하는 코드를 작성해보자.
redirect01.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%
response.sendRedirect("redirect02.jsp");
%>
redirect02.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>
<body>
redirect된 페이지입니다.
</body>
</html>
브라우저에서 redirect 확인
크롬 개발자도구에서 Network 탭을 선택한 다음 redirect01.jsp를 실행하면 서버로부터 응답코드로 302를 받는 것을 알 수 있다.
실제 이루어지는 동작은 아래 그림과 같다.
Forward
forward란?
- 웹 브라우저에서 Servlet1에게 요청을 보냄
- Servlet1은 요청을 처리한 후, 그 결과를 HttpServletRequest에 저장
- Servlet1은 결과가 저장된 HttpServletRequest와 응답을 위한 HttpServletResponse를 같은 웹 어플리케이션 안에 있는 Servlet2에게 전송 (forward)
- Servlet2는 Servlet1로부터 받은 HttpServletRequest와 HttpServletResponse를 이용해 요청을 처리한 후 웹 브라우저에게 결과 전송
forward와 redirect를 헷갈리는 경우가 많은데 redirect는 클라이언트가 서버에게 요청을 보냈고, 서버는 일들을 처리한 후 클라이언트에게 새로운 요청할 곳을 알려주는 것이다. 그래서 redirect 후 url의 주소가 바뀌어 있었다.
반면 forward는 클라이언트가 요청을 보냈는데, 서버쪽에서 그 요청에 대해 혼자 처리하는 것이 아니라 다른 누군가에게 처리를 맡기는 것이다. 이때 클라이언트는 서버가 어떻게 처리했는지 알 필요가 없다. 그래서 forward 후에는 url이 바뀌지 않는다.
실제 클라이언트가 서버에게 요청을 하면 WAS가 request와 response 객체를 생성한다. 그리고 request와 response 객체는 서버가 응답을 할 때까지 계속 유지된다. 그래서 forward의 경우에는 한 번만 요청하므로 req와 res 객체가 한 번 만들어지지만, redirect의 경우에는 요청이 여러 번 왔다갔다 하므로 req와 res 객체가 여러 번 생성된다.
그런데 위의 그림에서 Servlet1이 요청을 처리하고, 요청을 처리한 결괏값이 존재한다면 그 결괏값을 Servlet2에게 넘겨줘야 할 텐데 어떻게 넘겨줘야 할까? 그래서 Servlet1도 접근이 가능하고 Servlet2도 접근이 가능한 누군가가 필요하다.
앞서 작성했듯이 forward할 때는 request와 response 객체가 하나만 생성되므로, Servlet1의 결괏값을 request 객체에 저장하면 된다. 이 과정이 “2. Servlet1은 요청을 처리한 후, 그 결과를 HttpServletRequest에 저장”이다.
저장 후, Servlet1은 결과가 저장된 HttpServletRequest와 응답을 위한 Response를 같은 웹 어플리케이션 안에 있는 Servlet2에게 전송한다. Servlet2가 request, response의 레퍼런스 변수를 알지 못한다면 사용할 수 없기 때문에, forward 시 인자로 request와 response를 넘겨준다.
그럼 이제 두 개의 서블릿(FrontServlet, NextServlet)을 작성해보자.
URL이 호출되면 FrontServlet이 실행된다. FrontServlet에서는 랜덤한 주사위 값을 구하고 그 값을 NextServlet에게 forward한다. NextServlet에서는 FrontServlet으로부터 전달받은 주사위값만큼 “hello”를 출력한다.
FrontServlet.java
import java.io.IOException;
import javax.servlet.RequestDispatcher;
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("/front")
public class FrontServlet extends HttpServlet {
private static final long serialVersionUID = 1L;
public FrontServlet() {
super();
}
protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
int diceValue = (int)(Math.random() * 6 + 1);
// 요청된 이름의 Value를 Object로 넘겨줌
request.setAttribute("dice", diceValue);
// RequestDispatcher: 클라이언트로부터 최초에 들어온 요청을 JSP/Servlet 내에서 원하는 자원으로 요청을 넘기는(보내는) 역할을 수행하거나, 특정 자원에 처리를 요청하고 처리 결과를 얻어오는 기능을 수행
RequestDispatcher requestDispatcher = request.getRequestDispatcher("/next"); // forward할 경로는 반드시 '/'로 시작해야 하며 반드시 같은 웹 애플리케이션 내에서만 가능
// forward하기
requestDispatcher.forward(request, response);
}
}
NextServlet.java
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("/next")
public class NextServlet extends HttpServlet {
private static final long serialVersionUID = 1L;
public NextServlet() {
super();
}
protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
response.setContentType("text/html");
PrintWriter out = response.getWriter();
out.println("<html>");
out.println("<head><title>form</title></head>");
out.println("<body>");
// getAttribute는 특정 요소노드 내에 특정 한 속성값을 가져오는 메소드. Object 타입이므로 타입 변환 해줘야 함
int dice = (Integer)request.getAttribute("dice");
out.println("dice: " + dice + "<br>");
for(int i = 0; i < dice; i++) out.print("hello<br>");
out.println("</body>");
out.println("</html>");
}
}
Servlet & JSP 연동
- Servlet은 프로그램 로직이 수행되기에 유리하다(자바니까). IDE 등에서 지원을 좀 더 해준다.
- JSP는 결과를 출력하기에 Servlet보다 유리하다. 필요한 html 문을 그냥 입히면 된다.
- 프로그램 로직 수행은 Servlet에게, 결과 출력은 JSP에서 하는 것이 유리하다.
- Servlet과 JSP의 장단점을 해결하기 위해 Servlet에서 프로그램 로직이 수행되고, 그 결과를 JSP에게 포워딩하는 방법을 사용한다. 이를 Servlet과 JSP 연동이라 한다.
LogicServlet에서 1부터 100까지의 랜덤한 값 두 개와 그들의 합을 구한 후 그 결과를 result.jsp에게 포워딩하는 방법으로 전송해 결과를 출력하는 예제를 작성해보자.
LogicServlet.java
import java.io.IOException;
import javax.servlet.RequestDispatcher;
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("/logic")
public class LogicServlet extends HttpServlet {
private static final long serialVersionUID = 1L;
public LogicServlet() {
super();
}
protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
int v1 = (int)(Math.random() * 100 + 1);
int v2 = (int)(Math.random() * 100 + 1);
int result = v1 + v2;
request.setAttribute("v1", v1);
request.setAttribute("v2", v2);
request.setAttribute("result", result);
RequestDispatcher rd = request.getRequestDispatcher("/result.jsp");
rd.forward(request, response);
}
}
result.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>
<body>
<%
int v1 = (int)request.getAttribute("v1");
int v2 = (int)request.getAttribute("v2");
int result = (int)request.getAttribute("result");
%>
<%=v1 %> + <%=v2 %> = <%=result %>
</body>
</html>
근데 jsp에서 자바 코드를 누구나 쉽게 쓸 수 있도록 할 수는 없을까? 그래서 등장한 것이 EL, JSTL 표기법 등등이다. EL를 사용하면
<%
int v1 = (int)request.getAttribute("v1");
int v2 = (int)request.getAttribute("v2");
int result = (int)request.getAttribute("result");
%>
코드를
${v1} + ${v2] = ${result}
이렇게 간단히 작성할 수 있다.