Comment
@Entity
@Getter
@NoArgsConstructor(access = AccessLevel.PROTECTED)
public class Comment extends TimeEntity {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "comment_id")
private Long comment_id;
@Column(name = "comment_content")
private String content;
@Column(name = "comment_writer")
private String writer;
@JsonBackReference
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "board_id")
private Board board;
@JsonBackReference
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "user_id")
private User user;
}
User
의 comments
@Entity
@Getter
@NoArgsConstructor(access = AccessLevel.PROTECTED)
public class User {
//...
@JsonManagedReference
@OneToMany(mappedBy = "user", cascade = CascadeType.ALL, orphanRemoval = true)
private List<Comment> comments = new ArrayList<>();
//...
}
Board
의 comments
@Entity
@Getter
@NoArgsConstructor(access = AccessLevel.PROTECTED)
public class Board extends TimeEntity {
//...
@JsonManagedReference
@OneToMany(mappedBy = "board", cascade = CascadeType.ALL, orphanRemoval = true)
private List<Comment> comments;
//...
}
fetch
, mappedBy
, cascade
, orphanRemoval
은 JPA의 중요 개념이기 때문에 따로 공부할 필요가 있습니다! (CommentRepository
public interface CommentRepository extends JpaRepository<Comment, Long> {
List<Comment> findByBoard(Board board);
Optional<Comment> findByUserAndBoard(User user, Board board);
}
List<Comment> findByBoard(Board board);
: 특정 게시판의 모든 댓글 조회Optional<Comment> findByUserAndBoard(User user, Board board);
: 특정 게시판의 특정 사용자가 작성한 단일 댓글 조회CommentFindService
@Service
@RequiredArgsConstructor
public class CommentFindService {
private final CommentRepository commentRepository;
private final BoardFindService boardFindService;
private final UserFindService userFindService;
@Transactional(readOnly = true)
public List<Comment> findAllCommentsInBoard(Long boardId) {
Board board = boardFindService.findById(boardId);
return commentRepository.findByBoard(board);
}
@Transactional(readOnly = true)
public Comment findCommentByUserAndBoard(Long userId, Long boardId) {
User user = userFindService.findById(userId);
Board board = boardFindService.findById(boardId);
return commentRepository.findByUserAndBoard(user, board)
.orElseThrow(() -> new NotFoundCommentException(String.format("There is no comment")));
}
}
BoardFindService
와 UserFindService
를 주입시켰습니다.CommentWriteService
FirebaseCloudMessageService
는 FCM을 통한 댓글 알림 서비스 구현 부입니다. 이 후 포스팅 할 글에 해당합니다.
@Slf4j
@Service
@RequiredArgsConstructor
public class CommentWriteService {
private final CommentRepository commentRepository;
private final UserFindService userFindService;
private final BoardFindService boardFindService;
private final FirebaseCloudMessageService messageService;
@Transactional
public Long writeComment(Long userId, Long boardId, CommentWriteRequest commentWriteRequest) throws FirebaseMessagingException, IOException, ExecutionException, InterruptedException {
Board board = boardFindService.findById(boardId);
User user = userFindService.findById(userId);
Comment comment = Comment.builder() // 중요 부분
.content(commentWriteRequest.getContent())
.writer(user.getName())
.board(board)
.build();
Comment savedComment = commentRepository.save(comment);
user.writeComment(savedComment);
String targetToken = board.getUser().getDeviceToken();
sendMessageToBoardWriter(targetToken, "Comment Notification!", comment.getWriter(), comment.getContent());
return savedComment.getComment_id();
}
private void sendMessageToBoardWriter(String targetToken, String title, String writer, String content) throws FirebaseMessagingException, IOException, ExecutionException, InterruptedException {
messageService.sendMessageTo(targetToken, title, "[" + writer + "]" + "가 댓글 : <" + content + ">을 작성했습니다.");
}
}
@Entity
@Getter
@NoArgsConstructor(access = AccessLevel.PROTECTED)
public class Comment extends TimeEntity {
//...
@Builder
public Comment(String content, String writer, Board board) {
this.content = content;
this.writer = writer;
writtenBoard(board);
}
private void writtenBoard(Board board) {
this.board = board;
board.getComments().add(this);
}
//...
}
@Builder
를 통해 객체를 생성할 때 request
를 통해 넘어온 댓글의 내용과 UserFindService
를 통해 찾은 사용자의 정보를 사용했습니다.writtenBoard()
)를 구현했습니다.user.writeComment(savedComment)
)User
@Entity
@Getter
@NoArgsConstructor(access = AccessLevel.PROTECTED)
public class User {
//...
public void writeComment(Comment comment) {
this.comments.add(comment);
comment.writeUser(this);
}
//...
}
Comment
//...
public void writeUser(User user) {
this.user = user;
}
//...
}
CommentUpdateService
@Service
@RequiredArgsConstructor
public class CommentUpdateService {
private final CommentFindService commentFindService;
@Transactional
public Long updateComment(Long userId, Long boardId, CommentUpdateRequest commentUpdateRequest) {
Comment comment = commentFindService.findCommentByUserAndBoard(userId, boardId);
return comment.update(
commentUpdateRequest.getContent()
);
}
}
CommentDeleteService
@Service
@RequiredArgsConstructor
public class CommentDeleteService {
private final CommentFindService commentFindService;
private final CommentRepository commentRepository;
@Transactional
public void deleteComment(Long userId, Long boardId) {
Comment comment = commentFindService.findCommentByUserAndBoard(userId, boardId);
commentRepository.deleteById(comment.getComment_id());
}
}
참고 : 댓글이 삭제될 경우, 게시판과 사용자 측면에서 조회할 수 없게 되지만, 사용자나 게시판이 삭제될 경우 그에 해당하는 댓글은 저절로 삭제가 됩니다. ⇒ 연관관계를 맺을 때 옵션을 사용했기 때문입니다.
API 구현 부는 앞선
User
,Board
와 매우 유사한 구조이기 때문에 생략하겠습니다.
특히, 필요한 정보는@PathVariable
및@RequestBody
+XXXRequest
클래스의 파라미터를 통해 얻을 수 있도록 구현했습니다.
You've come this far and I think that's great, keep chasing your dreams and one day you'll reach the top and then you'll see how great you've become . bitlife game