Conversation
KoSeonJe
left a comment
There was a problem hiding this comment.
고생하셨어요~
코멘트 남겼는데,,,,, 조금 많네요.
그래도 알아야할 개념만 남겼습니다! 고민해보세요! 답글은 괜찮아요~ㅎㅎ
자신이 작성한 코드에 의문을 계속해서 가져보면 좋을 것 같아요!
|
|
||
| @RestController | ||
| @RequiredArgsConstructor | ||
| @RequestMapping |
There was a problem hiding this comment.
해당 어노테이션은 무슨 역할을 하나요!? 적절하게 사용하고 계시는지 다시 한번 고민해주세요!
There was a problem hiding this comment.
@RequestMapping을 클래스 단위로 사용했을 때 그 아래에 있는 메서드들은 @RequestMapping의 url을 공통 경로로 해서 라우팅이 이루어지게 됩니다. 이전에는 따로 경로를 명시해주지 않았기 때문에 모든 메서드가 /를 기준으로 경로를 계산해서 딱히 의미가 없었네요! 수정했습니다 336c6d8
pr 본문에도 적어두었는데, 특정 게시글의 댓글을 조회할 때의 엔드포인트는 /comments/post/{postId} 와/post/{postId}/comments 중 어느 것이 더 좋은 방식인가요? (저는 게시글 안에 댓글이 있는거라고 생각해서 후자가 더 나은 것 같은데 👀)
There was a problem hiding this comment.
말씀하신것처럼 게시글 안에 댓글이라는 계층이 있으니 posts/{postId}/comments 가 좋아보이네요!
There was a problem hiding this comment.
아 답장이 늦어졌네요~ 저도 posts/{postId}/comments가 좋아보여요!
아래 글 중 5번을 읽어보면 좋을 것 같네요~!
Restful하기 위한 6가지 원칙
| import java.util.List; | ||
|
|
||
| @RestController | ||
| @RequiredArgsConstructor |
There was a problem hiding this comment.
해당 어노테이션은 어떤 필드를 대상으로 생성자를 생성해주나요?
이 외에 생성자를 자동으로 생성하는 어노테이션은 어떤 것들이 있나요!?
There was a problem hiding this comment.
@RequiredArgsConstructor는 final 또는 @nonnull이 붙은 필드에 대해 자동으로 생성자를 생성해줍니다!
그래서 private final CommentService commentService; 의 생성자를 lombok이 자동으로 생성해주게 됩니다.
생성자를 자동으로 생성해주는 어노테이션은
- @NoArgsConstructor : 파라미터가 없는 기본 생성자를 생성합니다.
- @AllArgsConstructor : 모든 필드를 파라미터로 받는 생성자를 생성합니다.
- 모든 필드를 수동으로 초기화할 필요가 있을 때 사용합니다.
|
|
||
| import java.util.List; | ||
|
|
||
| @RestController |
There was a problem hiding this comment.
@RestController와 @Controller의 차이는 무엇인가요!?
There was a problem hiding this comment.
@RestController는 이름에서부터 REST API에 적합한 어노테이션임을 알 수 있습니다. 메서드의 리턴값이 http 응답 body로, 보통은 json 형태로 전송됩니다. 해당 어노테이션이 붙은 클래스는 @Controller와 @ResponseBody가 합쳐진 형태가 되게 됩니다.
@Controller는 리턴값이 view로 처리되게 되며, json 등의 데이터 자체를 응답하기 위해서는 메서드에 @ResponseBody를 추가로 붙여야합니다.
| public List<CommentResponse> getComments(@PathVariable Long postId) { | ||
| return commentService.getCommentsByPost(postId); |
There was a problem hiding this comment.
controller 내부에서는 다른 건 생각하지 않고 기본적인 crud에 맞춰 생각하느라 다르게 지었던 것 같아요!.. 좀 더 명확하게 service 내부 메서드와 메서드명을 getCommentsByPost로 일치시켰습니다! 89124e5
| private Long postId; | ||
|
|
||
| public Comment toEntity(Member member, Post post) { | ||
| return Comment.builder().content(content).member(member).post(post).build(); |
There was a problem hiding this comment.
| return Comment.builder().content(content).member(member).post(post).build(); | |
| return Comment.builder() | |
| .content(content) | |
| .member(member) | |
| .post(post) | |
| .build(); |
가독성을 위해 줄바꿈~!
| } | ||
|
|
||
| public List<CommentResponse> getCommentsByPost(Long postId){ | ||
| return commentRepository.findByPostId(postId).stream().map(CommentResponse::from).toList(); |
There was a problem hiding this comment.
줄바꿈 해주세요! 가독성을 위해
| return commentRepository.findByPostId(postId).stream().map(CommentResponse::from).toList(); | |
| return commentRepository.findByPostId(postId).stream() | |
| .map(CommentResponse::from) | |
| .toList(); |
| @OneToMany(mappedBy = "member", cascade = CascadeType.ALL) | ||
| private List<Post> posts = new ArrayList<>(); | ||
|
|
||
| @OneToMany(mappedBy = "member", cascade = CascadeType.ALL) | ||
| private List<Comment> comments = new ArrayList<>(); |
There was a problem hiding this comment.
연관관계를 양방향으로 선언해주신 이유가 궁금해요~
어떤 장단점이 있고, 어떤 경우에 써야 할까요??
There was a problem hiding this comment.
양방향으로 매핑을 해주었을 때, 각각 하나의 테이블을 기준으로 연결되어있는 다른 테이블을 조회할 수 있다고 알고있어서 이런식으로 구현해보았습니다! (ex. 멤버가 작성한 게시글 리스트를 조회하고 싶을 때)
-
장점
- 앞서 말한대로 테이블을 조회할 때 편리합니다.
- 자식 엔티티를 일괄 삭제할 때 관리가 편리합니다.
-
단점
- 양쪽 관계를 유지할 때 필요합니다.
- 연관관계를 설정하는 복잡도가 증가하게 됩니다.
-
양방향을 써야하는 경우
- 연관된 데이터를 같이 자주 조회할 때 사용하게 됩니다.
| @Column(name="post_id") | ||
|
|
||
| private Long id; |
There was a problem hiding this comment.
| @Column(name="post_id") | |
| private Long id; | |
| @Column(name="post_id") | |
| private Long id; |
ㅎㅎ
| @OneToMany(mappedBy = "post", cascade = CascadeType.ALL, orphanRemoval = true) | ||
| private List<Comment> comments = new ArrayList<>(); |
There was a problem hiding this comment.
cascade와 orphanRemoval은 어떤 차이가 있나요?
There was a problem hiding this comment.
-
cascade: 부모와 연결된 자식에게도 똑같은 작업을 한다는 의미가 있습니다.- 부모 저장 -> 자식 저장 / 부모 수정 -> 자식 수정 / 부모 삭제 -> 자식 삭제
-
orphanRemoval: true일 때 부모-자식 관계가 끊어진 자식의 경우, 자동으로 삭제되게 한다는 의미가 있습니다.
| @ManyToOne | ||
| @JoinColumn(name = "member_id", nullable = false) | ||
| private Member member; | ||
|
|
||
| @OneToMany(mappedBy = "post", cascade = CascadeType.ALL, orphanRemoval = true) | ||
| private List<Comment> comments = new ArrayList<>(); |
There was a problem hiding this comment.
@ManyToOne과 @OneToMany의 기본 fetch 전략은 어떻게 될까요?
(지연로딩 혹은 즉시로딩)
알맞게 설정해주면 좋을 것 같아요~
There was a problem hiding this comment.
@ManyToOne의 기본 fetch 전략은 즉시로딩으로, post를 조회하면 member도 즉시 조회가 됩니다.
@OneToMany의 기본 fetch 전략은 지연로딩으로, post를 조회할 때 comments는 조회되지 않고, post.getComments()를 하는 시점에 조회할 수 있습니다.
찾아보았을 때 여러 post를 조회했을 때 연관된 member까지 매번 불러오면 불필요한 쿼리가 많아질 수 있기 때문에 ManyToOne의 경우에도 LAZY로 바꾸는 것이 좋다고 하는데 맞나요?.?
There was a problem hiding this comment.
맞아요! 실행해보시면 아시겠지만, 즉시로딩일 때에는 Member와 Join을 하여 member의 정보를 항상 모두 불러옵니다!
그래서 Lazy로 fetch 전략을 바꾸는 것이 좋아요! 즉시 로딩을 관리하기도 힘들기도하니까요~
하지만, Post를 사용할때 항상 Member를 사용한다면, 어떻게 될까?
지연로딩된 객체의 데이터를 조회하기위해 쿼리를 항상 하나 추가로 날리게됩니다. 어쩌면 join보다 더 비용이 많이 들 수 있겠네요
그래서 이를 해결하기위해 fetch join 혹은 entityGraph를 통해 직접 쿼리를 커스텀해줘야합니다!
잘 관리할 수 있다면 EAGER로 선언해도 되구요!
이것도 잘 이해가 안되면 멘토 콜!
There was a problem hiding this comment.
fetch join 방식을 사용해 postId로 comment를 찾는 api가 있어야해서 해당 부분을 수정했습니다! 174a9e1
말씀해주셨던 post-member의 관계에서는 memberId로 post를 찾는다는가 그런 api는 없어가지구 @manytoone 에 LAZY를 달아두기만 했습니다!.!
추가 Q & A
🤔 잘 모르겠거나, 더 궁금한 내용이 있었나요?
집중적으로 리뷰 받고싶은 것이 있나요?