Skip to content

Commit cda7a68

Browse files
authored
refactor: 댓글 정책 변경에 따른 리팩터링 (#373)
* refactor: community 도메인 제외 연관관계 삭제 - Application, Country, GpaScore, InterestedCountry/Region, LanguageTestScore, LikedUniversity. SiteUser, UnivApplyInfo 도메인 수정 - 연관관계 삭제 및 생성자 parameter 수정으로 인한 쿼리 수정 다수 - test코드 수정 * fix: application, university 테스트 코드 오류 수정 - 연관관계 변경에 따른 transaction 문제 해결 - 쿼리 일부 수정, 테스트 코드 일부 수정 - GeneralUniversityRecommendService 로직 수정 * refactor: 불필요한 siteUser 재조회 수정 - ScoreService에서 siteUser정보를 다시 불러오는 로직 삭제 * refactor: 수정사항 반영 - 컨벤션, 타입수정 다수 * fix: conflict merge 과정에서 생긴 더미 파일 삭제 * refactor: 불필요한 import문 삭제 * refactor: Community 연관관계 매핑 수정/삭제 - Board, Comment, Post, PostLike 연관관계 수정/삭제 - 연관관계 수정에 따른 Repository및 Service 레이어 수정 - 연관관계 수정에 따른 테스트 코드 수정 * refactor: 사용하지 않는 import문 삭제 * refactor: 댓글 정책 변경에 따른 수정 - is_deleted Column추가 - 해당 Column추가에 따른 Repository, Service, DTO 수정 - 댓글 삭제 시 content 보존하도록 변경 - 테스트 코드 수정 * fix: likedPostCount 테스트 코드 주석처리 및 todo 추가 * fix: 서브모듈 참조 오류 수정 * refactor: 코드리뷰 수정사항 반영 - Long타입 long으로 수정 - collect -> toList단독으로 변경 - 컨벤션 수정 - Recomment 변경사항 원복 - getGeneralRecommendsExcludingSelected함수 OutOfRange 방지 추가 * refactor: 코드리뷰 수정사항 반영 - UnivApplyInfo University fetchType LAZY로 변경 - 컨벤션 수정 - findRandomByTerm함수 nativeQuery제거 및 Pageable로 LIMIT 구현 * refactor: Parameter 명칭 변경 - siteUser -> siteUserId로 변경 * refactor: 코드리뷰 수정사항 반영 - 메서드명 각자 기능에 맞게 수정 * refactor: 코드리뷰 수정사항 반영 - 사용하지 않는 import문 제거 - 코드 컨벤션 수정 - postLikeList에서 의미없는 BatchSize 삭제 * fix: SiteUser 결함 수정 - password nullable true -> false로 수정 * fix: 직전 커밋 revert - password nullable 변경사항 취소 This reverts commit 6fcc903. * refactor: 코드 리뷰 수정사항 반영 - 조건문에서 isDeleted사용 - 코드 컨벤션 수정 - 부모 댓글이 삭제된 경우에 자식 댓글이 있는 경우 테스트 코드 추가 * refactor: flyway script 추가 * fix: CommentService 수정 - CommentService에서 현재 접속중인 User정보가 아닌 댓글의 User정보를 반환하도록 수정 * refactor: 삭제된 댓글에 대한 사용자 정보를 반환하지 않도록 수정 - DTO에서 구분하여 isDeleted true면 null로 반환 - 테스트 코드 추가 * refactor: 테스트 코드 수정 - 디버깅용 코드 삭제 - 꼭 필요한 부분만 검증하도록 테스트 검증 간소화
1 parent cfe8489 commit cda7a68

File tree

6 files changed

+142
-32
lines changed

6 files changed

+142
-32
lines changed

src/main/java/com/example/solidconnection/community/comment/domain/Comment.java

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,9 @@ public class Comment extends BaseEntity {
4040
@Column(length = 255)
4141
private String content;
4242

43+
@Column(name = "is_deleted", columnDefinition = "boolean default false", nullable = false)
44+
private boolean isDeleted = false;
45+
4346
@ManyToOne(fetch = FetchType.LAZY)
4447
@JoinColumn(name = "post_id")
4548
private Post post;
@@ -102,6 +105,6 @@ public void updateContent(String content) {
102105
}
103106

104107
public void deprecateComment() {
105-
this.content = null;
108+
this.isDeleted = true;
106109
}
107110
}

src/main/java/com/example/solidconnection/community/comment/dto/PostFindCommentResponse.java

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,11 +20,11 @@ public static PostFindCommentResponse from(Boolean isOwner, Comment comment, Sit
2020
return new PostFindCommentResponse(
2121
comment.getId(),
2222
getParentCommentId(comment),
23-
comment.getContent(),
23+
getDisplayContent(comment),
2424
isOwner,
2525
comment.getCreatedAt(),
2626
comment.getUpdatedAt(),
27-
PostFindSiteUserResponse.from(siteUser)
27+
getDisplaySiteUserResponse(comment, siteUser)
2828
);
2929
}
3030

@@ -34,4 +34,13 @@ private static Long getParentCommentId(Comment comment) {
3434
}
3535
return null;
3636
}
37+
38+
private static String getDisplayContent(Comment comment)
39+
{
40+
return comment.isDeleted() ? "" : comment.getContent();
41+
}
42+
43+
private static PostFindSiteUserResponse getDisplaySiteUserResponse(Comment comment, SiteUser siteUser) {
44+
return comment.isDeleted() ? null : PostFindSiteUserResponse.from(siteUser);
45+
}
3746
}

