PRG 패턴(Post - Redirect - Get)

  • 웹 개발 패턴 중 자주 쓰이는 패턴
  • POST 요청에 대한 응답을 또 다른 URL로의 GET 요청을 한다는 것을 의미한다.

GET과 POST는 HTTP Method를 검색하면 더 자세한 내용들이 나옵니다.

 

PRG 중에서 이번 글의 목적은 R인 Redirect입니다.

 

redirect는 유사한 방식으로 forward가 있습니다.

  • redirect
    • 새로운 요청이 발생하는 것이므로 HttpServletRequest 객체는 소멸 후 새롭게 생성되며 HttpSession 객체는 그대로 유지된다.
    • 뒤로 가기 가능( A -> B로 리다이렉트 시 B -> A 이동 )
    • 시스템(session, DB)에 변화가 생기는 요청
    • URL이 변경된다.
  • forward
    • HttpServletRequest,  HttpSession 객체를 공유, 기존 호출에 이어서 진행
    • 뒤로 가기 불가(A -> B로 포워딩 시 뒤로 가기로 A를 부른 다른 URL로 이동)
    • 단순 조회(리스트, 검색)
    • URL의 변경이 없다.
    • forward는 이번 내용과 큰 상관이 없어서 다른 설명은 생략하겠습니다.
그래서 우리는 왜 PRG 패턴을 사용할까?

 

쉽게 말하면, PRG 패턴은 POST 방식으로 온 요청에 대해 GET 방식의 웹페이지로 리다이렉트 시키는 패턴을 말합니다.

 

이 방식을 사용해서 POST 방식으로 전달된 데이터를 redirect 과정을 통해 관련 데이터를 남기지 않기 위함입니다.

 

더 정확히 말하면 새로고침으로 인한 문제를 방지하기 위함이라고 생각하시면 됩니다.

 

여기서 새로고침은 웹 브라우저에서 마지막에 서버에 전송한 데이터를 다시 전송하는 것입니다.

 

중요한 점은 Redirect를 이용하는 방식이기 때문에 '뒤로 가기'와는 상관이 없습니다.

 

오로지 새로 고침으로 인한 문제를 해결하기 위함이라고 생각하시면 됩니다.

 

아래는 PRG의 기본 패턴입니다.

//HTML 페이지는 동일하게 사용

//첫 번째 html 페이지 : post

<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8">
  </head>
  <body>
    <form action="/list" method="post">
      <button>글쓰기</button>
    </form>
  </body>
</html>

============================================

//두 번째 html 페이지 : list

<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
</head>
  <body>
    성공
  </body>
</html>

----------------------------------------------

//PRG 미적용

@Controller
class Controller {

  @GetMapping("/post")
  public String post(){
    return "post";
  }

  @PostMapping("/list")
  public String list(){
  // 글쓰기 로직
  return "list";
  }
  
}

//1. post() 실행 : post 페이지 출력(GET)
//2. post 페이지에서 form 데이터 전송(POST)
//3. list() 실행 : list 페이지로 이동(GET)

----------------------------------------------

//PRG 적용

@Controller
class Controller {

  @GetMapping("/post")
  public String post(){
    return "post";
  }

  @PostMapping("/post")
  public String post(){
    // 글쓰기 로직
    return "redirect:/list";
  }

  @GetMapping("/list")
  public String list(){
    return "list";
  }
 
//1. post() 실행 : post 페이지 출력(GET)
//2. post 페이지에서 form 데이터 전송(POST)
//3. list() 찾기 : redirect(REDIRECT)
//4. list() 실행 : list 페이지 출력(GET)
 
//보통 2~4 단계의 영어 첫 글자를 따서 PRG 패턴이라고 한다.
 
}

 

하지만 우리는 PRG 패턴에서 관련 데이터를 더 전달하고 싶습니다.

 

그럴 때 필요한 파라미터를 담아서 전달하기 위해 RedirectAttributes를 사용합니다.

 

이해를 돕자면, GET / POST 방식에서의 @PathVariable 혹은 @RequestParam과 비교하면 됩니다.

 

RedirectAttributes를 사용할 때는 두 가지 방식을 사용할 수 있습니다.

  1. addAttribute
    • GET 방식이며 페이지를 새로고침을 해도 데이터가 유지된다.
    • String으로 변환이 가능한 원시 타입만 넘길 수 있다.
  2. addFlashAttribute
    • POST 방식이며 일회성이라 페이지를 새로고침할 경우 데이터가 소멸한다.
    • 2개 이상을 쓸 경우 맵을 사용해서 한 번에 값을 전달해야 한다.
    • 세션을 이용해 데이터를 저장하고 리다이렉트 후 알아서 삭제해준다.
@Controller
@RequestMapping("/user")
public class HelloController {
  
  @PostMapping("/hello")
  public String addItem(@Valid @ModelAttribute Hello hello,
                        BindingResult bindingResult,
                        RedirectAttributes redirectAttributes) {
  //...                     
  redirectAttributes.addAttribute("name", "hong");
  return "redirect:/[출력할 URL]/{name}";
  }
  
-----------------------------------------------------------------

  @PostMapping("/hello")
  public String addItem(@Valid @ModelAttribute Hello hello,
                        BindingResult bindingResult,
                        RedirectAttributes redirectAttributes) {
  //...
  User user = new User();
  user.setName("hong");
  user.setAge(10);
  
  redirectAttributes.addFlashAttribute("user", user);
  return "redirect:/[출력할 URL]/{user}";
  }
}