ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • @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의 동작 방식

    1. Setter 메서드로 값 주입: Spring MVC는 @ModelAttribute가 붙은 메서드 매개변수에 대해 객체를 생성합니다. 이때, 해당 객체의 각 필드에 대응하는 Setter 메서드가 있다면 Setter를 통해 값을 주입합니다.
    2. 기본 생성자와 Setter 메서드 사용: Spring은 기본 생성자로 객체를 먼저 생성하고, 필요한 경우 Setter 메서드를 통해 값들을 주입합니다.
    3. 모든 매개변수가 있는 생성자: 객체에 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}";
        }

     

     

    포스팅은 개인 프로젝트를 통해 겪은 문제와 그에 대한 해결책을 다른 사람들과 공유하고자 하는 목적으로 작성되었습니다.

     

     


    참고 : https://jojoldu.tistory.com/361

    '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
Designed by Tistory.