안녕하세요 동호님. 저도 미션을 수행하다가 이 문제를 겪어서 혹시 도움이 될까 적어봅니다!
아마도 클라이언트에서 time : Long의 형식으로 Request를 보내는 것 같아요. reservation RequestDTO의 timeId -> time으로 필드 명 바꾸시면 해결될 것 같습니다!
sangu1026: 안녕하세요 동호님, 우선 미션하시느라 고생 많으셨습니다 👏 저도 테스트가 모두 통과하여서 잘 몰랐었는데 실제로 reservation을 등록하는 페이지에서 등록이 잘 안되는 문제가 발생하더라구요...
저도 ReservationRequestDto에서 timeId로 time의 id를 받았었는데 알고 보니 time으로 받아야지 컨트롤러에서 @RequestBody로 ReservationRequestDto에 매핑이 가능하더라구요. 덕분에 저도 알지 못하는 오류를 고칠 수 있었습니다. 감사합니다!
sangu1026: 여기서 timeId가 아닌 time으로 수정해야 될 것 같습니다
PlusUltraCode: 감사합니다 상우님!! TimeEntity 클래스 명을 Time으로 바꾸니 다른 클래스에서 TimeEntity time~~~ 라고 선언한 변수들까지 모두 바뀌어서 프로그램이 정상 작동하게 되었습니다. 오류를 찾기 위해 많은 시간을 쓴 만큼 매핑의 중요성을 뼈저리게 느꼈습니다.
sangu1026: 맞습니다. 저도 스프링 미션을 하면서 여러 가지 오류들 때문에 꽤 고생을 했습니다. 오류들을 해결하는 과정에서 오류를 아예 내지 않는 것도 중요하지만, 오류가 발생했을 때 그 원인을 빠르게 찾고 수정하는 것도 정말 중요한 것 같습니다. 수고하셨습니다! 👍
sangu1026: 저는 Service가 Repository를 호출하는 역할밖에 할 수 없어서 Service를 도입하는 게 역할 분리에 큰 의미가 없다고 생각했었습니다. 그런데 다시 생각해보니 컨트롤러가 직접적으로 Repository를 호출하고 의존하는 것은 컨트롤러에 맞는 역할이 아닌 것 같아 동호님처럼 Service를 도입하는 것이 맞는 것 같습니다.
PlusUltraCode: 저도 서비스의 존재 이유를 몰랐지만, 직접 사용해보니 서비스는 컨트롤러와 Repository 사이에 있는 다리와 같다는 느낌을 많이 받았습니다. 컨트롤러 입장에서 서비스에게 명령만 해주고, 나머지는 서비스와 Repository 두 객체끼리 해결한 뒤 정보를 컨트롤러에게 전달하는 느낌이라 생각합니다.
sangu1026: 생성자를 사용하지 않고 @Builder와 같은 애노테이션으로 객체를 생성할 수 있군요. 혹시 @Builder를 사용하신 이유가 단순히 생성자 코드를 생략하기 위해서인가요? 아니면 다른 이점이 있나요?
PlusUltraCode: ReservationEntity.builder().id(id2).name(name2).date(date2).time(time2).build(); 와 같은 형식으로 빌더를 작성합니다. 빌더를 사용하지 않고 해당 ReservationEntity 클래스에 따로 생성자를 선언해주어도 가능합니다. 그렇게 되면 new ReservationEntity(id2, name2, date2, time2);와 같은 형식으로 만들어야 됩니다.
이런 상황이면 문제가 되지 않지만, 만약에 date2나 time2에 값을 NULL로 하고 싶을 경우가 생겼다고 해봅시다. 클래스를 수정하지 않았다고 가정하고 new ReservationEntity(id2, name2)와 같이 선언하게 되면 오류가 생깁니다. 즉, 어떤 경우가 생길 때마다 필요에 맞는 생성자를 추가하거나 아니면 default 생성자를 만든 뒤 reservationEntity.setId(id2); reservationEntity.name(name2);와 같이 초기화해야 됩니다.
다만 빌더를 사용하면 ReservationEntity.builder().id(id2).name(name2).build();와 같이 선언만 하게 되면 자동으로 date와 time은 null값으로 초기화 되는 간편함이 있습니다. 또한 빌더를 사용하면 가독성이 높다고 생각합니다.
sangu1026: 아, 저번 미션을 진행할 때 default 생성자를 만들지 않아 오류가 발생하기도 하고, null값을 넣어줘야 할 때마다 해당 생성자를 직접 만들었는데, builder를 사용하면 이러한 문제도 해결하고 가독성도 높여주는 효과가 있었군요! 다음번에 저도 builder를 적용시켜서 코드를 작성해보도록 하겠습니다. 감사합니다 👍
sangu1026: 저는 @GetMapping(value = "/reservations"), @PostMapping(value = "/reservations"), @DeleteMapping(value = "/reservations/{id}") 부분에서 /reservations가 중복되는 걸 줄여주고 싶어서 @controller 애노테이션 위에 @RequestMapping("/reservations")를 적어주었습니다.
이렇게 하면 중복되는 부분은 제거되어 @GetMapping(value = "/reservations") -> @GetMapping @PostMapping(value = "/reservations") -> @PostMapping @DeleteMapping(value = "/reservations/{id}") -> @DeleteMapping("/{id}) 조금 더 간결하게 작성할 수 있었던 것 같아요.
PlusUltraCode: @RequestMapping("/reservations")를 사용하니 해당 클래스 안에 있는 @GetMapping, @PostMapping에 중복되는 /reservations을 없앨 수 있게 되었습니다. 감사합니다 ^^
sangu1026: @NotBlank 애노테이션을 통해서 간단히 검증할 수 있다는 것을 알게 되었습니다. 감사합니다!
PlusUltraCode: @NotBlank null인지 검사 및 공백 검사 및 빈칸 검사 @NotNull null인지 검사 @NotEmpty null인지 검사 및 빈칸 검사
등 애노테이션을 통해 간단하게 검증할 수 있도록 구현되어 있습니다. @NotBlank를 사용하면 자동으로 null, 공백, 빈칸 3가지를 검증해주기 때문에 @NotBlank를 사용하는 편입니다.
sangu1026: @NotNull, @NotEmpty라는 애노테이션도 있었네요. 추가적인 설명 감사합니다 👍
'멋쟁이사자처럼 동아리 > 코드 리뷰' 카테고리의 다른 글
코드 리뷰 리뷰어 : 이동호 로또 미션 (0) | 2024.06.22 |
---|---|
코드리뷰 리뷰어 : 이동호 Spring MVC (0) | 2024.06.22 |
코드리뷰 Lotto mission (0) | 2024.06.22 |
코드리뷰 Spring MVC (0) | 2024.06.22 |
코드리뷰 racing Car 1주 (0) | 2024.06.22 |