Forward & Redirect
웹에서 제공하는 현재 작업중인 페이지에서 다른페이지로 이동하는 2가지 페이지 전환기능
- 가장 큰 차이 : 사용자에게(웹브라우저에서)
URL 변경
되어 보이는지
Forward
- WAS의 Servlet or JSP가 요청받아 처리하다가,
추가적인 처리를 같은 웹 어플리케이션 안에 있는 다른 Servlet or JSP에 위임하여 처리- 주로 Servlet으로 request 요청을 보내고 응답처리를 JSP로 데이터를 가공하여 만듦
- Web Container 차원에서 페이지의 이동만 존재, 웹 브라우저는 다른 페이지로 이동했음을 알 수 없음
=> 웹 브라우저에는최초 호출한 URL만
표시(이동한 페이지의 URL 정보는 확인할 수 없음) -
현재 페이지와 forward에 의해 호출될 페이지는 Request 객체와 Response 객체를 공유
= 현재 페이지에서 이동할 URL로사용자가 최초로 요청한 요청정보
를 그대로 전달함- ex) 게시글 작성 - forward 사용하여 응답 페이지 부르면 문제 발생
사용자가 보낸 요청 정보로 글쓰기 기능 수행
-> 만약 글쓰기 응답 페이지에서 새로고침
-> 요청 정보가 그대로 살아있어 요청이 여러 번 전달되어 동일한 게시물이 여러 번 등록될 수 있음
=> forward로 응답하는 것은 시스템에 변화가 생기지 않는 단순 조회 요청(READ)의 경우 바람직
ex) 글 목록 보기, 검색 등 - ex) 게시글 작성 - forward 사용하여 응답 페이지 부르면 문제 발생
forward 동작과정
Client(웹 브라우저) -> Server(Servlet1)
: 요청을 보냄Servlet1
: 요청처리 후,결과를 HttpServletRequest에 저장
Servlet1 -> (같은 웹 어플리케이션 안에 있는) Servlet2
결과가 저장된 HttpServletRequest와 응답을 위한 HttpServletResponse 전송(forward)
=request, response 객체가 한번만
생성- Servlet2 : 받은 HttpServletRequest와 HttpServletResponse를 이용하여 요청처리 후 웹 브라우저에게 결과 전송
Redirect
http프로토콜로 정해진 규칙
- Server가 Client로부터 요청 받은 후, Client에게
특정 URL로 이동
하라고 요청- 웹 브라우저(Client)는 URL을 지시된 주소로 바꾸고 해당 주소로 이동
-
새로운 페이지에서 Request와 Response객체가
새롭게 생성
(처음 보냈던 최초의 Request와 Response 객체는 유효하지 않음)- ex) 게시글 작성 - redirect를 사용하여 응답 페이지 부르기
사용자가 보낸 요청 정보로 글쓰기 기능 수행
-> 글쓰기 응답 페이지에서 새로고침
-> 처음의 요청 정보는 존재하지 않으므로 게시물이 여러 번 등록되지 않음
=> redirection 사용은
시스템에 변화가 생기는 요청
(CREATE, UPDATE, DELETE)의 경우 바람직
ex) 회원가입, 글쓰기 등 - ex) 게시글 작성 - redirect를 사용하여 응답 페이지 부르기
redirect 동작과정
Client -> Server(Servlet1)
: 요청 받음Servlet1 -> Client
헤더(Location)에상태코드 302 + redirect 할 URL
포함하여 반환
같은 서버, 다른 서버에 있는 페이지 모두 가능Client
1) 서버로부터 받은 상태 값이 302이면(= 리다이렉션 응답을 받게 되면)Location에 포함된 URL로 재요청
2) 이때 브라우저의 주소창은 새 URL로 변경 + Request와 Response객체 새롭게 생성
= request, response 객체 여러번 생성
3) 서블릿이나 JSP는 redirect하기 위해 HttpServletResponse 클래스의 sendRedirect() 사용
Forward 실습
- FrontServlet, NextServlet이란 두 개의 서블릿을 작성
- localhost:8080/firstweb/front를 요청하면 FrontServlet이 실행
- FrontServlet에서는 랜덤한 주사위 값을 구하고, 그 값을 NextServlet에게 forward
- NextServlet에서는 FrontServlet으로부터 전달 받은 주사위 값만큼 “hello” 출력
@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;
request.setAttribute("dice", diceValue);
RequestDispatcher requestDispatehcer = request.getRequestDispatcher("/next");
requestDispatehcer.forward(request, response);
}
}
- NextServlet에게 request, response 객체 전달 :
RequestDispatcher
라는 객체를 이용해 전달HttpServletRequest
의getRequestDispatcher()
: 포워딩을 수행할 서블릿 지정
-> 인자 값으로 포워딩을 수행할 서블릿의 url 입력- RequestDispatcher의
forward()
: 전달할request와 response
를 인자로 받음
@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>");
//전달받은 request에서 dice 정보 get
int dice = (Integer)request.getAttribute("dice");
out.println("dice : " + dice);
for(int i = 0; i < dice; i++) {
out.print("<br>hello");
}
out.println("</body>");
out.println("</html>");
}
}
결과
Spring에서 Forward 사용
- 구매상세 페이지 : 시스템에 변화가 생기지 않는 단순 조회 요청
-> forward return "viewname";
- 앞에 / 가 붙든 안붙든 상관없이 view/ (SpringBoot에서는 yml의 view 설정에 따라) 다음으로 감
- view 밑으로 폴더 명부터 적어주기
@Controller
@RequestMapping("/api/v1/purchase/*")
@RequiredArgsConstructor
public class PurchaseController {
private final PurchaseService purchaseService;
private final UserService userService;
//구매상세
@GetMapping("purchase/{purchaseNo}")
public String getPurchase(@PathVariable int purchaseNo, Purchase purchase,
Model model) throws Exception {
purchase = purchaseService.getPurchase(purchaseNo);
model.addAttribute(purchase);
return "purchase/getPurchase";
}
}
Spring에서 Redirect 사용
- 결제 후 구매상세 페이지로 이동 : 요청 정보가 그대로 살아있으면 요청이 여러 번 전달되어 여러 번 구매될 수 있음
-> redirect 이용해서 구매상세 메서드로 재요청 return "redirect:/";
- 컨트롤러로 재 요청하는 주소
@Controller
@RequestMapping("/api/v1/purchase/*")
@RequiredArgsConstructor
public class PurchaseController {
private final PurchaseService purchaseService;
//결제
@PostMapping("payment")
public String insertPurchase(Purchase purchase, HttpSession session,
Model model) throws Exception {
purchase = purchaseService.insertPurchase(purchase);
model.addAttribute(purchase);
return "redirect:/api/v1/purchase/purchase/" + purchase.getPurchaseNo();
}
}
Reference
https://mangkyu.tistory.com/51?category=762469
https://goodncuteman.tistory.com/58
https://bellog.tistory.com/80
https://bellog.tistory.com/79?category=911042
https://velog.io/@100bona/Spring-Redirect-vs-Forward
http://jonggs.blogspot.com/2018/11/spring-redirect-forward.html