본문 바로가기
프로그래밍/스프링프레임워크

@SessionAttributes와 SessionStatus 사용하기(세션에 모델 객체 저장)

by pentode 2018. 8. 26.

스프링프레임워크에서 @ModelAttribute또는 model.addAttribute()를 사용해서 프레젠테이션 계층으로 객체를 보낼 수 있습니다. 이때는 request scope에 객체가 저장 되므로 요청시에만 객체가 사용되어 집니다.


@RequestMapping(value = "/sawon_list.do", method = RequestMethod.GET)

public String sawonList(Model model) throws Exception {

    model.addAttribute("list", sawonService.selectSawonList());

    return "sawon_list";

}


@RequestMapping(value = "/sawon_regform.do", method = RequestMethod.GET)

public String sawonRegform(@ModelAttribute("sawon") SawonVO sawon, Model model) {

    return "sawon_regform";

}


@SessionAttributes를 사용하면 @ModelAttribute또는 model.addAttribute()를 사용해서 객체를 저장할 경우 세션에 저장이 되도록 지정할 수 있습니다. 이 경우는 세션이 파괴되거나 SessionStatus 객체를 사용해서 명시적으로 지울때까지 동일한 세션에서는 계속 사용할 수 있습니다.


직접 세션 객체에 setAttribute(), getAttribute() 메소드를 사용해서 원하는 작업을 할 수도 있지만, @SessionAttribute는 사용한 컨트롤러에서만 @ModelAttribute가 세션에 객체를 저장하고, 찾도록 한정할 수 있습니다. 또한 @ModelAttribute와 같이 사용해서 세션 객체에 넣고 빼는 작업을 숨겨주고 스프링 form 태그와 연동되어 폼에 값을 넣는 작업도 단순화 되는 장점이 있습니다.



어떤 경우에 session scope에 객체를 저장해두고 쓰면 좋을까요? 가장 먼저 떠오르는게 등록폼에서 입력해야할 정보가 아주 많을 경우 페이지를 나눠서 작성하는 경우가 되겠습니다. 입력한 정보가 세션에 저장되어 있으면 이전/다음 으로 등록폼들 사이를 왔다갔다 하면서 작성하는 것도 가능합니다.


이번 예제에서는 두 가지를 해봅니다. 첫번째는 등록폼이 여러 페이지로 나뉘어 있는 경우와 상세보기 후에 수정 또는 삭제시에 키값을 히든 필드를 사용해서 넘기지않고, 조회시 세션에 저장된 값을 사용해서 처리하는 것입니다.


@SessionAttributes를 사용할 때 주의할 점이 있는데, 사용후에는 SessionStatus객체의 setComplete() 메소드를 사용해서 해제해 주어야 하는 것입니다. 그렇지 않으면 세션에 계속 남아 있게 됩니다.


이 해제와 관련해서 생각해 봐야 할 문제가 있는데, 작업시 성공하면 세션에서 제거하게되는데, 사용자가 중도에 다른 메뉴를 클릭해서 빠져 나가게 되면 세션에 저장된 객체가 그대로 남게 됩니다. 세션에 제거되지 않고 남아있는 객체들로 문제가 되지 않을지 생각을 해봐야 할 것 갈습니다.



1. 두 개로 나눠진 등록폼에서 사용하기


1.1. 컨트롤러에서 @SessionAttributes를 지정합니다.


@Controller

@SessionAttributes({"sawon","sawonDetail"})

public class SawonController {

    ...

}


Model 객체에 저장시 세션에 저장될 키값을 지정합니다. "sawon"은 등록폼과 수정에서 사용할 키값이고,  "sawonDetail" 은 상세보기에서 사용할 키 값입니다. 같은 키값을 상세보기와 입력해서 사용하면 상세보기후에 등록폼으로 가면 조회 했던 내용이 폼에 미리 보여지게 되므로 분리했습니다.



1.2. 등록폼 컨트롤러


@RequestMapping(value = "/sawon_regform.do", method = RequestMethod.GET)

public String sawonRegform(@ModelAttribute("sawon") SawonVO sawon, Model model) {

    return "sawon_regform";

}


@ModelAttributre("sawon") 으로 지정하면 세션에 "sawon"을 키로 세션에서 객체를 찾게 됩니다.


등록폼을 최초 호출시에 에러가 다음 발생합니다.. 세션에서 sawon 으로 객체를 찾기 못했기 때문입니다.


org.springframework.web.HttpSessionRequiredException: Expected session attribute 'sawon'


Model 객체에 직접 빈 객체를 생성, 추가해서 해결 할 수 있지만 이전으로 되돌아 왔을때 초기화 되어 버리게 됩니다.


