Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -44,9 +44,11 @@ public ResponseEntity<RsData<CommentResponse>> createComment(
@GetMapping
public ResponseEntity<RsData<PageResponse<CommentListResponse>>> getComments(
@PathVariable Long postId,
@AuthenticationPrincipal CustomUserDetails user,
@PageableDefault(sort = "createdAt", direction = Sort.Direction.ASC) Pageable pageable
) {
PageResponse<CommentListResponse> response = commentService.getComments(postId, pageable);
Long userId = (user != null) ? user.getUserId() : null;
PageResponse<CommentListResponse> response = commentService.getComments(postId, userId, pageable);
return ResponseEntity
.status(HttpStatus.OK)
.body(RsData.success(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,8 @@ public interface CommentControllerDocs {
"postId": 101,
"author": {
"id": 5,
"nickname": "홍길동"
"nickname": "홍길동",
"profileImageUrl": null
},
"content": "좋은 글 감사합니다!",
"createdAt": "2025-09-22T11:30:00",
Expand Down Expand Up @@ -170,10 +171,12 @@ ResponseEntity<RsData<CommentResponse>> createComment(
"parentId": null,
"author": {
"id": 5,
"nickname": "홍길동"
"nickname": "홍길동",
"profileImageUrl": null
},
"content": "부모 댓글",
"likeCount": 2,
"likedByMe": true,
"createdAt": "2025-09-22T11:30:00",
"updatedAt": "2025-09-22T11:30:00",
"children": [
Expand All @@ -183,10 +186,12 @@ ResponseEntity<RsData<CommentResponse>> createComment(
"parentId": 1,
"author": {
"id": 5,
"nickname": "홍길동"
"nickname": "홍길동",
"profileImageUrl": null
},
"content": "자식 댓글",
"likeCount": 0,
"likedByMe": false,
"createdAt": "2025-09-22T11:35:00",
"updatedAt": "2025-09-22T11:35:00",
"children": []
Expand Down Expand Up @@ -252,6 +257,7 @@ ResponseEntity<RsData<CommentResponse>> createComment(
})
ResponseEntity<RsData<PageResponse<CommentListResponse>>> getComments(
@PathVariable Long postId,
@AuthenticationPrincipal CustomUserDetails user,
Pageable pageable
);

Expand All @@ -275,7 +281,8 @@ ResponseEntity<RsData<PageResponse<CommentListResponse>>> getComments(
"postId": 101,
"author": {
"id": 5,
"nickname": "홍길동"
"nickname": "홍길동",
"profileImageUrl": null
},
"content": "수정된 댓글 내용입니다.",
"createdAt": "2025-09-22T11:30:00",
Expand Down Expand Up @@ -532,7 +539,8 @@ ResponseEntity<RsData<Void>> deleteComment(
"parentId": 25,
"author": {
"id": 7,
"nickname": "이몽룡"
"nickname": "이몽룡",
"profileImageUrl": null
},
"content": "저도 동의합니다!",
"createdAt": "2025-09-22T13:30:00",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,9 @@ public class CommentListResponse {
@Setter
private long likeCount;

@Setter
private Boolean likedByMe;

private final LocalDateTime createdAt;
private final LocalDateTime updatedAt;

Expand All @@ -35,6 +38,7 @@ public CommentListResponse(Long commentId,
AuthorResponse author,
String content,
long likeCount,
Boolean likedByMe,
LocalDateTime createdAt,
LocalDateTime updatedAt,
List<CommentListResponse> children) {
Expand All @@ -43,6 +47,7 @@ public CommentListResponse(Long commentId,
this.parentId = parentId;
this.author = author;
this.content = content;
this.likedByMe = likedByMe;
this.likeCount = likeCount;
this.createdAt = createdAt;
this.updatedAt = updatedAt;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,16 @@

import com.back.domain.board.comment.entity.CommentLike;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;
import org.springframework.stereotype.Repository;

import java.util.Collection;
import java.util.List;
import java.util.Optional;

@Repository
public interface CommentLikeRepository extends JpaRepository<CommentLike, Long> {
public interface CommentLikeRepository extends JpaRepository<CommentLike, Long>, CommentLikeRepositoryCustom {
boolean existsByUserIdAndCommentId(Long userId, Long commentId);
Optional<CommentLike> findByUserIdAndCommentId(Long userId, Long commentId);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package com.back.domain.board.comment.repository;

import java.util.Collection;
import java.util.List;

public interface CommentLikeRepositoryCustom {
List<Long> findLikedCommentIdsIn(Long userId, Collection<Long> commentIds);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package com.back.domain.board.comment.repository;

import com.back.domain.board.comment.entity.QCommentLike;
import com.querydsl.jpa.impl.JPAQueryFactory;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Repository;

import java.util.Collection;
import java.util.List;


@Repository
@RequiredArgsConstructor
public class CommentLikeRepositoryImpl implements CommentLikeRepositoryCustom {

private final JPAQueryFactory queryFactory;

@Override
public List<Long> findLikedCommentIdsIn(Long userId, Collection<Long> commentIds) {
QCommentLike commentLike = QCommentLike.commentLike;

return queryFactory
.select(commentLike.comment.id)
.from(commentLike)
.where(
commentLike.user.id.eq(userId),
commentLike.comment.id.in(commentIds)
)
.fetch();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -112,9 +112,10 @@ private List<CommentListResponse> fetchComments(
comment.id,
comment.post.id,
comment.parent.id,
new QAuthorResponse(user.id, profile.nickname),
new QAuthorResponse(user.id, profile.nickname, profile.profileImageUrl),
comment.content,
Expressions.constant(0L), // likeCount는 별도 주입
Expressions.constant(false),
comment.createdAt,
comment.updatedAt,
Expressions.constant(Collections.emptyList()) // children은 별도 주입
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
import com.back.domain.board.comment.dto.CommentRequest;
import com.back.domain.board.comment.dto.CommentResponse;
import com.back.domain.board.comment.dto.ReplyResponse;
import com.back.domain.board.comment.entity.CommentLike;
import com.back.domain.board.comment.repository.CommentLikeRepository;
import com.back.domain.board.common.dto.PageResponse;
import com.back.domain.board.comment.entity.Comment;
import com.back.domain.board.post.entity.Post;
Expand All @@ -22,11 +24,16 @@
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.HashSet;
import java.util.List;
import java.util.Set;

@Service
@RequiredArgsConstructor
@Transactional
public class CommentService {
private final CommentRepository commentRepository;
private final CommentLikeRepository commentLikeRepository;
private final UserRepository userRepository;
private final PostRepository postRepository;
private final ApplicationEventPublisher eventPublisher;
Expand Down Expand Up @@ -85,6 +92,41 @@ public PageResponse<CommentListResponse> getComments(Long postId, Pageable pagea
return PageResponse.from(comments);
}

// TODO: 추후 메서드 통합 및 리팩토링
@Transactional(readOnly = true)
public PageResponse<CommentListResponse> getComments(Long postId, Long userId, Pageable pageable) {
// 기본 댓글 목록
PageResponse<CommentListResponse> response = getComments(postId, pageable);

// 로그인 사용자용 로직
if (userId != null) {
// 댓글 ID 수집
List<Long> commentIds = response.items().stream()
.map(CommentListResponse::getCommentId)
.toList();

if (commentIds.isEmpty()) return response;

// QueryDSL 기반 좋아요 ID 조회 (단일 쿼리)
List<Long> likedIds = commentLikeRepository.findLikedCommentIdsIn(userId, commentIds);
Set<Long> likedSet = new HashSet<>(likedIds);

// likedByMe 세팅
response.items().forEach(c -> c.setLikedByMe(likedSet.contains(c.getCommentId())));

// 자식 댓글에도 동일 적용
response.items().forEach(parent -> {
if (parent.getChildren() != null) {
parent.getChildren().forEach(child ->
child.setLikedByMe(likedSet.contains(child.getCommentId()))
);
}
});
}

return response;
}

/**
* 댓글 수정 서비스
* 1. Post 조회
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,20 +6,23 @@
/**
* 작성자 응답 DTO
*
* @param id 작성자 ID
* @param nickname 작성자 닉네임
* @param id 작성자 ID
* @param nickname 작성자 닉네임
* @param profileImageUrl 작성자 프로필 이미지
*/
public record AuthorResponse(
Long id,
String nickname
String nickname,
String profileImageUrl
) {
@QueryProjection
public AuthorResponse {}

public static AuthorResponse from(User user) {
return new AuthorResponse(
user.getId(),
user.getUserProfile().getNickname()
user.getUserProfile().getNickname(),
user.getProfileImageUrl()
);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -59,9 +59,12 @@ public ResponseEntity<RsData<PageResponse<PostListResponse>>> getPosts(
// 게시글 단건 조회
@GetMapping("/{postId}")
public ResponseEntity<RsData<PostDetailResponse>> getPost(
@PathVariable Long postId
@PathVariable Long postId,
@AuthenticationPrincipal CustomUserDetails user
) {
PostDetailResponse response = postService.getPost(postId);
PostDetailResponse response = (user != null)
? postService.getPostWithUser(postId, user.getUserId())
: postService.getPost(postId);
return ResponseEntity
.status(HttpStatus.OK)
.body(RsData.success(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,10 +43,12 @@ public interface PostControllerDocs {
"postId": 101,
"author": {
"id": 5,
"nickname": "홍길동"
"nickname": "홍길동",
"profileImageUrl": null
},
"title": "첫 번째 게시글",
"content": "안녕하세요, 첫 글입니다!",
"thumbnailUrl": null,
"categories": [
{ "id": 1, "name": "공지사항" },
{ "id": 2, "name": "자유게시판" }
Expand Down Expand Up @@ -171,8 +173,9 @@ ResponseEntity<RsData<PostResponse>> createPost(
"items": [
{
"postId": 1,
"author": { "id": 10, "nickname": "홍길동" },
"author": { "id": 10, "nickname": "홍길동", "profileImageUrl": null },
"title": "첫 글",
"thumbnailUrl": null,
"categories": [{ "id": 1, "name": "공지사항" }],
"likeCount": 5,
"bookmarkCount": 2,
Expand Down Expand Up @@ -247,16 +250,19 @@ ResponseEntity<RsData<PageResponse<PostListResponse>>> getPosts(
"message": "게시글이 조회되었습니다.",
"data": {
"postId": 101,
"author": { "id": 5, "nickname": "홍길동" },
"author": { "id": 5, "nickname": "홍길동", "profileImageUrl": null },
"title": "첫 번째 게시글",
"content": "안녕하세요, 첫 글입니다!",
"thumbnailUrl": null,
"categories": [
{ "id": 1, "name": "공지사항" },
{ "id": 2, "name": "자유게시판" }
],
"likeCount": 10,
"bookmarkCount": 2,
"commentCount": 3,
"likedByMe": false,
"bookmarkedByMe": false,
"createdAt": "2025-09-22T10:30:00",
"updatedAt": "2025-09-22T10:30:00"
}
Expand Down Expand Up @@ -296,7 +302,8 @@ ResponseEntity<RsData<PageResponse<PostListResponse>>> getPosts(
)
})
ResponseEntity<RsData<PostDetailResponse>> getPost(
@PathVariable Long postId
@PathVariable Long postId,
@AuthenticationPrincipal CustomUserDetails user
);

@Operation(
Expand All @@ -318,10 +325,12 @@ ResponseEntity<RsData<PostDetailResponse>> getPost(
"postId": 101,
"author": {
"id": 5,
"nickname": "홍길동"
"nickname": "홍길동",
"profileImageUrl": null
},
"title": "수정된 게시글",
"content": "안녕하세요, 수정했습니다!",
"thumbnailUrl": null,
"categories": [
{ "id": 1, "name": "공지사항" },
{ "id": 2, "name": "자유게시판" }
Expand Down
Loading