Conversation
sinsehwan
left a comment
There was a problem hiding this comment.
고생하셨습니다! 2주차 과제 잘 작성해주셨네요! 피드백 내용 중 어려운 내용들이 조금 있습니다.. 알면 매우 좋은 내용들이라 일단 넣어뒀는데, 천천히 반영해보시면 좋을 것 같습니다!
| @OneToMany(mappedBy = "postEntity", cascade = CascadeType.ALL, orphanRemoval = true) | ||
| private List<CommentEntity> comments = new ArrayList<>(); |
There was a problem hiding this comment.
@OneToMany까지 적용해서 양방향으로 구현해주셨네요! 그런데 사실 단방향으로 @ManyToOne만 사용하더라도 DB 테이블에서 FK가 추가됩니다. 그래서 저는 양방향 연관관계가 꼭 필요한 게 아니면 보통 단방향만 사용하는 걸 추천드립니다. 양방향 연관관계 지정 시 객체 생성 시 서로 참조하는 형태라 순환참조 발생할 위험도 있고 cascade가 설정돼 있으면 삭제 시 의도치 않은 요소가 삭제될 수 있어서 관리하기가 어렵더라고요
| @ManyToOne(fetch = FetchType.LAZY) | ||
| @JoinColumn(name = "post_id") | ||
| private PostEntity postEntity; |
There was a problem hiding this comment.
현재 로직상 Comment는 Post가 존재할 때만 만들어 질 수 있으니 @ManyToOne, @JoinColumn에 제약조건을 더 걸어도 될 것 같습니다!
@ManyToOne(fetch = FetchType.LAZY, optional = false)
@JoinColumn(name = "post_id", nullable = false)| @Getter | ||
| public class CommentResponse { | ||
| private final Long id; | ||
| private final String username; | ||
| private final String content; | ||
| private final String createdAt; | ||
|
|
||
| public CommentResponse(Long id, String username, String content, String createdAt) { |
There was a problem hiding this comment.
DTO 적용 좋습니다! 현재 @Getter를 통해 값을 조회하네요! 그런데 이런 경우에 class 대신 record를 쓴다면 코드를 아주 간결하게 작성할 수 있습니다. 불변객체라서 안정적이라는 장점도 있습니다. 저는 개인적으로 DTO는 항상 record로 작성하는 편입니다
|
|
||
| // 특정 게시글 댓글 조회 | ||
| public List<CommentResponse> getCommentsByPost(Long postId) { | ||
| PostEntity postEntity = postRepository.findById(postId).orElseThrow(); |
There was a problem hiding this comment.
값이 없는 상황에서 예외처리의 경우 상황에 맞는 예외 클래스를 던지고, 전역 예외 핸들러를 통해서 예외처리하는 방식으로 작성할 수 있습니다! 나중에 프로젝트 하실 때 같이 고려하시면 될 것 같습니다!
//예시
@Transactional(readOnly = true)
public DiaryEntity findDiaryById(Long diaryId) {
return diaryRepository.findById(diaryId)
.orElseThrow(() -> new NotFoundEntityException(ExceptionCode.NOT_FOUND_DIARY.getDescription()));
}| @Transactional | ||
| public PostEntity updatePost(Long id, PostEntity updated) { | ||
| PostEntity postEntity = postRepository.findById(id).orElseThrow(); | ||
| postEntity.setTitle(updated.getTitle()); | ||
| postEntity.setContent(updated.getContent()); | ||
| return postRepository.save(postEntity); | ||
| } |
There was a problem hiding this comment.
postEntity에 update()함수를 구현해서 해당 함수를 호출해서 값을 업데이트하는 형식으로 사용한다면 postEntity에게 update 책임을 위임할 수 있습니다! 이렇게 구성하면 setter도 제거할 수 있어요
// 예시
public void update(
String musicTitle,
String artist,
String albumImageUrl
) {
updateMusicTitle(musicTitle);
updateArtist(artist);
updateAlbumImageUrl(albumImageUrl);
}
private void updateMusicTitle(String musicTitle) {
if (musicTitle != null && !musicTitle.isBlank()) {
this.musicTitle = musicTitle;
}
}
// ...| <html xmlns:th="http://www.thymeleaf.org"> | ||
| <body> |
There was a problem hiding this comment.
이거는 백엔드에서 꼭 알아야 하는 사항은 아닌데 Thymeleaf 사용하실 때 Thymeleaf Layout을 적용하면 시멘틱 태그 단위로 재사용할 수 있습니다! 나중에 시간이 되면 알아보시면 좋을 것 같아요
| return new CommentResponse( | ||
| comment.getId(), | ||
| comment.getUsername(), | ||
| comment.getContent(), | ||
| comment.getCreatedAt().toString() |
There was a problem hiding this comment.
이 부분은 취향이긴 한데 DTO가 Entity보다 상대적으로 상위 계층이라서 DTO에 Entity를 받는 생성자를 만들어서 간결하게 넘겨주는 방식도 있습니다! 의존성 생기는 게 부담스럽다면 DTO Mapper를 따로 정의해서 사용할 수도 있습니다!
| private PostEntity postEntity; | ||
|
|
||
| // JPA 기본 생성자 | ||
| protected CommentEntity() {} |
There was a problem hiding this comment.
잘 작성해주셨네요! JPA의 지연로딩 때문에 빈 프록시 객체를 만들기 위한 protected 레벨의 기본 생성자가 필요합니다! 이 부분을 다음과 같은 어노테이션 추가해서 생략할 수도 있습니다
@NoArgsConstructor(access = AccessLevel.PROTECTED)|
그리고 승인된 PR은 머지해주시면 됩니다! 병합 후 git pull 후 변경사항 추가 PR 날리는 형식으로 진행하면 지속적으로 브랜치를 업데이트할 수 있습니다. PR이 여러 개 열려 있으면 나중에 병합 시 충돌이 생길 가능성이 높아져서 승인된 변경사항은 바로 병합하시는 걸 추천드립니다! |
변경점 👍
Entity로 바로 연결했던 코드는 DTO를 통해 넘겨받을 수 있도록 수정
1주차에 임시로 연결했던 H2를 MySQL로 변경
버그 해결 💊
이전에 테이블에 likes 속성을 넣어뒀는데 default 값을 주지 않아 오류
likes 값을 지정하지 않아도 default 값을 0으로 설정