-
@ModelAttribute를 사용하여 직렬화Spring 2023. 11. 29. 17:19
본 포스팅은 개인프로젝트 진행시 겪었던 어려움과 해결 방법에 대해 이야기하는 글입니다.
졸업 과제 프로젝트로 다이어리 서비스를 개발하는 중 문제가 발생하였다.
우선 Event 엔티티 클래스 필드값이다.
@Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; @ManyToOne @JoinColumn(name = "USER_ID") private User user; @Enumerated(EnumType.STRING) private Status status; private String eventName; private LocalDate activationDate; private LocalDate deactivationDate;
사용자로부터 입력을 받아야할 값은 eventName, activationDate, deactivationDate 총 세가지이다.
컨트롤러에서 이를 입력받을 때 문제가 발생하였는데 html폼을 통해서 요청이 들어오는 날짜의 값과 필드값의 타입이 맞지 않기 때문에 @DateTimeFormat을 사용하여 String으로 들어온 요청을 LocalDate타입으로 변환하였다.
그렇게 완성된 첫번째 코드는 아래와 같다.
@PostMapping("/event/save") public String createEvent(@RequestParam("eventName") String eventName, @RequestParam("activationDate") @DateTimeFormat(pattern = "yyyy-MM-dd") LocalDate activationDate, @RequestParam("deActivationDate") @DateTimeFormat(pattern = "yyyy-MM-dd") LocalDate deActivationDate, @LoginUser SessionUser sessionUser, RedirectAttributes redirectAttributes) { User user = userService.findById(sessionUser.getId()); EventRequestDto eventRequestDto = new EventRequestDto().builder() .user(user) .eventName(eventName) .activationDate(activationDate) .deActivationDate(deActivationDate) .build(); Long eventId = eventService.save(eventRequestDto.toEntity()); redirectAttributes.addAttribute("id", eventId); return "redirect:/event/{id}"; }
한눈에 보기에도 코드가 지저분해보였다. 그래서 파라미터에서 @ModelAttribute로 dto클래스를 받아오게하게 위해 코드를 변경하였다.
그렇게 하기 위해선 위의 코드처럼 EventRequestDto클래스의 날짜를 받는 필드값에 @DateTimeFormat을 붙여주어야 컨트롤러에서 요청을 받을 때 String을 LocalDate타입으로 변환을 할 수 있다.
그래서 아래처럼 EventRequestDto 클래스의 필드값을 수정하였지만 정상적으로 이벤트가 저장되지 않았고, log를 통해 확인하니 정상적으로 값이 입력되어있지 않고 null로 처리가 되어있었다.
@NoArgsConstructor @Getter public class EventRequestDto { private User user; private Status status; private String eventName; @DateTimeFormat(pattern = "yyyy-MM-dd") private LocalDate activationDate; @DateTimeFormat(pattern = "yyyy-MM-dd") private LocalDate deActivationDate; }
이후 검색을 통해 밝혀진 원인은 @ModelAttribute의 동작 방식과 dto 클래스의 문제였다.
@ModelAttribute의 동작 방식
- Setter 메서드로 값 주입: Spring MVC는 @ModelAttribute가 붙은 메서드 매개변수에 대해 객체를 생성합니다. 이때, 해당 객체의 각 필드에 대응하는 Setter 메서드가 있다면 Setter를 통해 값을 주입합니다.
- 기본 생성자와 Setter 메서드 사용: Spring은 기본 생성자로 객체를 먼저 생성하고, 필요한 경우 Setter 메서드를 통해 값들을 주입합니다.
- 모든 매개변수가 있는 생성자: 객체에 Setter 메서드가 아닌 생성자가 있는 경우, Spring은 해당 생성자를 호출하여 값들을 매핑합니다. 이 경우, 요청 파라미터 이름과 생성자 매개변수 이름이 일치해야 합니다.
내가 작성한 dto 클래스는 setter도 없었고 @AllArgsConstructor 어노테이션도 없었기에 값을 주입할 방법이 없었기에 발생한 오류였다. 롬복 어노테이션인 @Setter를 붙여주어 값이 주입될 수 있게하였고 정상적으로 컨트롤러가 작동하는 것을 확인할 수 있었다.
아래는 수정된 컨트롤러의 코드이다.
@PostMapping("/event/save") public String createEvent(@ModelAttribute EventRequestDto eventRequestDto, @LoginUser SessionUser sessionUser, RedirectAttributes redirectAttributes){ eventRequestDto.setUser(userService.findById(sessionUser.getId())); Long save = eventService.save(eventRequestDto.toEntity()); redirectAttributes.addAttribute("id", save); return "redirect:/event/{id}"; }
포스팅은 개인 프로젝트를 통해 겪은 문제와 그에 대한 해결책을 다른 사람들과 공유하고자 하는 목적으로 작성되었습니다.
'Spring' 카테고리의 다른 글
Spring security test 에러 (0) 2024.04.02 JDBC API, JDBC Template (0) 2024.01.10 HttpMessageConverter (0) 2023.10.05 Converter, Formatter (0) 2023.08.29 IoC 컨테이너(Application Context)와 빈 (0) 2023.08.22