src/main/java/com/example/solidconnection/community/comment/repository/CommentRepository.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,14 +16,14 @@ public interface CommentRepository extends JpaRepository<Comment, Long> {
1616
WITH RECURSIVE CommentTree AS (
1717
SELECT
1818
id, parent_id, post_id, site_user_id, content,
19-
created_at, updated_at,
19+
created_at, updated_at, is_deleted,
2020
0 AS level, CAST(id AS CHAR(255)) AS path
2121
FROM comment
2222
WHERE post_id = :postId AND parent_id IS NULL
2323
UNION ALL
2424
SELECT
2525
c.id, c.parent_id, c.post_id, c.site_user_id, c.content,
26-
c.created_at, c.updated_at,
26+
c.created_at, c.updated_at, c.is_deleted,
2727
ct.level + 1, CONCAT(ct.path, '->', c.id)
2828
FROM comment c
2929
INNER JOIN CommentTree ct ON c.parent_id = ct.id

src/main/java/com/example/solidconnection/community/comment/service/CommentService.java

Lines changed: 41 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,11 @@
1717
import org.springframework.stereotype.Service;
1818
import org.springframework.transaction.annotation.Transactional;
1919

20+
import java.util.ArrayList;
2021
import java.util.List;
22+
import java.util.Map;
2123
import java.util.Objects;
24+
import java.util.Set;
2225
import java.util.stream.Collectors;
2326

2427
import static com.example.solidconnection.common.exception.ErrorCode.CAN_NOT_UPDATE_DEPRECATED_COMMENT;
@@ -36,14 +39,47 @@ public class CommentService {
3639

3740
@Transactional(readOnly = true)
3841
public List<PostFindCommentResponse> findCommentsByPostId(SiteUser siteUser, Long postId) {
39-
SiteUser commentOwner = siteUserRepository.findById(siteUser.getId())
40-
.orElseThrow(() -> new CustomException(USER_NOT_FOUND));
41-
return commentRepository.findCommentTreeByPostId(postId)
42+
List<Comment> allComments = commentRepository.findCommentTreeByPostId(postId);
43+
List<Comment> filteredComments = filterCommentsByDeletionRules(allComments);
44+
45+
Set<Long> userIds = filteredComments.stream()
46+
.map(Comment::getSiteUserId)
47+
.collect(Collectors.toSet());
48+
49+
Map<Long, SiteUser> userMap = siteUserRepository.findAllById(userIds)
4250
.stream()
43-
.map(comment -> PostFindCommentResponse.from(isOwner(comment, siteUser), comment, siteUser))
51+
.collect(Collectors.toMap(SiteUser::getId, user -> user));
52+
53+
return filteredComments.stream()
54+
.map(comment -> PostFindCommentResponse.from(
55+
isOwner(comment, siteUser), comment, userMap.get(comment.getSiteUserId())))
4456
.collect(Collectors.toList());
4557
}
4658

59+
private List<Comment> filterCommentsByDeletionRules(List<Comment> comments) {
60+
Map<Long, List<Comment>> commentsByParent = comments.stream()
61+
.filter(comment -> comment.getParentComment() != null)
62+
.collect(Collectors.groupingBy(comment -> comment.getParentComment().getId()));
63+
64+
List<Comment> result = new ArrayList<>();
65+
66+
List<Comment> parentComments = comments.stream()
67+
.filter(comment -> comment.getParentComment() == null)
68+
.toList();
69+
for (Comment parent : parentComments) {
70+
List<Comment> children = commentsByParent.getOrDefault(parent.getId(), List.of());
71+
boolean allDeleted = parent.isDeleted() &&
72+
children.stream().allMatch(Comment::isDeleted);
73+
if (!allDeleted) {
74+
result.add(parent);
75+
result.addAll(children.stream()
76+
.filter(child -> !child.isDeleted())
77+
.toList());
78+
}
79+
}
80+
return result;
81+
}
82+
4783
private Boolean isOwner(Comment comment, SiteUser siteUser) {
4884
return Objects.equals(comment.getSiteUserId(), siteUser.getId());
4985
}
@@ -99,7 +135,7 @@ public CommentDeleteResponse deleteCommentById(SiteUser siteUser, Long commentId
99135
comment.resetPostAndParentComment();
100136
commentRepository.deleteById(commentId);
101137
// 대댓글 삭제 이후, 부모댓글이 무의미하다면 이역시 삭제합니다.
102-
if (parentComment.getCommentList().isEmpty() && parentComment.getContent() == null) {
138+
if (parentComment.getCommentList().isEmpty() && parentComment.isDeleted()) {
103139
parentComment.resetPostAndParentComment();
104140
commentRepository.deleteById(parentComment.getId());
105141
}
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
ALTER TABLE comment
2+
ADD COLUMN is_deleted BOOLEAN NOT NULL DEFAULT FALSE;

src/test/java/com/example/solidconnection/community/comment/service/CommentServiceTest.java

Lines changed: 82 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
import com.example.solidconnection.community.post.domain.PostCategory;
1616
import com.example.solidconnection.community.post.fixture.PostFixture;
1717
import com.example.solidconnection.siteuser.domain.SiteUser;
18+
import com.example.solidconnection.siteuser.dto.PostFindSiteUserResponse;
1819
import com.example.solidconnection.siteuser.fixture.SiteUserFixture;
1920
import com.example.solidconnection.support.TestContainerSpringBootTest;
2021
import jakarta.transaction.Transactional;
@@ -108,6 +109,85 @@ class 댓글_조회_테스트 {
108109
))
109110
);
110111
}
112+
113+
@Test
114+
void 부모댓글과_대댓글이_모두_삭제되면_응답에서_제외한다() {
115+
// given
116+
Comment parentComment = commentFixture.부모_댓글("부모 댓글", post, user1);
117+
Comment childComment1 = commentFixture.자식_댓글("자식 댓글1", post, user2, parentComment);
118+
Comment childComment2 = commentFixture.자식_댓글("자식 댓글2", post, user2, parentComment);
119+
120+
parentComment.deprecateComment();
121+
childComment1.deprecateComment();
122+
childComment2.deprecateComment();
123+
commentRepository.saveAll(List.of(parentComment, childComment1, childComment2));
124+
125+
// when
126+
List<PostFindCommentResponse> responses = commentService.findCommentsByPostId(user1, post.getId());
127+
128+
// then
129+
assertAll(
130+
() -> assertThat(responses).isEmpty()
131+
);
132+
}
133+
134+
@Test
135+
void 부모댓글이_삭제된_경우에도_자식댓글이_존재하면_자식댓글의_내용만_반환한다() {
136+
// given
137+
Comment parentComment = commentFixture.부모_댓글("부모 댓글", post, user1);
138+
Comment childComment1 = commentFixture.자식_댓글("자식 댓글1", post, user2, parentComment);
139+
Comment childComment2 = commentFixture.자식_댓글("자식 댓글2", post, user2, parentComment);
140+
141+
parentComment.deprecateComment();
142+
commentRepository.saveAll(List.of(parentComment, childComment1, childComment2));
143+
144+
// when
145+
List<PostFindCommentResponse> responses = commentService.findCommentsByPostId(user1, post.getId());
146+
147+
// then
148+
assertAll(
149+
() -> assertThat(responses).hasSize(3),
150+
() -> assertThat(responses)
151+
.extracting(PostFindCommentResponse::id)
152+
.containsExactlyInAnyOrder(parentComment.getId(), childComment1.getId(), childComment2.getId()),
153+
() -> assertThat(responses)
154+
.filteredOn(response -> response.id().equals(parentComment.getId()))
155+
.extracting(PostFindCommentResponse::content)
156+
.containsExactly(""),
157+
() -> assertThat(responses)
158+
.filteredOn(response -> !response.id().equals(parentComment.getId()))
159+
.extracting(PostFindCommentResponse::content)
160+
.containsExactlyInAnyOrder("자식 댓글1", "자식 댓글2")
161+
);
162+
}
163+
164+
@Test
165+
void 부모댓글이_삭제된_경우_부모댓글의_사용자정보는_null이고_자식댓글의_사용자정보는_정상적으로_반환한다() {
166+
// given
167+
Comment parentComment = commentFixture.부모_댓글("부모 댓글", post, user1);
168+
Comment childComment1 = commentFixture.자식_댓글("자식 댓글1", post, user2, parentComment);
169+
Comment childComment2 = commentFixture.자식_댓글("자식 댓글2", post, user2, parentComment);
170+
171+
parentComment.deprecateComment();
172+
commentRepository.saveAll(List.of(parentComment, childComment1, childComment2));
173+
174+
// when
175+
List<PostFindCommentResponse> responses = commentService.findCommentsByPostId(user1, post.getId());
176+
177+
// then
178+
assertAll(
179+
() -> assertThat(responses)
180+
.filteredOn(response -> response.id().equals(parentComment.getId()))
181+
.extracting(PostFindCommentResponse::postFindSiteUserResponse)
182+
.containsExactly((PostFindSiteUserResponse) null),
183+
() -> assertThat(responses)
184+
.filteredOn(response -> !response.id().equals(parentComment.getId()))
185+
.extracting(PostFindCommentResponse::postFindSiteUserResponse)
186+
.isNotNull()
187+
.extracting(PostFindSiteUserResponse::id)
188+
.containsExactlyInAnyOrder(user2.getId(), user2.getId())
189+
);
190+
}
111191
}
112192

113193
@Nested
@@ -281,7 +361,8 @@ class 댓글_삭제_테스트 {
281361
// then
282362
Comment deletedComment = commentRepository.findById(response.id()).orElseThrow();
283363
assertAll(
284-
() -> assertThat(deletedComment.getContent()).isNull(),
364+
() -> assertThat(deletedComment.getContent()).isEqualTo("부모 댓글"),
365+
() -> assertThat(deletedComment.isDeleted()).isTrue(),
285366
() -> assertThat(deletedComment.getCommentList())
286367
.extracting(Comment::getId)
287368
.containsExactlyInAnyOrder(childComment.getId()),
@@ -316,27 +397,6 @@ class 댓글_삭제_테스트 {
316397
);
317398
}
318399

319-
@Test
320-
@Transactional
321-
void 대댓글을_삭제하고_부모댓글이_삭제된_상태면_부모댓글도_삭제된다() {
322-
// given
323-
Comment parentComment = commentFixture.부모_댓글("부모 댓글", post, user1);
324-
Comment childComment = commentFixture.자식_댓글("자식 댓글", post, user2, parentComment);
325-
List<Comment> comments = post.getCommentList();
326-
int expectedCommentsCount = comments.size() - 2;
327-
parentComment.deprecateComment();
328-
329-
// when
330-
CommentDeleteResponse response = commentService.deleteCommentById(user2, childComment.getId());
331-
332-
// then
333-
assertAll(
334-
() -> assertThat(commentRepository.findById(response.id())).isEmpty(),
335-
() -> assertThat(commentRepository.findById(parentComment.getId())).isEmpty(),
336-
() -> assertThat(post.getCommentList()).hasSize(expectedCommentsCount)
337-
);
338-
}
339-
340400
@Test
341401
void 다른_사용자의_댓글을_삭제하면_예외_응답을_반환한다() {
342402
// given

0 commit comments

Comments
 (0)