[팀프로젝트] 댓글 작성 기능

Sol's·2023년 1월 20일
0

팀프로젝트

목록 보기
3/25

crew모임글에 댓글을 작성하는 로직을 추가해 보겠습니다.

댓글 작성 기능

Emtity

  • 한명의 User가 여러게의 Comment를 작성할 수 있다.
  • 하나의 crew에 여러개의 Commnet를 작성할 수 있다.
@Entity
@AllArgsConstructor
@NoArgsConstructor
@Getter
@Setter
@Builder
public class Comment extends BaseEntity{

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    private String comment;

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "user_id") //외래키 : DB에 해당 명으로 컬럼설정
    private User user;

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "post_id") //DB에 해당 명으로 컬럼설정
    private Post post;
}

Controller

Authentication에서 UserName을 가져와 사용했습니다.
게시물에 댓글을 달것이여서 PostId를 PathVariable로 받아 service로 넘겨주었습니다.

    //댓글 작성
    @PostMapping("/{crewId}/comments")
    public Response<CommentResponse> addComment(@RequestBody CommentRequest commentRequest, @PathVariable Long crewId, Authentication authentication) {
        return Response.success(commentService.addComment(commentRequest, crewId, authentication.getName()));
    }

  

Dto

@AllArgsConstructor
@NoArgsConstructor
@Getter
public class CommentReplyRequest {
    private String comment;

    public Comment toEntity(User user, Crew crew, Comment parentComment) {
        return Comment.builder()
                .comment(this.comment)
                .user(user)
                .crew(crew)
                .parent(parentComment)
                .build();
    }
}
@AllArgsConstructor
@NoArgsConstructor
@Getter
@Builder
public class CommentResponse {
    private Long id;
    private String comment;
    private String userName;
    private Long crewId;

    private LocalDateTime createdAt;

    public static CommentResponse of(Comment comment) {
        return CommentResponse.builder()
                .id(comment.getId())
                .comment(comment.getComment())
                .userName(comment.getUser().getUsername())
                .crewId(comment.getCrew().getId())
                .createdAt(comment.getCreatedAt())
                .build();
    }
}

Service

public CommentResponse addComment(CommentRequest commentRequest, Long crewId, String userName) {
        User user = getUser(userName);
        Crew crew = getCrew(crewId);

        Comment comment = commentRepository.save(commentRequest.toEntity(user, crew));

        return CommentResponse.of(comment);
    }
  1. Conmmet는 우선 User가 저장되어야 하니, Authentication에서 받아온 username을 통해 User를 찾습니다.
  2. crew에 댓글을 달것이니 crew도 찾습니다.
  3. 그리고 user와 crew가 모두 존재한다면 Comment를 dto로 받아온 정보를 통해 저장합니다.
  4. CommentResponse.of(comment)메소드로 리턴을 합니다.

Repositoy

public interface CommentRepository extends JpaRepository<Comment, Long> {
    Page<Comment> findByCrewId(Long id, Pageable pageable);
    Page<Comment> findByCrewIdAndParentId(Long id, Pageable pageable, Long parentId);
}

List를 조회할떄 Pageing 처리를 해주기 위해 따로 JPA메소드를 설정해 주었습니다.
추가적으로 대댓글의 정보도 볼 수 있도록 쿼리문을 하나 더 추가시켜 주었습니다.

이제 댓글을 작성 할 수 있습니다.

대댓글

댓글기능을 만들었으니 대댓글도 만들어 보겠습니다.

처음에는 대댓글이라는 엔티티를 하나 더 만들 생각이었습니다.
하지만 댓글과 같은기능의 엔티티를 하나 더만들 필요가 있을까? 라는 생각이 들었고
구글링을 통해 셀프참조라는 방법을 알게되었습니다.

셀프조인

셀프 조인(Self Join)이란 동일 테이블 사이의 조인을 말합니다.

Entity

