SPRING MVC - PRG(Post-Redirect-Get) 패턴

Sungjin·2021년 7월 22일
1

Spring

목록 보기
5/23
post-thumbnail

상황 가정

  1. Member를 등록하는 Controller가 있다고 생각 해 봅시다.
  2. Member를 등록하고 나서 등록된 Member를 Model에 담습니다.
  3. 다른 View로 Forward!

코드를 먼저 보시져

@PostMapping("/save")
    public String memberAdd(@ModelAttribute Member member){
        memberService.join(member);
        log.info("Call MemberAdd memberId : {}",member.getId());
        return "save-success";
    }

가정된 상황과 똑같은 형태라고 보시면 될 것 같습니다!!

MemberService
Controller에서 비즈니스 로직을 호출하기 위한 Service 객체라고 보시면 될 것 같 습니다!
이해를 돕기 위해 코드를 보여 드리겠습니당
MemberRepository

@Repository
@RequiredArgsConstructor
public class MemberRepository {
    private final EntityManager em;
    public void save(Member member){
        em.persist(member);
    }
    public Member findById(Long memberId){
        return em.find(Member.class,memberId);
    }
    public List<Member> findAll(){
        return em.createQuery("select m from Member m",Member.class).getResultList();
    }
}

MemberService

public interface MemberService {
    public Long join(Member member);
    public Member findOne(Long memberId);
    public List<Member> findAll();
}

MemberServiceImpl

@Service
@RequiredArgsConstructor
@Transactional(readOnly = true)
public class MemberServiceImpl implements MemberService{
    private final MemberRepository memberRepository;
    @Transactional
    @Override
    public Long join(Member member) {
        memberRepository.save(member);
        return member.getId();
    }
    @Override
    public Member findOne(Long memberId) {
        return memberRepository.findById(memberId);
    }
    @Override
    public List<Member> findAll() {
        return memberRepository.findAll();
    }
}

그렇다면 문제는?

Member를 등록하고 나서 한번 새로고침을 5번 정도 해보겠습니다.

결과

어찌된 영문인지 똑같은 정보의 Member가 id만 다르게 저장이 되었습니다!

그럼 서버의 로그를 한번 보죠!

서버에서도 새로고침을 한 횟수 만큼 똑같은 Contoller가 5번 호출된 것을 볼 수 있습니다.

그럼 DB에는??

DB에서도 똑같이 ID만 다른 똑같은 정보가 새로 고침을 한 횟수 만큼 저장이 되었습니다.

문제의 이유?

바로 새로 고침에 있습니다!
새로 고침은 마지막에 한 행위를 다시 요청하는 일입니다.
즉, 웹브라우저에서는 멤버 저장이 완성되고 나서 서버로 응답을 받은 화면을 보고 있지만 마지막에 한 행위는 POST를 이용하여 Member를 저장하려고 하는 행위 입니다.
이런 이유로 새로고침을 할 때마다 계속해서 똑같은 정보를 저장하는 것이죠..
이것은 본질적인 문제로 볼 수 있습니다.
그렇다면 서버에서의 코드를 한번 보죠.

@PostMapping("/save")
    public String memberAdd(@ModelAttribute Member member){
        memberService.join(member);
        log.info("Call MemberAdd memberId : {}",member.getId());
        return "save-success";
    }

이 코드였습니다. Controller에서 바로 View로 forward 하였기 때문에 어쩔 수 없이 웹브라우저가 마지막으로 한 요청이 지금 이 Controller에서의 행위이기 때문에! DB에 저장과 같은 행위의 요청 뒤 새로고침을 할 때 마다 문제가 생길 수 밖에 없는 거죠...
그러면 코드를 바꿔줘야 합니다... 어떻게 할까요???!!?!??!
바로 바로 redirect를 사용하는 것입니다.

redirect
리다이렉트는 실제 클라이언트에 응답이 나갔다가, 클라이언트가 redirect 경로로 다시 서버에 요청을 한다고 보면 됩니다.

코드를 바꿔 보아요~

    @GetMapping("/{memberId}")
    public String memberDetail(@PathVariable Long memberId,Model model){
        Member member = memberService.findOne(memberId);
        model.addAttribute("member",member);

        return "save-success";
    }

    @PostMapping("/save")
    public String memberAdd(@ModelAttribute Member member, RedirectAttributes redirectAttributes){
        memberService.join(member);
        log.info("Call MemberAdd memberId : {}",member.getId());
        redirectAttributes.addAttribute("memberId",member.getId());
        return "redirect:/spring-mvc/members/{memberId}";
    }

코드에서 보시다 시피 redirect를 활용하여 멤버 저장 후 다시 멤버 상세 화면으로 이동하게 만들으시면 됩니다. 마지막에 호출한 내용이 @GetMapping("/{memberId}")이기 때문에 이후 새로 고침을 하더라도 멤버 상세 화면으로 요청이 가는 것이므로 새로 고침시 문제를 해결할 수 있습니다!!

지금 까지 알아본 이와 같은 패턴을 Post-Redirect-Get, PRG라고 부르게 됩니다!

이상으로 포스팅을 마치겠습니다. 감사합니다 :)

이 글은 인프런 김영한님의 '스프링 MVC 1편 - 백엔드 웹 개발 핵심 기술'을 수강하고 작성합니다.
출처:https://www.inflearn.com/course/%EC%8A%A4%ED%94%84%EB%A7%81-mvc-1/dashboard

profile
WEB STUDY & etc.. HELLO!

0개의 댓글