// 등록폼 첫페이지에 이 코드가 있으면 이전으로 돌아올때 초기화 되어 버린다.

model.addAttribute("sawon", new SawonVO());


이 상황은 컨트롤러에 @ModelAttribute 아노테이션은 메소드에 적용한 빈 SawonVO 객체를 반환하는 메도드를 추가함으로 해결할 수 있습니다.


@ModelAttribute("sawon")

public SawonVO setEmptySawon() {

    return new SawonVO();

}


폼에 출력되었습니다. 내용을 직접 입력한 것입니다.




다른 방법으로 최초의 등록폼 호출을 GET과 POST 방식으로 각각 나눠서 GET 방식의 호출에서는 model.addAttribute("sawon", new SawonVO()); 를 사용하고, POST방식의 호출에서는  @ModelAttribute("sawon")을 사용할 수도 있습니다. "이전" 버튼으로 돌아올때는 현재 입력한 값이 적용되어야 하므로 POST 방식을 사용해야 합니다.


@RequestMapping(value = "/sawon_regform.do", method = RequestMethod.GET)

public String sawonRegform(Model model) {

    model.addAttribute("sawon", new SawonVO());

    return "sawon_regform";

}


@RequestMapping(value = "/sawon_regform.do", method = RequestMethod.POST)

public String sawonRegform(@ModelAttribute("sawon") SawonVO sawon, Model model) {

    return "sawon_regform";

}



1.3. 두 번째 폼입니다.

첫 번째 폼에서 "다음" 버튼을 누르면 입력한 값들이 POST 되고, 자동으로 SawonVO 객체에 맵핑되어 집니다. 맵핑에 사용된 객체는 세션에서 찾아서 사용됩니다.


@RequestMapping(value = "/sawon_regnextform.do", method = RequestMethod.POST)

public String sawonRegnextform(@ModelAttribute("sawon") SawonVO sawon, Model model) {

    return "sawon_regnextform";

}




1.4. 등록코드 입니다.


입력이 성공하면 SessionStatus 객체의 setComplete() 메소드로 세션에서 지웁니다.


@RequestMapping(value = "/sawon_register.do", method = RequestMethod.POST)

public String sawonRegister(@ModelAttribute("sawon") SawonVO sawon, SessionStatus sessionStatus, Model model) throws Exception {

    // 입력한다.

    sawonService.register(sawon);

    

    // 세션에서 지운다.

    sessionStatus.setComplete();

    return "redirect:/sawon_list.do";

}



2. 수정에서 사용하기

상세보기에서 조회된 정보를 세션에 저정해두고, 수정폼에서는 키값을 제외한 실제 수정될 수 있는 값만을 폼에서 보여줍니다.


2.1. 상세보기


@RequestMapping(value = "/sawon_detail.do", method = RequestMethod.GET)

public String sawonDetail(String sabun, Model model) throws Exception {

    model.addAttribute("sawonDetail", sawonService.selectSawon(sabun));

    return "sawon_detail";

}


컨트롤러에 @SessionAttribute("sawonDetail") 으로 지정해 두면 @ModelAttribute("sawonDetail") 뿐만아니라, model.addAttribute("sawonDetail", obj); 도 세션에 저장됩니다.



2.2. 수정폼 보기

일반적으로 수정폼에서 키값을 받아서 조회후 보여주지만 여기서는 상세보기에서 세션에 저장된 값을 보여줍니다.


@RequestMapping(value = "/sawon_updateform.do", method = RequestMethod.GET)

public String sawonUpdateform(@ModelAttribute("sawonDetail") SawonVO sawon, Model model) throws Exception {

    return "sawon_updateform";

}




2.3. 수정하기

수정폼에는 키값이 존재하지 않지만 폼이 제출되면 세션에 저장되어 있던 사원객체에 수정된 내용된 변경되고, 기존의 키값을 그대로 유지된채 값이 맵핑되어 집니다.


@RequestMapping(value = "/sawon_update.do", method = RequestMethod.POST)

public String sawonUpdate(@ModelAttribute("sawonDetail") SawonVO sawon, SessionStatus sessionStatus, Model model) throws Exception {

    // 수정한다.

    sawonService.updateSawon(sawon);

    // 세션에서 지운다.

    sessionStatus.setComplete();

    return "redirect:/sawon_list.do";

}



@SessionAttributes 아노테이션을 사용해서 데이터의 등록과 수정에서 세션에 데이터를 저장해두고 사용하는 방법을 알아보았습니다. 전체 예제의 소스는 아래에 추가해 두었습니다. 스프링프레임워크 5.0.7과 MySQL 데이터베이스가 사용되었습니다.


※ 전체소스

session_attribute.zip


반응형