[Spring] PRG(Post-Redirect-Get) 패턴이란?

PRG(Post-Redirect-Get)

웹 개발 패턴 중 자주 사용되는 패턴으로 HTTP POST 요청에 대한 응답GET 요청을 위한 URI로 리다이렉트되는 것 입니다.

 

PRG 패턴을 사용하는 이유를 사용하는 이유는 사용하지 않았을 경우 다음과 같은 문제점이 있기 때문입니다.

PRG패턴을 사용하지 않았을 경우 문제점

1. 새로고침으로 인해 동일한 요청을 연속적으로 보내지는 이슈 발생

웹 브라우저의 새로 고침은 마지막에 서버에 전송한 데이터를 다시 전송하게 됩니다.

그래서 만약 결제 같은 중요한 로직을 POST 방식으로 구현을 했다면, 마지막으로 서버에 전송한 데이터가 POST 요청에 대한 응답 결과물이기 때문에

새로고침을 한다면 계속 중복 결제가 되는 심각한 문제점이 발생하게 됩니다.

 

2. POST 요청은 URL을 공유하더라도 다른 사람과 공유 할 수 없다.

Form 형식의 POST 요청을 보낼 경우 파라미터값들이 URL에 포함되지 않기 때문에

특정 파라미터가 필요한 POST 요청인 경우 URL을 복사, 붙여넣기 하더라도  서버에서는 에러페이지만을 내보내게 됩니다.

 

PRG 적용하기

적용하기 전 아래 코드에서 사용하는 각 클래스에 대한 정보는 다음과 같습니다.

 

PRG 적용 전
@Controller
@RequestMapping("/basic/items")
@RequiredArgsConstructor
public class ItemController {
	private final ItemRepository itemRepository;

	@GetMapping("/add")
    public String addForm() {
        return "basic/addForm";
    }
    
    @PostMapping("/add")
	public String addItem(Item item) { // @ModelAttribute 생략됨
        itemRepository.save(item);

        return "basic/item";
    }
}

 

PRG를 적용하기 전의 작동 과정을 그림으로 보면 다음과 같습니다.

 

PRG 적용 후
@Controller
@RequestMapping("/basic/items")
@RequiredArgsConstructor
public class ItemController {
	private final ItemRepository itemRepository;

	@GetMapping("/add")
    public String addForm() {
        return "basic/addForm";
    }
    
    @PostMapping("/add")
    public String addItem(Item item) { // @ModelAttribute 생략됨
        itemRepository.save(item);

        return "redirect:/basic/items/" + item.getId();
    }
}

 

PRG 패턴을 적용하고 난 뒤의 작동 과정을 그림으로 보면 다음과 같습니다.

하지만 여기서 주의할점이 있는데, 위 코드에서 "return "redirect:/basic/items/" + item.getId();"에 문제가 있습니다.

여기서 사용한 방식이 URL에 변수를 더해서 반환을하도록 했는데 이렇게 사용할 경우 한글이나 뛰어쓰기가 있을 경우 URL 인코딩에 문제가 있어 위험합니다.

그래서 이 경우에는 RedirectAttributes 를 사용해 해결 할 수 있습니다.

 

RedirectAttributes이 적용된 코드
@Controller
@RequestMapping("/basic/items")
@RequiredArgsConstructor
public class ItemController {
	private final ItemRepository itemRepository;

	@GetMapping("/add")
    public String addForm() {
        return "basic/addForm";
    }
    
    @PostMapping("/add")
    public String addItem(Item item, RedirectAttributes redirectAttributes) { // @ModelAttribute 생략됨
        Item savedItem = itemRepository.save(item);
        redirectAttributes.addAttribute("itemId", savedItem.getId());

        return "redirect:/basic/items/{itemId}";
    }
}

RedirectAttributes에 넣은 "itemId"값인 savedItem.getId()가 "{itemId}"로 치환이 되게 됩니다.

이 외에도 RedirectAttributes 대한 기능들은 추후에 포스팅하도록 하겠습니다.