Servlet - Redirect와 Forward

Redirect

출처 : edwith

위의 이미지를 설명하면, 리다이렉트는 클라이언트가 redirect01.jsp를 요청했더니 웹서버로부터 반환된 내용에 리다이렉트 내용이 있어서 웹서버로 다시 요청, 웹서버에서 최종적으로 redirect02.jsp를 반환하는 과정이다.

리다이렉트를 현실에서 비유를 들자면, 휴대폰 관련 상담을 위해 고객상담센터에 연락을 했다고 가정해보자.
휴대폰 보험 관련 문의를 위해 고객상담센터에 전화했더니 센터에서는 해결을 못하니 보험 회사로 직접 연락하라며 번호를 알려주었다. 이 번호로 다시 연락해서 보험 상담원과 연결이 되었다.

이렇게 클라이언트가 처음 요청과 다른 요청을 해서 2번의 요청을 통해 연결되는 상황을 리다이렉트라고 할 수 있다.

결과적으로 브라우저를 통해 페이지 로드가 한 번 되는것처럼 보이지만, 사실은 클라이언트와 웹서버간 통신이 각각 2번씩 이뤄진다는 점 이다. 클라이언트가 웹서버에 2번의 요청을 하는 것이다.

클라이언트 >> 웹서버(redirect) >> 클라이언트(redirect) >> 웹서버 >> 클라이언트

예제코드

RedirectFirst.class

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
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;

/**
* Servlet implementation class redirect01
*/
@WebServlet("/redirect-first")
public class RedirectFirst extends HttpServlet {
private static final long serialVersionUID = 1L;

public redirectFirst() {
super();
}

protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
PrintWriter out = response.getWriter();
out.println("Hello Servlet 01");
response.sendRedirect("/redirect-second");
}
}

RedirectSecond.class

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
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;

/**
* Servlet implementation class redirect01
*/
@WebServlet("/redirect-second")
public class RedirectSecond extends HttpServlet {
private static final long serialVersionUID = 1L;

public redirectSecond() {
super();
}

protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
response.setContentType("text/html");
response.setCharacterEncoding("UTF-8");
PrintWriter out = response.getWriter();

out.println("Hello Servlet 02");
}

}

서블릿 클래스를 생성한 예제 코드이다. /redirect-first 라는 API로 요청이 들어오면, HttpServletResponsesendRedirect()를 이용하여 /redirect-second API로 요청을 리다이렉트하는 코드이다. 결국 화면에 Hello Servlet 02가 출력된다.

HttpServletResponsesendRedirect()에 대해 더 알아보자.

아래 포워드에서 비교해서 알 수 있겠지만, 리다이렉트는 sendRedirect() 함수가 호출되는 시점에 페이지를 바로 이동하는게 아니라 서블릿 클래스 내에 코드가 모두 실행되고 반환될 때, 웹서버의 서블릿 클래스가 클라이언트의 요청을 바꿔서 다시 요청받는거다.

HTTP Header의 Status Code를 301(Permanently) 또는 302(Temporarily)로 바꿔서 반환한다. 그래서 바뀐 Header값으로 클라이언트가 웹서버에 재요청하여 다른 페이지를 반환받는 것이다. 이게 리다이렉트 과정이다.


Forward

출처 : edwith

포워드는 클라이언트가 웹서버에 forward01.jsp를 요청했더니 forward01.jsp에서 작성된 코드에의해 포워딩되어 forward02.jsp를 반환하는걸 의미한다. 결과적으로 위에서 설명한 리다이렉트와 비슷하지만, 클라이언트와 서버간 통신이 각각 1번씩이라는 점에서 다르다.

이번에도 현실에서 비유를 들어보겠다.
고객상담센터에 전화를 걸어서 요금 관련 문의를 했더니, 잠시만 기다려달라고 하더니 저절로 요금 관련 상담원으로 전화연결이 바뀌었다. 전화를 끊고 다시 한것도 아닌데 고객상담센터에서 내 전화를 요금 관련 상담원으로 연결해준 것이다.

이처럼 클라이언트는 한 번의 요청을 했지만, 서버에서 페이지를 바꿔서 반환하는 것을 포워딩이라고 한다.

포워드는 다른 서버와는 할 수 없고, 클라이언트가 처음 요청한 서버내에서 다른 클래스를 전달할 수 있다. 또한 포워딩하는 과정에서 처음 요청받은 서블릿 클래스의 request와 response도 함께 포워딩할 수 있다는 특징이 있다.

클라이언트 >> 웹서버(forward) >> 웹서버(forwared) >> 클라이언트

예제코드

FrontServlet.class

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
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;

/**
* Servlet implementation class FrontServlet
*/
@WebServlet("/front")
public class FrontServlet extends HttpServlet {
private static final long serialVersionUID = 1L;

public FrontServlet() {
super();
}

@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
int randNum = (int)(Math.random() * 6) +1;
req.setAttribute("rand", randNum);

RequestDispatcher requestDispatcher = req.getRequestDispatcher("/forward");
requestDispatcher.forward(req, resp);
}
}

BackServlet.class

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
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;

/**
* Servlet implementation class BackServlet
*/
@WebServlet("/forward")
public class BackServlet extends HttpServlet {
private static final long serialVersionUID = 1L;

public BackServlet() {
super();
}

protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
response.setContentType("text/html");
response.setCharacterEncoding("UTF-8");
PrintWriter out = response.getWriter();

out.println("<html>");
out.println("<head><title>form</title></head>");
out.println("<body>");

int rand = (int)request.getAttribute("rand");
out.println("랜덤 수 : "+rand);

out.println("</body>");
out.println("</html>");
}
}

/front 로 요청이 들어오면, FrontServlet.class 에서 랜덤 숫자를 생성했다. 이렇게 생성한 숫자를 포워딩할 클래스에 보내려면, HttpServletRequestsetAttribute()RequestDispatcherforward()를 사용해야한다.

1
2
int randNum = (int)(Math.random() * 6) +1;
req.setAttribute("rand", randNum);

생성된 랜덤 숫자를 “rand”라는 이름으로 attribute에 넣는다. 이렇게 저장된 attribute를 포워딩하는 서블릿 클래스로 보낼때 사용하는 객체가 RequestDispatcher이다.

1
2
RequestDispatcher requestDispatcher = req.getRequestDispatcher("/forward");
requestDispatcher.forward(req, resp);

/forward 로 요청을 포워딩하는 RequestDispatcher를 생성하고, RequestDispatcherforward() 파라미터에 request와 response를 넣으면, 포워딩되는 클래스에 request와 response를 함께 보내준다. 이 request 객체에 attribute가 있기 때문에 RequestDispatcher.forward() 파라미터에 request와 response를 주입하는 것이다.

1
2
3
4
5
6
response.setContentType("text/html");
response.setCharacterEncoding("UTF-8");
PrintWriter out = response.getWriter();

int rand = (int)request.getAttribute("rand");
out.println("랜덤 수 : "+rand);

그럼 포워딩되는 서블릿 클래스에서 API 요청을 받아서 랜덤숫자가 저장된 attribute를 화면에 출력하는 코드이다.

RequestDispatcher 객체는 HttpServletRequest에서 제공하는 객체며, forward()include() 2개의 메서드를 가지고 있다.

출처 : 생활코딩 - JSP/서블릿 훑어보기


Reference