PRG 패턴 (Post → Redirect → Get)

동민·2022년 10월 24일
1
post-thumbnail

개발 공부를 하면서 위와 같은 알림창을 한 번이라도 보신적이 있나요?
혹시 본인이 이런 알림창을 보고도 귀찮아서 무시하고 개발을 진행한 적이 있다면,
그리고 이런 알림창이 뜨는 이유를 몰랐다면 이 글을 한 번 읽고 가는 걸 추천합니다!

PRG 패턴이란?

권장되는 디자인 패턴 중 하나로, Post → Redirect → Get방식으로 동작하는 패턴입니다.
쉽게 말하면 POST 요청의 응답으로 GET 요청을 리다이렉트 하는 패턴입니다.

PRG 패턴을 왜 사용해야 할까?

  • 새로고침으로 인한 중복 요청이 발생할 수 있기 때문입니다.
    • 새로고침: 가장 최근에 수행한 요청을 실행하는 것 (GET, POST)
    • 단순한 로직이라면 상관없지만, 결제 관련 로직이라면 대참사 발생.. 비상!!🚨😱

  • POST방식은 URL을 통해 공유가 불가능하기 때문입니다.
    • URL에 파라미터 정보를 포함하지 않기 때문에 필요한 데이터를 가져올 방법이 없음!

❌ PRG 패턴 적용 전 코드

@PostMapping("/findPassword")
public String memberPasswordFind(
			@Valid MemberFindPwdFormDto memberFindPwdFormDto, 
            BindingResult result) throws MessagingException {

	if (result.hasErrors()) {
    	return "member/find_password_form";
	}

	Member member = memberService.findByUsernameAndEmail(memberFindPwdFormDto.getUsername(), memberFindPwdFormDto.getEmail());
    String temporaryPwd = UUID.randomUUID().toString().replaceAll("-", "");

	if (member != null) {
    	memberService.changePassword(member, passwordEncoder.encode(temporaryPwd));
        mailService.sendMail(member.getEmail(), temporaryPwd);
	}

	return "member/find_password";
}


  • 예제는 회원의 아이디와 이메일을 입력받아서 사용자 이메일로 임시 비밀번호를 발송해주는 로직을 사용했습니다.
  • PRG 패턴이 적용되지 않은 현재 방식에서는 새로고침을 하는 경우 똑같은 POST 요청이 다시 실행되어 메일이 반복되서 전송 되는 문제점을 가지고 있습니다.

✨ PRG 패턴 적용 후 코드

@PostMapping("/findPassword")
public String memberPasswordFind(
            @Valid MemberFindPwdFormDto memberFindPwdFormDto,
            BindingResult result,
            RedirectAttributes redirectAttributes) throws MessagingException {

	if (result.hasErrors()) {
		return "member/find_password_form";
	}

	Member member = memberService.findByUsernameAndEmail(memberFindPwdFormDto.getUsername(), memberFindPwdFormDto.getEmail());
	String temporaryPwd = UUID.randomUUID().toString().replaceAll("-", "");

	if (member != null) {
      memberService.changePassword(member, passwordEncoder.encode(temporaryPwd));
      mailService.sendMail2(member.getEmail(), temporaryPwd);
	}

	redirectAttributes.addAttribute("password", member != null ? temporaryPwd : null);

	return "redirect:/member/findPassword/success";
}


@GetMapping("/findPassword/success")
public String successFindPassword() {
	return "member/find_password";
}
  • PRG 패턴을 적용해서 응답으로 GET 방식의 URL로 리다이렉트를 했습니다.
  • 마지막 요청은 GET 요청이기 때문에 새로고침을 해도 중복 요청이 발생하지도 않고, 알림창도 뜨지도 않습니다.
  • 데이터를 담아서 리다이렉트 하는 경우에는 RedirectAttributes를 사용해서 전달하면 됩니다. → RedirectAttributes에 담으면 URL에 쿼리 파라미터 형식으로 데이터가 노출되기 때문에 보안에 민감한 데이터라면 다른 방식으로 담아서 사용하는 것을 추천합니다!!

PRG 패턴은 저도 이번에 처음 적용해봤지만, 가성비가 굉장히 좋은 패턴이라고 생각합니다!
(학습 시간 ⬇, 사이트 개선도 ⬆)
본인이 만든 사이트에 아직 적용이 안되있다면 꼭 한 번 적용해보시면 좋을 것 같습니다.

profile
Backend engineer

0개의 댓글