@ModelAttribute
와 @SessionAttributes
는 모두 스프링 MVC에서 데이터 바인딩과 세션 관리를 위해 사용됩니다. 이 두 애노테이션을 함께 사용하면, 세션에 저장된 데이터를 쉽게 바인딩하거나, 세션 속성에 접근할 수 있습니다. @ModelAttribute
는 주로 폼 데이터 바인딩에 사용되고, @SessionAttributes
는 세션에 특정 모델 속성을 저장하여 여러 요청 간에 유지할 수 있게 해줍니다.
아래에서 @ModelAttribute
와 @SessionAttributes
를 함께 사용하는 방식에 대해 설명하겠습니다.
1. @ModelAttribute
와 @SessionAttributes
의 역할
@ModelAttribute
- 바인딩:
@ModelAttribute
는 요청 파라미터를 자동으로 모델 객체에 바인딩하는 데 사용됩니다. 주로 폼 데이터를 자바 객체에 바인딩하거나, 모델 객체를 뷰로 전달할 때 사용합니다. - 뷰에서 사용: 모델에 추가된 속성은 뷰(HTML, JSP 등)에서 쉽게 참조할 수 있습니다.
@SessionAttributes
- 세션 저장:
@SessionAttributes
는 특정 모델 속성을 세션에 저장하여, 해당 속성이 여러 요청 간에 유지되도록 합니다. - 다중 요청 간 상태 유지: 주로 사용자 세션 상태를 유지하거나, 여러 페이지에 걸친 폼 데이터를 유지할 때 사용됩니다.
2. @ModelAttribute
와 @SessionAttributes
를 함께 사용
이 두 애노테이션을 함께 사용하면, 컨트롤러에서 처리된 데이터를 세션에 저장하고, 이후의 요청에서도 세션에 저장된 데이터에 접근할 수 있습니다.
예시 시나리오
사용자가 여러 단계를 거쳐서 데이터를 입력하는 폼이 있다고 가정해 봅시다. 사용자는 페이지를 이동하면서 입력한 데이터를 세션에 저장해야 하고, 마지막 단계에서 세션에 저장된 모든 데이터를 제출할 수 있어야 합니다.
1) @SessionAttributes
와 @ModelAttribute
를 설정한 컨트롤러
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.SessionAttributes;
import org.springframework.web.bind.support.SessionStatus;
import org.springframework.ui.Model;
@Controller
@SessionAttributes("userForm") // "userForm" 모델 속성을 세션에 저장
public class UserController {
// 첫 번째 단계: 사용자가 첫 번째 페이지를 요청할 때 userForm 객체 생성
@RequestMapping("/step1")
public String step1(Model model) {
model.addAttribute("userForm", new UserForm()); // 새로운 UserForm 객체를 모델에 추가하고 세션에 저장
return "step1"; // "step1.html"로 이동
}
// 두 번째 단계: 사용자가 입력한 데이터 처리
@RequestMapping("/step2")
public String step2(@ModelAttribute("userForm") UserForm userForm) {
// "userForm" 모델 속성은 세션에 저장된 객체를 참조
// 폼 데이터를 바인딩하여 userForm 객체에 자동으로 설정됨
return "step2"; // "step2.html"로 이동
}
// 마지막 단계: 세션에 저장된 userForm 데이터 제출
@RequestMapping("/submit")
public String submit(@ModelAttribute("userForm") UserForm userForm, SessionStatus sessionStatus) {
// 세션에 저장된 "userForm"을 사용하여 데이터 처리
// 이후 세션을 정리
sessionStatus.setComplete(); // 세션에 저장된 "userForm" 속성 제거
return "result"; // 결과 페이지로 이동
}
}
3. 동작 흐름 설명
- 첫 번째 단계 (
/step1
):- 사용자가
/step1
URL을 요청하면, 컨트롤러는UserForm
객체를 생성하고 세션에 저장합니다. @SessionAttributes("userForm")
덕분에userForm
객체가 세션에 유지됩니다.- 이 단계에서 사용자는 폼에 데이터를 입력하고, 해당 데이터는 세션에 저장된
userForm
객체에 바인딩됩니다.
- 사용자가
- 두 번째 단계 (
/step2
):- 사용자가
/step2
페이지로 이동하면, 세션에 저장된userForm
객체가 자동으로 바인딩됩니다. - 스프링은 세션에서
userForm
을 가져와 사용자가 입력한 데이터를 유지하며, 이후의 폼 데이터가 계속해서 이 객체에 바인딩됩니다.
- 사용자가
- 최종 단계 (
/submit
):- 사용자가 최종적으로 폼을 제출하면,
userForm
객체에 저장된 데이터를 처리하고, 제출 후sessionStatus.setComplete()
를 호출하여 세션에서userForm
을 제거합니다. 이는 세션이 더 이상 필요하지 않음을 의미합니다.
- 사용자가 최종적으로 폼을 제출하면,
4. 세션 속성에 접근하는 @ModelAttribute
@ModelAttribute
는 세션에 저장된 모델 속성을 매개변수로 주입받을 수 있습니다. 즉, 세션에 이미 저장된 속성을 폼이나 다른 페이지에서 계속해서 사용할 수 있습니다.- 사용자가 세션에 저장된 데이터를 수정하거나 추가할 때,
@ModelAttribute
는 폼 데이터를 바인딩할 때 자동으로 세션 데이터를 사용할 수 있습니다.
5. 세션 속성 관리와 SessionStatus
SessionStatus.setComplete()
: 세션에 저장된 특정 속성을 더 이상 사용할 필요가 없을 때, 이를 명시적으로 제거할 수 있습니다. 세션 속성을 정리하지 않으면 세션이 계속해서 유지되며, 다른 페이지에서 의도치 않게 사용될 수 있습니다.- 세션 상태 관리: 여러 요청 간 상태를 유지하기 위해 필요한 속성만을 세션에 저장하고, 불필요한 속성은 제거하는 것이 중요합니다. 세션이 길어지면 메모리와 리소스를 많이 차지할 수 있기 때문에 적절히 관리해야 합니다.
6. 요약
@ModelAttribute
: 주로 폼 데이터 바인딩과 모델 속성을 컨트롤러 메서드에 전달하는 데 사용됩니다. 세션에 저장된 속성에 접근할 수 있으며, 폼 데이터를 자동으로 바인딩해줍니다.@SessionAttributes
: 특정 모델 속성을 세션에 저장하여, 여러 요청 간 상태를 유지하는 데 사용됩니다.- 두 애노테이션을 함께 사용하면, 세션에 저장된 모델 속성에 자동으로 접근할 수 있고, 폼 데이터를 바인딩하여 여러 페이지에 걸친 폼 데이터 처리도 가능합니다.
@GetMapping("/{id}")에서 중괄호 {}는 경로 변수(Path Variable)를 나타냅니다. 이는 URL 경로의 일부분을 변수로 사용하여 메서드에서 해당 값을 동적으로 처리할 수 있게 해줍니다.
@GetMapping("/posts/{id}")
public String getPostById(@PathVariable("id") Long id, Model model) {
// id를 사용하여 처리
Post post = postService.findById(id);
model.addAttribute("post", post);
return "post/detail";
}
위 코드를 통해 설명하자면:
- @GetMapping("/posts/{id}")는 /posts/1, /posts/2 같은 경로에서 id 값을 추출하여 해당 메서드로 전달하는 역할을 합니다.
- {id}는 경로에서 동적으로 변하는 부분을 나타내며, 이를 통해 URL의 일부분을 변수로 받아 처리할 수 있습니다.
- @PathVariable("id")를 사용하여 해당 경로에서 가져온 값을 메서드 인자로 전달받아 사용하게 됩니다.
즉, @GetMapping("/posts/{id}")에서 {id}는 URL의 변수 부분을 의미하며, 이 값을 받아서 메서드 내부에서 처리할 수 있게 해주는 역할을 합니다.