๐ŸŒฑ Spring MVC (5) PRG (Post-Redirect-Get) ํŒจํ„ด

Kim Dae Hyunยท2021๋…„ 7์›” 9์ผ
2

Spring-MVC

๋ชฉ๋ก ๋ณด๊ธฐ
5/13
post-thumbnail

Github ์†Œ์Šค์ฝ”๋“œ

๐Ÿ”Ž PRG (Post-Redirect-Get)

PRG๋Š” POST ์š”์ฒญ์œผ๋กœ ๋ฆฌ์†Œ์Šค๋ฅผ ๋ณ€๊ฒฝํ•˜๋Š” ๊ฒฝ์šฐ ์žฌ์š”์ฒญ์— ๋Œ€ํ•œ ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•˜๊ธฐ ์œ„ํ•œ ํŒจํ„ด์ž…๋‹ˆ๋‹ค.

ํ•œ ๊ฐ€์ง€ ์ƒํ™ฉ์„ ์˜ˆ์‹œ๋กœ ๋“ค์–ด๋ณผ๊ป˜์š”.

  • ํ•œ ์‚ฌ์šฉ์ž๊ฐ€ ์–ด๋–ค ์›น ์‚ฌ์ดํŠธ์—์„œ ์ƒํ’ˆ์„ ๋“ฑ๋กํ•œ๋‹ค๊ณ  ๊ฐ€์ •
  • ์‚ฌ์šฉ์ž๋Š” ์ƒํ’ˆ ๋“ฑ๋ก ํผ์„ ์ž‘์„ฑํ•˜๊ณ  ๋“ฑ๋ก ๋ฒ„ํŠผ์„ ํด๋ฆญํ•˜์—ฌ ์ •์ƒ์ ์œผ๋กœ ์ƒํ’ˆ์„ ๋“ฑ๋กํ•˜๊ณ  ๋‚œ ํ›„ ์—ฌ๋Ÿฌ๋ฒˆ ์ƒˆ๋กœ๊ณ ์นจ์„ ์ˆ˜ํ–‰
  • ์ดํ›„ ์ž์‹ ์˜ ์ƒํ’ˆ ๋“ฑ๋ก ๋ชฉ๋ก์„ ๋ณด๋‹ˆ ์ˆ˜์‹ญ ๊ฐœ์˜ ์ƒํ’ˆ์ด ๋“ฑ๋ก ๋˜์–ด ์žˆ์Œ... ๐Ÿ˜ฑ

์ด์œ ๋ฅผ ์•Œ์•„๋ณด๊ธฐ ์œ„ํ•ด ์œ„ ์š”์ฒญ์„ ์„œ๋ฒ„ ์ž…์žฅ์—์„œ ์ƒ๊ฐํ•ด๋ณผ๊ป˜์š”.

  • ์‚ฌ์šฉ์ž๋กœ๋ถ€ํ„ฐ ์ƒํ’ˆ ๋“ฑ๋ก ํผ์„ ์š”์ฒญ๋ฐ›์Œ
    • GET /items/new-form (ํผ์„ ๋žœ๋”๋ง)
  • ์‚ฌ์šฉ์ž๋กœ๋ถ€ํ„ฐ ์ƒํ’ˆ ๋“ฑ๋ก ํผ ๋ฐ์ดํ„ฐ๋ฅผ ์ „์†ก๋ฐ›์Œ
    • POST /items
  • ์„œ๋ฒ„๋Š” ์ „์†ก๋ฐ›์€ ๋ฐ์ดํ„ฐ๋ฅผ DB์— ์ €์žฅํ•˜๊ณ  ์‚ฌ์šฉ์ž์—๊ฒŒ ๊ฒฐ๊ณผ ํ™”๋ฉด์œผ๋กœ ์ƒํ’ˆ์˜ ์ƒ์„ธ ํŽ˜์ด์ง€๋ฅผ ๋žœ๋”๋ง
    • ์—ฌ๊ธฐ์„œ ๋ฌธ์ œ๊ฐ€ ๋ฐœ์ƒํ•ฉ๋‹ˆ๋‹ค.
    • ์„œ๋ฒ„๋Š” POST /items ์ด URL์„ ์œ ์ง€ํ•œ ์ฑ„๋กœ View๋งŒ์„ ๋ฟŒ๋ ค์ฃผ์—ˆ๊ธฐ ๋•Œ๋ฌธ์— ํด๋ผ์ด์–ธํŠธ์˜ URL์€ ๋ณ€ํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.
    • ์—ฌ๊ธฐ์„œ ์ƒˆ๋กœ๊ณ ์นจ์„ ํ•œ๋‹ค๋ฉด ๋งˆ์ง€๋ง‰ ์š”์ฒญ URL์ธ POST /items์ด ๋ฐ˜๋ณต์ ์œผ๋กœ ์š”์ฒญ๋˜๋Š” ๊ฒƒ ์ž…๋‹ˆ๋‹ค.
  • ์˜ˆ์‹œ์˜ POST ์š”์ฒญ์ด ๋‹จ์ˆœํ•œ ๋“ฑ๋ก ์š”์ฒญ์ด ์•„๋‹ˆ๊ณ  ๊ฒฐ์ œ์š”์ฒญ์ด๋ผ๊ณ  ์ƒ๊ฐํ•ด๋ด…์‹œ๋‹ค. ์ƒˆ๋กœ๊ณ ์นจ์„ ํ•  ๋•Œ๋งˆ๋‹ค ๊ฒฐ์ œ๊ฐ€ ๋˜๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค.. ๋”์ฐํ•ฉ๋‹ˆ๋‹ค..
  • ์ด ๋ฌธ์ œ๋ฅผ ์กฐ๊ธˆ ์‚ฌ์ „์ ์œผ๋กœ ํ’€์–ด๋ณด๋ฉด POST ์š”์ฒญ์€ ๋ฉฑ๋“ฑ(idempotent)ํ•˜์ง€ ์•Š๊ธฐ ๋•Œ๋ฌธ์— ์žฌ์š”์ฒญ์— ๋Œ€ํ•œ ๋Œ€๋น„๊ฐ€ ๋˜์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ๋ฐ˜๋ฉด GET์˜ ๊ฒฝ์šฐ ๋ฉฑ๋“ฑํ•˜๊ธฐ ๋•Œ๋ฌธ์— ์žฌ์š”์ฒญ์ด ์˜ค๋Š” ๊ฒฝ์šฐ ์—ฌ๋Ÿฌ ๋ฒˆ ์กฐํšŒ๊ฐ€ ๋  ๋ฟ ์„œ๋ฒ„์ธก ๋ฆฌ์†Œ์Šค์— ๋ณ€ํ™”๋ฅผ ์ฃผ์ง€๋Š” ์•Š์ง€์š”.