@Entity
@NoArgsConstructor
@AllArgsConstructor
@Getter
@Builder
@Where(clause = "deleted_at is null")
public class Comment extends BaseEntity{
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    private String comment;

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "user_id")
    User user;

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "crew_id")
    Crew crew;
    
    public void setComment(String comment) {
        this.comment = comment;
    }

	// 아래 추가
    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "parent_id")
    private Comment parent;

    @OneToMany(mappedBy = "parent", orphanRemoval = true)
    private List<Comment> children = new ArrayList<>();

   
}

셀프 조인을 하기위해서 추가된것은 @ManyToOne@OneToMany을 통한 연관관계 맵핑밖에 없습니다.
대댓글을 작성할때, 부모인 댓글의 정보를 parent_id에 넣으면 됩니다.

Controller

 @PostMapping("/{crewId}/comments/{parentCommentId}")
    public Response<CommentReplyResponse> addCommentReply(@RequestBody CommentReplyRequest commentReplyRequest, @PathVariable Long crewId, @PathVariable Long parentCommentId, Authentication authentication) {
        return Response.success(commentService.addCommentReply(commentReplyRequest, crewId, parentCommentId, authentication.getName()));
    }

대댓글을 작성하기 위해서는 Entity에 parent필드에 들어가 부모댓글의 id도 알고있어야 합니다.
따라서 @PathVariable을 통해 Comment의 정보도 받아옵니다.

service

public CommentReplyResponse addCommentReply(CommentReplyRequest commentReplyRequest, Long crewId, Long parentCommentId, String userName) {
        User user = getUser(userName);
        Crew crew = getCrew(crewId);
        //추가
        Comment parentComment = getComment(parentCommentId);

        Comment comment = commentRepository.save(commentReplyRequest.toEntity(user, crew, parentComment));

        return CommentReplyResponse.of(comment);
    }

기존 댓글에서 추가된 로직은 대댓글을 작성할 댓글을 찾는것입니다.
그리고 저장할때 찾은 Comment를 parent로 저장하면서 대댓글임을 표시하면 됩니다.

대댓글 조회

Controller

 @GetMapping("/{crewId}/comments/{parentCommentId}/list")
    public Response<Page<CommentReplyResponse>> getCommentReplyList(@PathVariable Long crewId, Long parentCommentId, Pageable pageable) {
        return Response.success(commentService.getCommentReplyList(pageable, crewId, parentCommentId));
    }

대댓글을 조회할 것이기 때문에, crewId와 commentId를 받아옵니다.

Service

public Page<CommentReplyResponse> getCommentReplyList(Pageable pageable, Long crewId, Long parentCommentId) {
        Page<Comment> list = commentRepository.findByCrewIdAndParentId(crewId, pageable, parentCommentId);
        return list.map(CommentReplyResponse::of);
    }

받아온 정보을 통해 정보를 찾고 .map을 통해 Comment를 CommentReplyResponse로 변경하여 리턴 합니다.

Dto

@AllArgsConstructor
@NoArgsConstructor
@Getter
@Builder
public class CommentReplyResponse {
    private Long id;
    private String comment;
    private String userName;
    private Long crewId;
    private Long parentId;

    private LocalDateTime createdAt;

    public static CommentReplyResponse of(Comment comment) {
        return CommentReplyResponse.builder()
                .id(comment.getId())
                .comment(comment.getComment())
                .userName(comment.getUser().getUsername())
                .crewId(comment.getCrew().getId())
                .createdAt(comment.getCreatedAt())
                .parentId(comment.getParent().getId())
                .build();
    }
}

해결해야할 문제

  1. 댓글을 작성하면 parentId가 null로 되어 문제가 있음.
    • 조회를 하면 ParentId를 조회 할 수 없다.
      - 널값이여서 nullPoint에러가 난다.
  2. 어떤식으로 대댓글을 보여줄지
    • 댓글을 나열하고 그 사이에 대댓글 리스트를 나열
    • 댓글과 대댓글을 한꺼번에 정렬하여 나열
profile
배우고, 생각하고, 행동해라

0개의 댓글