전체 코드는 여기에서 볼 수 있습니다.
오늘은 게시물에 댓글 기능을 적용해보도록 하겠습니다.
- Controller, Service, Entity, Repository, DTO
- CommentController
@RestController
@RequiredArgsConstructor
public class CommentController {
private final CommentService commentService;
@PostMapping("/post/{id}/comment")
@ResponseStatus(value = HttpStatus.OK)
@PreAuthorize("isAuthenticated()")
@ApiOperation(value = "댓글 쓰기", notes = "id에 해당하는 게시글에 댓글을 작성합니다.")
public SuccessResponse<String> createComment(@PathVariable(name = "id") Long postId,
@Valid @RequestBody CommentDTO commentDTO,
Principal principal) {
CommentDTO comment = commentService.createComment(postId, commentDTO, principal.getName());
return SuccessResponse.success(null);
}
}
- CommentService
@Service
@RequiredArgsConstructor
@Transactional(readOnly = true)
public class CommentService {
private final CommentRepository commentRepository;
private final PostRepository postRepository;
private final UserRepository userRepository;
@Transactional
public CommentDTO createComment(Long postId, CommentDTO commentDTO, String email) {
Optional<Post> byId = postRepository.findById(postId);
Post post = byId.orElseThrow(() -> new PostNotFound("게시물이 삭제되었거나 존재하지 않습니다."));
Optional<User> byEmail = userRepository.findByEmail(email);
User user = byEmail.orElseThrow(() -> new UsernameNotFoundException("유저를 찾을 수 없습니다."));
Comment comment = Comment.builder()
.contents(commentDTO.getContents())
.build();
comment.mappingPostAndUser(post, user);
Comment saveComment = commentRepository.save(comment);
return CommentDTO.convertToCommentDto(saveComment);
}
}
- CommentEntity
@Entity
@Getter
@Builder
@AllArgsConstructor(access = PROTECTED)
@NoArgsConstructor(access = PROTECTED)
public class Comment {
@Id
@GeneratedValue(strategy = IDENTITY)
private Long id;
@Column(nullable = false, length = 100)
private String contents;
@ManyToOne(fetch = LAZY)
@JoinColumn(name = "post_id", foreignKey = @ForeignKey(name = "FK_user_comment"))
private Post post;
@ManyToOne(fetch = LAZY)
@JoinColumn(name = "user_id", foreignKey = @ForeignKey(name = "FK_post_comment"))
private User user;
public void mappingPostAndUser(Post post, User user) {
this.post = post;
this.user = user;
post.mappingComment(this);
user.mappingComment(this);
}
}
- CommentRepository
@Repository
public interface CommentRepository extends JpaRepository<Comment, Long> {
}
- CommentDTO
@Builder
@AllArgsConstructor(access = AccessLevel.PROTECTED)
@RequiredArgsConstructor(access = AccessLevel.PROTECTED)
@Data
public class CommentDTO {
private Long id;
@NotBlank
@Length(max = 100)
private String contents;
private UserDTO user;
public static CommentDTO convertToCommentDto(Comment comment) {
return CommentDTO.builder()
.id(comment.getId())
.contents(comment.getContents())
.user(UserDTO.convertToUserDTO(comment.getUser()))
.build();
}
public static List<CommentDTO> convertToCommentDtoList(List<Comment> commentList) {
Stream<Comment> stream = commentList.stream();
return stream.map(CommentDTO::convertToCommentDto).collect(Collectors.toList());
}
}
여기 까지가 Comment 관련 코드이고 이제 Comment와 Post, User와 관계를 지정해주어야 합니다.
Comment와 Post는 N : 1 관계, Comment와 User는 N : 1 관계 입니다. 그러므로 PostEntity와 UserEntity에 연관관계및 연관관계 메서드를 설정해줍니다.
- PostEntity
@OneToMany(fetch = LAZY, mappedBy = "post")
private List<Comment> commentList = new ArrayList<>();
public void mappingComment(Comment comment) {
this.commentList.add(comment);
}
- UserEntity
@OneToMany(fetch = FetchType.LAZY, mappedBy = "user")
private List<Comment> commentList = new ArrayList<>();
public void mappingComment(Comment comment) {
commentList.add(comment);
}
그리고 이제 게시물에 댓글을 달고 게시물을 조회하고 나면 쿼리가 무언가 이상합니다.
1번 게시물에 1번 유저, 2번 유저, 3번 유저가 댓글을 달경우 1번 게시물을 조회할때 1, 2, 3번 유저를 조회하는 쿼리가 나가기 때문입니다.
이를 해결하기 위해 저번에 작업한 PostRepository에 최적화 작업을 하겠습니다.
- PostRepository
@Repository
public interface PostRepository extends JpaRepository<Post, Long> {
@EntityGraph(attributePaths = {"category", "user", "commentList.user"})
@Override
Optional<Post> findById(Long postId);
@EntityGraph(attributePaths = {"user", "category"})
@Override
List<Post> findAll(Sort sort);
}
@EntityGraph에 "commentList.user"를 추가해줌으로써 CommentList와 여기에 댓글을 작성한 유저의 정보까지 한번에 가져오므로 쿼리 1번에 모두 조회가 가능합니다.
'스프링 부트' 카테고리의 다른 글
스프링부트 게시판 API 만들기 - 12 (게시물 검색기능 및 QueryDSL) - 2 (0) | 2021.05.03 |
---|---|
스프링부트 게시판 API 만들기 - 11 (게시물 검색기능 및 QueryDSL) - 1 (0) | 2021.05.01 |
스프링부트 게시판 API 만들기 - 9 (유저와 게시물 연관시키기 및 쿼리 최적화) (0) | 2021.04.29 |
스프링부트 게시판 API 만들기 - 8 (API 문서 Swagger 사용하기) (0) | 2021.04.28 |
스프링부트 게시판 API 만들기 - 7 (게시물 카테고리 적용하기) (0) | 2021.04.27 |