PRG ํŒจํ„ด์„ ๋„์ž…ํ•˜์—ฌ ์œ„ ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐ

  • ๊ฒฐ์ •์ ์œผ๋กœ ์œ„ ๋ฌธ์ œ๊ฐ€ ๋ฐœ์ƒํ•œ ๊ฒƒ์€ POST ์š”์ฒญ ์ดํ›„ URL ์ •๋ณด๋ฅผ ๋ฐ”๊ฟ”์ฃผ์ง€ ์•Š์•˜๊ธฐ ๋•Œ๋ฌธ์ž…๋‹ˆ๋‹ค. URL ์ •๋ณด๋ฅผ ๋ฐ”๊ฟ”์ค˜์•ผ ํ•ฉ๋‹ˆ๋‹ค.
  • ๋“ฑ๋ก ์ •๋ณด๋ฅผ DB์— ์ €์žฅ ํ›„ Redirect ๋ฅผ ์ด์šฉํ•ด ์ƒํ’ˆ์˜ ์ƒ์„ธ ์ •๋ณด๋ฅผ ๋ณด์—ฌ์ฃผ๋Š” GET /items/{itemId}๋กœ ์š”์ฒญ์„ ์˜ฎ๊ฒจ์ค๋‹ˆ๋‹ค.
  • ์ด๋ ‡๊ฒŒ ๋˜๋ฉด POST ์š”์ฒญ ์ดํ›„ URL์€ /Items/{ItemID}๊ฐ€ ๋˜๊ณ  ํ•ด๋‹น URL์€ GET์œผ๋กœ ๋งคํ•‘๋˜์–ด ์žˆ๊ธฐ ๋•Œ๋ฌธ์— ๋ฉฑ๋“ฑํ•˜์—ฌ ์žฌ์š”์ฒญ์— ๋Œ€ํ•ด ๋ฌธ์ œ๋ฅผ ๋ฐœ์ƒ์‹œํ‚ค์ง€ ์•Š์Šต๋‹ˆ๋‹ค.
  • ๐Ÿš€ POST ์š”์ฒญ์— ๋Œ€ํ•ด ๊ฒฐ๊ณผ๋ฅผ Redirect๋ฅผ ์ด์šฉํ•ด GET ์š”์ฒญ์œผ๋กœ ์˜ฎ๊ฒจ์ฃผ๋Š” ๊ฒƒ ์ž…๋‹ˆ๋‹ค.

๐Ÿ”Ž Spring MVC์—์„œ PRG ํŒจํ„ด ๊ตฌํ˜„

