본문 바로가기

WEB

PRG 패턴 (Post-Redirect-Get) - 삽질중인 개발자

반응형

최근 발생한 이슈 중 중복 결제에 대한 이슈가 있어서 원인을 찾아보는 중에 브라우저에서 새로고침이나 뒤로 가기를 했을 경우 이전에 보내진 POST 요청이 다시 보내져 중복 결제가 일어나는 버그가 있다는 것을 발견했다.

 

이러한 새로고침 버그에 대해서 회피할 수 있는 방법을 찾아보는 중 PRG 패턴에 대해서 알게 되어 정리 차원에서 포스팅을 한다.

PRG 패턴 (Post-Redirect-Get)

웹 개발 패턴 중 자주 쓰이는 패턴으로 HTTP POST 요청에 대한 응답이 또 다른 URL로의 GET 요청을 위한 리다이렉트(응답 코드가 3XX) 여야 한다는 것을 의미한다. 
즉, 쉽게 설명하면 POST 방식으로 온 요청에 대해서 GET 방식의 웹페이지로 리다이렉트 시키는 패턴을 말한다.

 

PRG 패턴을 사용하지 않으면 발생하는 문제점

첫번째로 새로고침으로 인한 동일한 요청이 연속적으로 보내지는 이슈가 발생한다는 것이다. 만약 이 이슈가 중요하지 않은 로직이 있는 부분이라면 상관없겠지만 결제 같은 중요한 로직에서는 중복 결제가 일어나는 문제가 발생한다.

 

두 번째로는 POST 요청은 URL을 복사하더라도 다른 사람과 공유할 수 없다는 점이 문제가 된다. POST 요청을 보내게 되면 Parameter 값들이 URL에 남겨있지 않기에 특정 Paramter 가 필요한 POST 요청인 경우 URL을 복붙 하더라도 에러 페이지만 보이게 될 것이다.

 

 

아마 대부분이 스프링으로 구성된 환경일 것이라고 생각하고 예제 코드를 첨부했다.

PRG 패턴 적용 전 코드

TestController.java

 

@Controller
class TestController {

    @GetMapping
    public String main(){
        return "main";
    }

    @PostMapping("/pay")
    public String pay(){
        // 결제 로직
        return "success";
    }
}

main.html

<!DOCTYPE html>
<html lang="en" >
<head>
    <meta charset="UTF-8">
</head>
<body>
<form action="/pay"  method="post">
    <button>결제</button>
</form>
</body>
</html>

 

success.html

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

 

적용 전 코드를 사용해서 결제 버튼을 클릭하게 되면 성공 시 정상적으로 페이지에 가지지만 해당 페이지에서 새로고침을 하게 되면 아래와 같은 브라우저 경고가 뜨면서 확인 시 똑같은 POST 요청이 반복적으로 보내진다.

 

이런 경고가 뜨면서 새로고침 시 재요청을 한다

 

이렇게 되면 해당 로직이 계속 호출이 되는 것이라 중요한 로직이 들어 있는 경우 문제가 될 가능성이 많이 있다.

 

PRG 패턴 적용 후 코드

 

딱 POST 요청으로 들어온 경우 redirect로 새로 만들어지는 /success라는 페이지로 이동을 시키는 것이 전부이다.

@Controller
class TestController {

    @GetMapping
    public String main(){
        return "main";
    }

    @PostMapping("/pay")
    public String pay(){
        // 결제 로직
        return "redirect:/success";
    }

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

 

이렇게 되면 브라우저에서는 /pay라는 POST 요청이 오게 되면 302 응답 코드를 받게 될 것이고 302 응답 코드를 받은 브라우저는 /success라는 곳으로 리다이렉트 시켜준다. 

 

브라우저는 /success 라는 곳으로 이동했기에 새로고침을 하더라도 GET /success로 새로 고침을 하는 것이라 결제 로직이 중복으로 발생하지 않을뿐더러 3XX 응답 코드를 받은 페이지에 대해서는 브라우저에서는 저장하고 있지 않으므로 뒤로 가기를 하더라도 해당 페이지( /pay )가 나오지 않게 된다.


 

 

반응형

'WEB' 카테고리의 다른 글

CSRF (Cross Site Request Forgery)공격이란? - 삽질중인 개발자  (0) 2020.06.22