게시판의 CRUD중 'R'을 구현하던 중, 삭제완료 후 메인 페이지로 이동했을 때, 삭제가 완료되었다는 메세지를 띄워주고 싶었다.
그러기 위해선 메인 페이지로 화면을 이동시키고, 삭제했음을 표시하는 어떤 변수값을 전달해야 한다.
이를 위해서는 포워딩과 리다이렉트를 쓸 수 있는데,
정말 단순하게 설명하면 포워딩은 URL의 변화 없이 화면만 클라이언트 측에서 바꿔준다.
리다이렉트는, 브라우저 주소창에 주소를 입력하고 엔터를 누른 것처럼 URL도 바뀌는 서버측의 행동이다.
포워딩이 당연히 성능도 더 좋지만, url이 바뀌지 않기 때문에 화면만 이동된 메인 화면에서도 새로고침을 누르면 delete 작업을 다시 수행하게 될것이다.
따라서 redirect를 써야한다.
Redirect 과정에서 메인 페이지에게 "내가 삭제후 메인 페이지로 이동하였음"을 알릴 방법이 필요하다. 이 때 사용 가능한것이 RedirectAttributes다.
RedirectAttributes는 인터페이스인데, 다음과 같은 메서드들이 정의되어있다.
이중에서 사용 빈도가 높은 addAttribute와 addFlashAttribute에 대해 알아보자.
addAttribute는 주로 String이나 숫자와 같은 값을 넘길때 사용된다. 물론 커스텀 객체나 리스트 등을 넘겨도 괜찮지만, 쿼리 파라미터로서 넘어가는데 모든 정보를 uri에 노출하는건 좋지 않은 방법일 것이다.
@GetMapping("/customUrl")
public String doTest(RedirectAttributes redirectAttributes){
redirectAttributes.addAttibute("name", "Kim");
return "redirect:/destination";
}
과 같은 형태로 사용한다.
그렇다면 리다이렉트 페이지에 추가한 attribute가 "쿼리 파라미터" 형태로 넘어간다. (/destination?name=kim)
만약 PathVariable을 쓴다면, 쿼리 파라미터로 넘기지 않고 바인딩도 해준다.
@GetMapping("/customUrl")
public String doTest(RedirectAttributes redirectAttributes){
redirectAttributes.addAttibute("id", 320);
redirectAttributes.addAttibute("name", "Kim");
return "redirect:/destination/{id}";
}
위 코드에서는 "id"값은 바인딩되고, 나머지 name만 쿼리 파라미터로서 전달된다.
쿼리 파라미터 방식을 사용하기에, 특성으로는 새로고침을 해도 데이터가 남아있다는 것이다.
우리가 항상 String이나 Integer만 넘길일이 생기진 않는다. 복잡한 객체를 노출 없이 넘겨주고 싶기도 하고, 내가 게시판을 만들면서 필요했던 일회성 성공 알림등을 만들고 싶을땐 FlashAttirubte를 쓰면 된다.
이름에서 볼 수 있듯, 일회성으로 사용 가능하다. 새로고침 하면 휘발된다.
이 경우엔 쿼리 파라미터가 아니라, 내부적으로 저장되어 넘어간다. 이 내부 동작 방식을 세션에 저장한다고 표현해둔 게시글이 많은데, 정확히 어떤 의미인지 이해하지 못해 추가 조사 후 게시글을 수정하겠다.
스프링 기초를 했다면, 쿼리 파라미터라면 @RequestParam을 사용하고 FlashAttribute로 넘겼다면 @ModelAttribute를 쓰면 된다고 생각 할 수 있다.
그러나 두 애노테이션 모두 붙이지 않아도 잘 바인딩 해준다.
@GetMapping("/")
public String indexPage(String isAct, Model model){
List<Question> allQuestion = questionService.findAllQuestion();
model.addAttribute("questions", allQuestion);
log.info("isAct = {}",isAct);
return "board";
}
리다이렉트 대상에 대한 매핑을 위와 같이 했다고 해보자.
isAct를 쿼리 파라미터로 넘기든, Flash로 넘기든 RedirectAttributes가 자동으로 바인딩을 해준다. 게다가, Model 객체에도 자동으로 추가해준다!
자동 추가가 가능한 이유는, RedirectAttributes는 인터페이스면서 Model을 상속하기 때문이다.
개인적인 추가 궁금사항으로, 만약 같은 이름으로 addAttribute와 addFlashAttribute를 동시에 넘겨준다면 무엇이 바인딩 될지 궁금했다.
redirectAttributes.addAttribute("isAct", "쿼리 파라미터");
redirectAttributes.addFlashAttribute( "isAct", "객체로 전달");
위와 같이 코드를 작성하고 리다이렉트를 걸어 로그값을 확인해봤다.
isAct = 쿼리 파라미터
쿼리 파라미터 방식의 승리다. 작동 순서는
1. 쿼리 파라미터(@RequestParam) 방식이 먼저 isAct에 바인딩한다.
2. 이 과정에서 쿼리 파라미터 값을 model 객체에 추가한다.
3. @ModelAttribute 방식이 isAct에 바인딩을 시도하지만, 이미 값이 존재하므로 무시한다.
4. @ModelAttribute 방식의 값이 model 객체에 추가된다. 이 때, 2번 과정에서 이미 추가된 값을 덮어쓴다. (model 객체에 값을 추가 할 때, 같은 이름으로 존재한다면 덮어쓰는 방식으로 작동한다)
챗지피티와 구글링을 하며 오랫동안 긁어 도출해낸 순서다. 만약 틀린 내용이 존재한다면 피드백 항상 환영
여담
작동 과정이나 모르는게 생겼을때 이렇게 미약하게라도 열심히 조사해가며 배우는 것이 성장에 큰 도움이 되는 것 같다. 아니더라도 그렇게 믿을래
그리고 모르는게 생기면 마지막에라도 꼭 레퍼런스 문서를 확인하자