์•„๋ž˜ ์˜ˆ์ œ๋กœ Spring MVC์—์„œ redirect ํ•˜๋Š” ๋ฐฉ๋ฒ•์„ ์•Œ์•„๋ณผ๊ป˜์š”.

  • redirect:/hello ๋ฅผ ๋ฆฌํ„ดํ•˜๊ฒŒ ๋˜๋ฉด /hello ๋กœ GET๋ฐฉ์‹์œผ๋กœ ์š”์ฒญํ•ฉ๋‹ˆ๋‹ค. /test ์—์„œ /hello ๋กœ redirect ํ•˜๋Š” ๊ฒƒ ์ž…๋‹ˆ๋‹ค.
  • ์•„๋ž˜ ์ฝ”๋“œ๋ฅผ ์‹คํ–‰ํ•˜๊ณ  /test๋กœ POST ์š”์ฒญ์‹œ ok๊ฐ€ ๋žœ๋”๋ง ๋œ๋‹ค๋ฉด redirect๊ฐ€ ์ž˜๋œ ๊ฒƒ ์ž…๋‹ˆ๋‹ค.
@Slf4j
@Controller
public class TestController {

    @PostMapping("/test")
    public String test() {
        log.info("POST /test ํ˜ธ์ถœ");
        return "redirect:/hello";
    }
    
    @ResponseBody
    @GetMapping("/hello")
    public String hello() {
        log.info("GET /hello ํ˜ธ์ถœ");
        return "ok";
    }
}

์˜ˆ์ œ๋ฅผ ํ†ตํ•ด Spring mvc๊ฐ€ ์ง€์›ํ•˜๋Š” redirect์˜ ํŽธ์˜๊ธฐ๋Šฅ์„ ์ข€ ๋” ์•Œ์•„๋ณผ๊ป˜์š”.

  • Redirect๋กœ ๋‹ค๋ฅธ URL๋กœ ์ด๋™์‹œ ์š”์ฒญ ํŒŒ๋ผ๋ฏธํ„ฐ๋ฅผ ํ•จ๊ป˜ ๋„˜๊ธฐ๊ณ  ์‹ถ์€ ๊ฒฝ์šฐ์—๋Š” RedirectAttributes ๋ฅผ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค.
    ๋งˆ์น˜ Model์— attribute๋ฅผ ์ถ”๊ฐ€ํ•˜๋Š” ๊ฒƒ์ฒ˜๋Ÿผ ๊ฐ’์„ key-value ์Œ์œผ๋กœ ์ €์žฅํ•˜๊ณ  ํ•„์š”ํ•˜๋‹ค๋ฉด redirect ์‹œ URL์— Path Variable๋กœ ๋„˜๊ฒจ์ค๋‹ˆ๋‹ค.
  • ์•„๋ž˜ ์˜ˆ์ œ๋Š” ์‚ฌ์šฉ์ž๋กœ๋ถ€ํ„ฐ ์ƒํ’ˆ์ •๋ณด๋ฅผ ์ž…๋ ฅ๋ฐ›์•„ DB์— ์ €์žฅ ํ›„ ์‚ฌ์šฉ์ž์—๊ฒŒ ์ƒํ’ˆ์ƒ์„ธ์ •๋ณด ํŽ˜์ด์ง€๋ฅผ ๋ฟŒ๋ ค์ฃผ๋Š” ๊ธฐ๋Šฅ์„ ํ•ฉ๋‹ˆ๋‹ค.
  • ์ƒํ’ˆ์ƒ์„ธ์ •๋ณด ํŽ˜์ด์ง€์˜ URL์€ GET /items/{itemId}์ด๋ฏ€๋กœ redirect์‹œ ์ƒํ’ˆ์˜ ID๋ฅผ ํฌํ•จ์‹œ์ผœ์ฃผ์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.
  • RedirectAttributes์— ์ถ”๊ฐ€ํ•œ ๊ฐ’์€ ๋‘๊ฐ€์ง€ ๋ฐฉ์‹์œผ๋กœ ์‚ฌ์šฉ ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค.
    • ์ฒซ๋ฒˆ์งธ๋Š” ์•„๋ž˜ ์˜ˆ์ œ์™€ ๊ฐ™์ด redirect url์— ํฌํ•จ์‹œํ‚ค๋Š” ๋ฐฉ๋ฒ•์ž…๋‹ˆ๋‹ค.
      itemId๊ฐ€ 10์ด๋ผ๋ฉด redirect url์€ items/10์ด ๋˜๊ฒ ์ง€์š”.
    • ๋‘๋ฒˆ์งธ๋Š” Query-Parameter๋กœ ์ „์†กํ•˜๋Š” ๋ฐฉ์‹์ž…๋‹ˆ๋‹ค.
      ์•„๋ž˜ ์˜ˆ์ œ์—์„œ success๋ฅผ true๋กœ ์„ค์ •ํ•˜์—ฌ RedirectAttributes์— ๋‹ด์•˜์ง€๋งŒ ์•„์ง ์‚ฌ์šฉํ•˜์ง€๋Š” ์•Š์•˜์Šต๋‹ˆ๋‹ค.
      ์ด๋ ‡๊ฒŒ RedirectAttributes์— ๋‹ด์•˜์ง€๋งŒ ์‚ฌ์šฉ๋˜์ง€ ์•Š์€ ๊ฐ’์€ ์ฟผ๋ฆฌ ํŒŒ๋ผ๋ฏธํ„ฐ๋กœ ๋ถ™๊ฒŒ ๋ฉ๋‹ˆ๋‹ค.
      ์•„๋ž˜ ์˜ˆ์ œ์˜ ๊ฒฝ์šฐ /items/10?success=true๊ฐ€ ๋˜๊ฒ ๋„ค์š”.
