-
Notifications
You must be signed in to change notification settings - Fork 0
[테크블로그] 무한 대댓글
MoonMinHyuk1 edited this page Jun 5, 2023
·
3 revisions

all_children_size
- 해당 댓글에 해당하는 모든 하위 댓글의 수입니다.
level
- 댓글의 계층을 나타냅니다. (댓글 : 1, 대댓글 : 2, 대대댓글 : 3, …)
ref
- 최상위 댓글의 고유 번호입니다.
step
- 댓글, 대댓글, 대대댓글 등 계층에 상관없이 위에서부터의 순서를 나타냅니다.
- 댓글 조회 시 정렬에 사용됩니다.
parent_id
- 해당 댓글의 부모 댓글 아이디입니다.
- 최상위 댓글의 경우 null값이 저장됩니다.
댓글 예시
@Transactional
override fun saveRecordComment(recordCommentReqDto: RecordCommentReqDto, user: User): Long {
val record = recordRepository.findByIdOrThrow(recordCommentReqDto.recordId)
var parent: RecordComment? = null
val refCount = recordCommentRepository.findNextRef(record)
var ref: Long = (refCount + 1).toLong()
var step: Long = 1
var level: Long = 1
val allChildrenSize: Long = 0
if(recordCommentReqDto.parentId != null) {
parent = recordCommentRepository.findByIdOrThrow(recordCommentReqDto.parentId)
val parentStep: Long = parent.step
val parentAllChildrenSize: Long = parent.allChildrenSize
step = parentStep + parentAllChildrenSize + 1
ref = parent.ref
level = parent.level + 1
val comments = recordCommentRepository.findSameRef(record, ref)
updateAllChildrenSize(parent)
updateStep(comments, step)
}
return recordCommentRepository.save(
RecordComment(recordCommentReqDto.content, record, user, ref, step, level, allChildrenSize, parent)
).id
}
parent_id가 없는 경우 (최상위 댓글인 경우)
- parent 에 null을 저장합니다. (parent_id에는 null값이 저장)
- step, level에 1, all_children_size에 0을 저장합니다.
- ref는 최상위 댓글이 가지는 고유 번호이기 때문에 record의 댓글 중 최대 값의 +1 해준 값을 저장합니다.
parent_id가 있는 경우 (하위 댓글인 경우)
-
parent에 부모 댓글의 객체를 저장합니다. (parent_id에는 parent의 id값이 저장)
-
step에는 (부모 댓글의 step 값) + (부모 댓글의 모든 하위 댓글 수) +1 해준 값을 저장합니다. 부모 댓글의 모든 하위 댓글 다음에 위치해야 하기 때문입니다.
-
ref는 최상위 댓글이 가지는 고유 번호이기 때문에 부모 댓글의 ref값을 저장합니다.
-
level은 부모 댓글보다 한단계 깊은 계층이기 때문에 부모 댓글 level에 +1 해준 값을 저장합니다.
-
하위 댓글은 댓글의 어디 들어가든 간에 ref가 같은 댓글 중 상당 수의 댓글의 all_children_size나 step값을 변경해주어야 합니다. 그렇기 때문에 같은 ref를 가지는 댓글을 전체 조회합니다.
-
ref가 같은 댓글 중 parent댓글을 타고 올라가면서 해당 댓글의 all_children_size를 +1 해줍니다.
private fun updateAllChildrenSize(parent: RecordComment?) { var grandParent = parent while(grandParent != null) { grandParent!!.updateAllChildrenSize() grandParent = grandParent!!.parent } }
-
ref가 같은 댓글 중 등록될 댓글의 step 값 이상인 댓글의 step을 +1 해줍니다.
private fun updateStep(comments: List<RecordComment>, step: Long) { comments.forEach { if(it.step >= step) { it.updateStep() } } }
override fun findRecordComments(record: Record, pageable: Pageable): Page<RecordCommentResDto> {
val content = jpaQueryFactory
.select(QRecordCommentResDto(recordComment.id, recordComment.level, recordComment.content, user.nickName, recordComment.createdAt))
.from(recordComment)
.leftJoin(recordComment.user, user)
.where(recordComment.record.eq(record))
.orderBy(
recordComment.ref.asc(),
recordComment.step.asc()
)
.offset(pageable.offset)
.limit(pageable.pageSize.toLong())
.fetch()
val countQuery = jpaQueryFactory
.select(QRecordCommentResDto(recordComment.id, recordComment.level, recordComment.content, user.nickName, recordComment.createdAt))
.from(recordComment)
.leftJoin(recordComment.user, user)
.where(recordComment.record.eq(record))
return PageableExecutionUtils.getPage(content, pageable) { countQuery.fetch().size.toLong() }
}
- 하나의 최상위 댓글과 그에 해당하는 모든 하위 댓글을 먼저 보여주어야 하기 때문에 ref를 기준으로 먼저 오름차순 정렬합니다.
- 댓글이 저장될 때 step을 계산해서 저장하기 때문에 같은 ref 중 step을 오름차순으로 정렬된 결과를 조회합니다.
- 추가로, count query의 효율성을 높이기 위해 PageableExecutionUtils를 이용해 필요한 경우에만 count query를 호출하도록 설정했습니다. 이를 사용하면 조회한 결과가 page size보다 작거나, 마지막 페이지일 때는 count query를 호출하지 않습니다.