skarltjr / Memory_Write_Record

나의 모든 학습 기록
0 stars 0 forks source link

Issue - 커뮤니티 댓글 #88

Open skarltjr opened 2 years ago

skarltjr commented 2 years ago

1. 기존 comment entity

@Entity
@Table(name = "comments")
class Comment(
    @field:Id
    @field:GeneratedValue(strategy = GenerationType.IDENTITY)
    val id: Long?,

    @field:ManyToOne(fetch = FetchType.LAZY)
    @field:JoinColumn(name = "article_id")
    val article: Article,

    @field:ManyToOne(fetch = FetchType.LAZY)
    @field:JoinColumn(name = "user_id")
    val user: User,

    @field:ManyToOne(fetch = FetchType.LAZY)
    @field:JoinColumn(name = "parent_id")
    val comment: Comment? = null,

    @field:Lob
    @field:Column(name = "content")
    var content: String,

    @field:Column(name = "is_deleted", nullable = false)
    var isDeleted: Boolean = false,

    @field:Column(name = "display_order", nullable = false)
    var displayOrder: Int

) 

2. 개요

문제가 될 수 있는 댓글을 완전히 지우지 않고 보관하기위해 프로젝트에서 댓글은 soft delete로 진행하기로 했다.
따라서 삭제 요청시 isDeleted flag를 변경하며 댓글과 관련된 게시글의 총 댓글개수는 감소시키는 방식으로 가져갔다.

그런데 클라이언트측에서 삭제된 댓글은 아예 정보를 클라이언트에게 안넘겨줬으면 한다고 요청했다.
따라서 우리가 전달해야할 댓글 종류를 정리해보면 
1. 모든 부모 댓글 중 삭제되지 않은 부모 댓글
2. 모든 삭제된 부모댓글 중 대댓글이 하나도 없는 부모 댓글
3. 모든 삭제되지 않은 대댓글

3. 기존방식

no offset paging으로 마지막 댓글 id를 전달받고 이 후 댓글30개에 각각 대댓글 10개씩을 가져온다
이 때
1. 현재 게시글을 참조하는 모든 댓글 중 (현재 게시글의 부모댓글+대댓글 중)
2. 부모 댓글이라면 마지막 댓글 이후 30개 
3. 자식 댓글이라면 부모 댓글의 displayOrder가 마지막 댓글 ~  이후 30개에 속하고 자식 댓글 자체의 displayOrder는 처음 10까지인 애들
을 모두 한 번에 가져온다.

4. 이 후 (부모 댓글을 키 / 자식댓글을 위한 mutableList) map에 정보를 넣고 이를 돌면서 response를 만든다.

4. 문제

여기서 문제는 지금까지 우리가 isDeleted==true인 애들도 다 보내줬지만 이제는 아래만 선별하여 보내줘야한다는것

  1. 모든 부모 댓글 중 삭제되지 않은 부모 댓글
  2. 모든 삭제된 부모댓글 중 대댓글이 하나도 없는 부모 댓글
  3. 모든 삭제되지 않은 대댓글 

5. 고민

1. 완전삭제
   - 기획을 따르기로 했고 soft delete를 유지하기로 했다.

2. 단순히 isDeleted == true면 안가져온다?
   - 삭제된 부모댓글이어도 자식댓글이 하나라도 존재하면 "삭제된 댓글입니다"로 표현하기위해 전달해줘야한다.

3. 그럼 조건에 해당되는 부모댓글 다 가져온 후 대댓글을 추가로가져오면?
   - 부모댓글 30개마다 한 번의 쿼리가 발생

목표 : 부모댓글을 다 가져온 후 이에 관련된 자식 댓글을 한 번에 가져오기.   

6. 해결법

솔직히 팀원분들과 아무리 생각을해봐도 원하는 답을 찾을 수 없어서 기획 변경을 요청할까하다가 아래 글을 찾았따.
- https://stackoverflow.com/questions/2129693/using-limit-within-group-by-to-get-n-results-per-group

우선 조건에 부합하는 30개의 부모 댓글을 가져온 후 각각 자식 댓글을 가져올 때 아래 쿼리를 활용

적용한 네이티브 쿼리
WHERE parent_id IN :parents AND is_deleted = false에서 parents는 파라미터로 받은 조건에 부합한 부모 댓글 리스트
--
SELECT
    c.*
FROM
    comments AS c JOIN (
        SELECT parent_id, GROUP_CONCAT(id) as recomments
        FROM comments
        WHERE parent_id IN :parents AND is_deleted = false
        GROUP BY parent_id
    ) group_max
ON c.parent_id = group_max.parent_id
AND FIND_IN_SET(id, recomments) BETWEEN 1 AND 10
ORDER BY c.parent_id AND c.id; 

7. 분석

1. 자식댓글을 부모 댓글(부모 댓글id)별로 그룹화
  - 그럼 이전에 먼저 구한 조건에 부합하는 30개의 부모댓글별로 자식 댓글을 가져오는데
  - ex ) 
     부모 : 자식 댓글들  
     1 :  11,12,13,14,15..
     2:  21,22,23,24,25...
    ... 
    30 : 301,302,303 ...  
2.   AND FIND_IN_SET(id, recomments) BETWEEN 1 AND 10
그 중에서도 각 자식들의 id로 find in set했을 때 1~10번 이내의 애들만

즉
- 먼저 조건에 부합하는 부모 댓글을 찾고 이를 매개변수로 넘겨준다
- 해당 부모댓글마다 삭제되지 않은 자식 댓글을 찾아온다
- 그 중에서도 앞에서부터 1~10번. 10개의 대댓글만 가져온다.