@PostMapping("/items")
public String addItem(
		@ModelAttribute Item item, 
                RedirectAttributes redirectAttributes) {
    Item savedItem = itemRepository.save(item);
    redirectAttributes.addAttribute("itemId", savedItem.getId());
    redirectAttributes.addAttribute("success",true);
    return "redirect:/items/{itemId}"; 
}

๐Ÿ”Ž Spring MVC์—์„œ PRG ํŒจํ„ด์„ ์ ์šฉํ•˜์ง€ ์•Š์•˜์„ ๋•Œ ๋ฌธ์ œ๊ฐ€ ๋ฐœ์ƒํ•˜๋Š” ์˜ˆ์ œ

์•„๋ž˜ ์˜ˆ์ œ๋Š” ์‚ฌ์šฉ์ž๋กœ๋ถ€ํ„ฐ ์ƒํ’ˆ ๋“ฑ๋ก ์ •๋ณด๋ฅผ ์ž…๋ ฅ๋ฐ›์•„ DB์ €์žฅํ•˜๊ณ  ์ƒํ’ˆ ๋“ฑ๋ก ์ •๋ณด๋ฅผ Model์— ๋‹ด์•„ ๋ฐ”๋กœ View๋ฅผ ๋žœ๋”๋งํ•˜๋Š” ๋ฐฉ์‹์ž…๋‹ˆ๋‹ค.

  • ๋ชจ๋“  ๋™์ž‘์ด ์ •์ƒ์ ์œผ๋กœ ์ด๋ฃจ์–ด์ง€๊ณ  item ์ด๋ผ๋Š” View๊นŒ์ง€ ์ •์ƒ์ ์œผ๋กœ ๋žœ๋”๋ง๋˜์—ˆ๋‹ค๊ณ  ๊ฐ€์ •ํ•ด๋ณผ๊ป˜์š”. ํด๋ผ์ด์–ธํŠธ์˜ ๋ธŒ๋ผ์šฐ์ €์— View๊ฐ€ ๋ฟŒ๋ ค์กŒ์„ ๋•Œ ์–ด๋–ค URL์„ ๊ฐ–๊ณ  ์žˆ์„๊นŒ์š”?
  • POST /items ๋ฅผ ๊ทธ๋Œ€๋กœ ๊ฐ–๊ณ  ์žˆ๋Š” ์ƒํƒœ์ž…๋‹ˆ๋‹ค. ์ฆ‰ ๋งˆ์ง€๋ง‰ ์š”์ฒญ์ด POST /items ์ธ ๊ฒƒ ์ž…๋‹ˆ๋‹ค. ์ด ์ƒํƒœ์—์„œ ์ƒˆ๋กœ๊ณ ์นจ์„ ์ˆ˜ํ–‰ํ•œ๋‹ค๋ฉด ๋งˆ์ง€๋ง‰ URL์ธ POST /items๊ฐ€ ๋˜ ์š”์ฒญ๋  ๊ฒƒ์ž…๋‹ˆ๋‹ค.
@PostMapping("/items")
public String addItem(@ModelAttribute Item item, Model model) {
    Item savedItem = itemRepository.save(item);
    model.addAttribute("item", savedItem); 
    return "item";
}

์ธํ”„๋Ÿฐ ๊น€์˜ํ•œ๋‹˜์˜ ์Šคํ”„๋ง MVC 1ํŽธ ์„ ์ˆ˜๊ฐ•ํ•˜๊ณ  ์ •๋ฆฌํ•œ ๋‚ด์šฉ์ž…๋‹ˆ๋‹ค.

profile
์ข€ ๋” ์ฒœ์ฒœํžˆ ๊นŒ๋จน๊ธฐ ์œ„ํ•ด ๊ธฐ๋กํ•ฉ๋‹ˆ๋‹ค. ๐Ÿง

0๊ฐœ์˜ ๋Œ“๊ธ€