대댓글 구현하기 (SpringBoot)

박지성 학부생·2024년 2월 8일
1

BackEnd Develop log

목록 보기
9/27

Velog 클론코딩과정에서 작성한 대댓글 구현 방법이다.
엔티티 설계를 중점으로 대댓글 기능을 위해 어떻게 ERD를 설계해야하는지에 대한 설명이다.

엔티티 관계도 결과

설명(Comment 댓글, Post 글작성 중심으로)

Post Entity

package com.velog.velog_backend.post.domain;

import com.velog.velog_backend.comment.domain.Comment;
import com.velog.velog_backend.common.Timestamped;
import com.velog.velog_backend.member.domain.Member;
import jakarta.persistence.*;
import lombok.*;

import java.util.ArrayList;
import java.util.List;

@Builder
@Getter
@NoArgsConstructor
@AllArgsConstructor
@Table(name = "posts")
@Entity
public class Post extends Timestamped {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "post_id", updatable = false)
    private Long id;

    @Column(name = "title")
    private String title;

    @Lob
    @Column(name = "content", length=3000)
    private String content;

    @Column(name = "likes")
    private Long likes;

    @Column(name = "thumbnail")
    private String thumbnail;

    @ElementCollection
    @Builder.Default
    @CollectionTable(name = "tags", joinColumns = @JoinColumn(name = "post_id"))
    private List<String> tagList = new ArrayList<>();

    @OneToMany(fetch = FetchType.LAZY, mappedBy = "post", cascade = CascadeType.ALL, orphanRemoval = true)
    private List<Comment> comments;

    @JoinColumn(name = "member_id", nullable = false)
    @ManyToOne(fetch = FetchType.LAZY)
    private Member member;

    public void update(String title, String content, String thumbnail, List<String> tagList) {
        this.title = title;
        this.content = content;
        this.thumbnail = thumbnail;
        this.tagList.clear(); // 기존 태그 리스트를 클리어하고 새로운 태그로 대체
        if (tagList != null) {
            this.tagList.addAll(tagList);
        }
    }
}

Comment Entity

package com.velog.velog_backend.comment.domain;

import com.velog.velog_backend.comment.dto.request.CommentRequestDTO;
import com.velog.velog_backend.common.Timestamped;
import com.velog.velog_backend.post.domain.Post;
import com.velog.velog_backend.member.domain.Member;
import jakarta.persistence.*;
import lombok.*;
import java.util.ArrayList;
import java.util.List;

@Builder
@Getter
@NoArgsConstructor
@AllArgsConstructor
@Table(name = "comments")
@Entity
public class Comment extends Timestamped {

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

    @JoinColumn(name = "member_id", nullable = false)
    @ManyToOne(fetch = FetchType.LAZY)
    private Member member;

    @JoinColumn(name = "post_id", nullable = false)
    @ManyToOne(fetch = FetchType.LAZY)
    private Post post;

    @Column(nullable = false)
    private String content;

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "parent_id")
    private Comment parent;

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

    public void updateContent(String content) {
        this.content = content;
    }

}

게시글(Post)과 댓글(Comment)

Post 엔티티는 여러 Comment 엔티티를 가질 수 있으며, 이 관계는 @OneToMany 어노테이션을 사용하여 정의된다. 이는 하나의 게시글에 여러 댓글이 달릴 수 있음을 의미한다.
Comment 엔티티는 post 필드를 통해 특정 Post 엔티티에 속하며, 이는 @ManyToOne 어노테이션으로 마크된다. 이는 댓글이 특정 게시글에 속함을 나타낸다.

댓글(Comment)과 대댓글(Child Comments) => 핵심

댓글과 대댓글의 관계를 구현하기 위해 Comment 엔티티는 재귀적인 관계를 사용한다. 이는 댓글이 다른 댓글을 부모로 가지며, 또한 자신의 대댓글을 가질 수 있게 하는 구조이다.

부모 댓글(Parent Comment)

parent 필드: 이 필드는 @ManyToOne 관계를 사용하여, 하나의 댓글이 다른 댓글에 대한 대댓글이 될 수 있도록 한다. 즉, 어떤 댓글이 다른 댓글의 '자식'이 되기 위해, 그 '부모' 댓글을 이 필드를 통해 참조한다.

예를 들어, 댓글 B가 댓글 A에 대한 대댓글이라면, B의 parent 필드는 A를 참조한다.

자식 댓글(Child Comments)

children 필드: 반면에, children 필드는 @OneToMany 관계를 사용하여, 하나의 댓글이 여러 개의 대댓글을 가질 수 있게 한다. 이는 한 댓글이 '부모'가 되어 다수의 '자식' 댓글을 가지는 구조이다. 댓글 A가 댓글 B와 C의 부모라면, A의 children 필드에는 B와 C가 리스트 형태로 저장된다.

예를 들어, 댓글 A에 대한 대댓글로 B를 작성하고, 다시 B에 대한 대댓글로 C를 작성할 수 있다. 이때, A는 B의 부모이며, B는 C의 부모이다. 이런 방식으로 댓글과 대댓글 사이의 관계가 정의할 수 있다.

예시

사용자가 게시글에 댓글 A를 단다.
다른 사용자가 댓글 A에 대해 댓글 B를 작성하여, A의 대댓글이 된다.
(B의 parent = A)
또 다른 사용자가 댓글 B에 대해 댓글 C를 작성하여, B의 대댓글이 된다.
(C의 parent = B)

profile
참 되게 살자

0개의 댓글