https://github.com/kimuky/schedule-management-develop
1. 복합키로 PK를 가능..?
1) 개요
- LV 7 단계를 해야하는데 댓글이라는 테이블을 추가해 CRUD를 구현해야한다.
하지만 댓글은 일정에 의존적이며, 유저에 의존적이다. (이 말이 맞는지는 모르겠다.)
- 결론적으로는 유저와 일정의 PK를 가져와서 댓글의 FK로 써야한다.
- 그렇다면 Entity를 만드는 방법은 Comment Entity에서 일정, 유저의 각 PK를 @ManytoOne으로 연결해주면 된다.
하지만 레포지토리에선 하나의 PK 기준으로 <Schedule, Long>, <User, Long> 요런 형태로 넣어줫는데 키가 두 개인 경우는 어떻게...?
2) 문제 상황
- 그러면 Entity를 살펴보자
package com.example.schedulemanagementdevelop.entity;
import jakarta.persistence.*;
import lombok.Getter;
import lombok.Setter;
@Getter
@Entity
@Table(name = "comment")
public class Comment extends BaseEntity {
@Id
@Setter
@ManyToOne
@JoinColumn(name = "user_id")
private User user;
@Id
@Setter
@ManyToOne
@JoinColumn(name = "schedule_id")
private Schedule schedule;
private String contents;
public Comment() {
}
public Comment(String contents) {
this.contents = contents;
}
public void updateContents(String contents) {
this.contents = contents;
}
}
음.. 오류는 뜨지않지만 경고가 뜬다.. 아직 여기에 대해서 이해를 하지 못한 것 같다.
도저히 이렇게 셋팅하고는 CommentRepository를 어떻게 구성해야하는지 모르겠다.
3) 해결
- 당장 구현을 위해서 댓글 자체적으로 PK(Auto_increment)를 통해 구성을 바꿔주었다.
package com.example.schedulemanagementdevelop.entity;
import jakarta.persistence.*;
import lombok.Getter;
import lombok.Setter;
@Getter
@Entity
@Table(name = "comment")
public class Comment extends BaseEntity {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Setter
@ManyToOne
@JoinColumn(name = "user_id")
private User user;
@Setter
@ManyToOne
@JoinColumn(name = "schedule_id")
private Schedule schedule;
private String contents;
public Comment() {
}
public Comment(String contents) {
this.contents = contents;
}
public void updateContents(String contents) {
this.contents = contents;
}
}
이렇게 하면 CommentRepository를 걱정하지 않아도 된다.
package com.example.schedulemanagementdevelop.repository;
import com.example.schedulemanagementdevelop.entity.Comment;
import com.example.schedulemanagementdevelop.entity.Schedule;
import org.springframework.data.jpa.repository.JpaRepository;
import java.util.List;
public interface CommentRepository extends JpaRepository<Comment, Long> {
List<Comment> findByScheduleId(Long findSchedule);
}
4) 결론
- 아직 JPA를 이해하지 못한 것 같다..
- 복잡한 구조를 가지면 엔터티를 어떻게 구성을 해야할지 감이 오지 않는다.
- @EmbeddedId를 이용해 복합키를 매칭할 수 있다는데 이해가 가지않는다. (추가적으로 공부하기)
2. 싱글 필드? Jackson? 변환? Delegating -> 오류 발생
1) 개요
- 일정에 달리는 댓글을 구현하는 중에 Dto, Controller, Service, Repository를 다 구성하고 디버깅을 해보았는데 오류가 발생했다.
2) 문제 상황
- 컴파일 단에서 오류가 발생되지 않는다.
- Postman으로 댓글 생성 시, Bad Request
- 음 .. 컨트롤러에서 오류가 발생하는 것 같다.
2024-11-15T00:18:00.820+09:00 WARN 21736 --- [schedule-management-develop] [nio-8080-exec-9] .w.s.m.s.DefaultHandlerExceptionResolver : Resolved [org.springframework.http.converter.HttpMessageNotReadableException: JSON parse error: Cannot construct instance of `com.example.schedulemanagementdevelop.dto.CommentRequestDto` (although at least one Creator exists): cannot deserialize from Object value (no delegate- or property-based Creator)]
- CommentRequestDto 쪽에 문제가 발생한 것 같다.
- Json Parser 오류, 비록 생성자가 있더라도 객체로부터 역직렬화 할 수 없다..? 이게 무슨 소리인가..
- delegate?, property-based Creator?
@Getter
public class CommentRequestDto {
@NotBlank
private final String contents;
public CommentRequestDto(String contents) {
this.contents = contents;
}
}
- 생성자도 잘 있고 문제도 없어보인다.
그럼 컨트롤러에서 문제가??
public class CommentController {
private final CommentService commentService;
@PostMapping("/{scheduleId}/comment")
public ResponseEntity<CommentResponseDto> saveComment(
@PathVariable Long scheduleId,
@Valid @RequestBody CommentRequestDto requestDto,
HttpServletRequest servletRequest) {
HttpSession session = servletRequest.getSession(false);
String userEmail = (String) session.getAttribute("email");
CommentResponseDto commentResponseDto = commentService.saveComment(scheduleId, requestDto, userEmail);
return new ResponseEntity<>(commentResponseDto, HttpStatus.CREATED);
}
}
- 문제가 없어보인다. @Requestbody를 통해 정상적으로 dto랑 맵핑이 되어야한다.. (Json으로 데이터만 잘 보낸다면)
뭔가 오류메시지를 보니 dto 쪽 문제가 확실한 것 같아서 dto를 수정해보았다.
@Getter
public class CommentRequestDto {
@NotBlank
private String contents;
public CommentRequestDto() {
}
public CommentRequestDto(String contents) {
this.contents = contents;
}
}
음 작동이 된다.. 물론 이게 맞는 구조인가 생각해보면 아닌 것 같다..
- GPT한테 물어봐도 무슨 말인지 통 모르겠다.
3) 해결
- 해결 방법
1. 필드를 하나 더 추가한다. -> 작동 O
2. final을 제거한다. -> 작동 O
1번 방식처럼 한다면 불필요한 필드를 추가해줘야하는데 그렇게 하기는 싫었다..
2번 방식처럼 한다면 final을 제거해야하는게 과연 이게 맞는가를 생각해보면 리퀘스트가 바뀌는 것도 아닌데 이상했다.
- 구글링을 통해 찾아보니 필드가 하나이면 Delgating vs Properties 방식 중 뭘 써야 할지 몰라서 발생하는 에러라고 한다..
(무슨 소리인지 1도 모르겠다..)
- 그래서 해결 방법을 찾아보았더니 생성자에 @JsonCreator를 붙혀주면 된다..
- 다른 방법은 @ConstructorProperties 어노테이션을 생성자에 넣어주면 된다고는 하나 해보지는 않았다.
package com.example.schedulemanagementdevelop.dto.comment;
import com.fasterxml.jackson.annotation.JsonCreator;
import jakarta.validation.constraints.NotBlank;
import lombok.Getter;
@Getter
public class CommentRequestDto {
@NotBlank
private final String contents;
@JsonCreator
public CommentRequestDto(String contents) {
this.contents = contents;
}
}
그 뒤, 결과
잘 된다..
4) 결론
- Json 변환 과정을 제대로 이해하지 못했다.
- Json에서 싱글필드를 쓸려면 delgating, properties 방식을 이해하자
'스파르타(부트캠프) > Troubleshooting' 카테고리의 다른 글
[내일배움캠프] 스프링심화주차 트러블슈팅 (TBU) (1) | 2024.12.26 |
---|---|
[내일배움캠프] 플러스주차(스프링 심화)에 대한 트러블슈팅 (0) | 2024.12.19 |
[내일배움캠프] 뉴스피드 프로젝트에 대한 트러블슈팅 (0) | 2024.11.22 |
[내일배움캠프] 일정관리 앱에 대한 트러블슈팅 (1) | 2024.11.08 |