diff --git a/src/main/java/com/redo/domain/community/controller/CommunityController.java b/src/main/java/com/redo/domain/community/controller/CommunityController.java index 818d317..6f31837 100644 --- a/src/main/java/com/redo/domain/community/controller/CommunityController.java +++ b/src/main/java/com/redo/domain/community/controller/CommunityController.java @@ -77,11 +77,12 @@ public ApiResponse getCommunityPosts( // 커뮤니티 게시글 상세 조회 API @GetMapping("/{communityId}") public ApiResponse getCommunityPost( + @AuthenticationPrincipal Long userId, @PathVariable Long communityId ) { return ApiResponse.onSuccess( CommunitySuccessCode.GET_COMMUNITY_POST_SUCCESS, - communityService.getCommunityPost(communityId) + communityService.getCommunityPost(userId, communityId) ); } diff --git a/src/main/java/com/redo/domain/community/converter/CommunityConverter.java b/src/main/java/com/redo/domain/community/converter/CommunityConverter.java index 0bf5c11..61170a0 100644 --- a/src/main/java/com/redo/domain/community/converter/CommunityConverter.java +++ b/src/main/java/com/redo/domain/community/converter/CommunityConverter.java @@ -72,7 +72,10 @@ public static CommunityDetailResponseDTO toCommunityDetailResponse( String writer, String profileImageUrl, String characterCode, - String imageUrl + List imageUrls, + long numComments, + boolean isLiked, + boolean isMine ) { return new CommunityDetailResponseDTO( community.getId(), @@ -82,7 +85,12 @@ public static CommunityDetailResponseDTO toCommunityDetailResponse( characterCode, community.getContent(), community.getCreatedAt(), - imageUrl + imageUrls, + String.valueOf(community.getCategory().getCode()), + numComments, + community.getLikeCount() == null ? 0 : community.getLikeCount(), + isLiked, + isMine ); } diff --git a/src/main/java/com/redo/domain/community/dto/res/CommunityDetailResponseDTO.java b/src/main/java/com/redo/domain/community/dto/res/CommunityDetailResponseDTO.java index b6d25d0..36405bd 100644 --- a/src/main/java/com/redo/domain/community/dto/res/CommunityDetailResponseDTO.java +++ b/src/main/java/com/redo/domain/community/dto/res/CommunityDetailResponseDTO.java @@ -1,6 +1,9 @@ package com.redo.domain.community.dto.res; +import com.fasterxml.jackson.annotation.JsonProperty; + import java.time.LocalDateTime; +import java.util.List; public record CommunityDetailResponseDTO( Long id, @@ -10,6 +13,12 @@ public record CommunityDetailResponseDTO( String characterCode, String content, LocalDateTime createdAt, - String imageUrl + List imageUrls, + String category, + Long numComments, + Integer numLikes, + // ApiResponse 의 isSuccess 와 동일하게, Jackson 이 is 접두사를 떼지 않도록 이름을 고정한다. + @JsonProperty("isLiked") boolean isLiked, + @JsonProperty("isMine") boolean isMine ) { } diff --git a/src/main/java/com/redo/domain/community/repository/CommunityCommentRepository.java b/src/main/java/com/redo/domain/community/repository/CommunityCommentRepository.java index 0b52134..d1583e5 100644 --- a/src/main/java/com/redo/domain/community/repository/CommunityCommentRepository.java +++ b/src/main/java/com/redo/domain/community/repository/CommunityCommentRepository.java @@ -27,6 +27,9 @@ interface CommunityCommentCount { long getCommentCount(); } + // 상세 조회용: 단일 게시글의 삭제되지 않은 댓글 수를 집계한다. + long countByCommunityAndDeletedAtIsNull(Community community); + List findByCommunityAndIdGreaterThanAndDeletedAtIsNullOrderByIdAsc( Community community, Long cursor, diff --git a/src/main/java/com/redo/domain/community/repository/CommunityImgRepository.java b/src/main/java/com/redo/domain/community/repository/CommunityImgRepository.java index 4c85da0..34385a5 100644 --- a/src/main/java/com/redo/domain/community/repository/CommunityImgRepository.java +++ b/src/main/java/com/redo/domain/community/repository/CommunityImgRepository.java @@ -5,11 +5,11 @@ import org.springframework.data.jpa.repository.JpaRepository; import java.util.List; -import java.util.Optional; public interface CommunityImgRepository extends JpaRepository { - Optional findFirstByCommunityOrderByDisplayOrderAsc(Community community); + // 상세 조회용: 단일 게시글의 첨부 이미지 전체를 display_order 오름차순으로 조회한다. + List findByCommunityOrderByDisplayOrderAsc(Community community); // 목록 조회용: 여러 게시글의 이미지를 한 번에 조회한다(display_order 오름차순, 게시글별 첫 항목이 대표 이미지). List findByCommunityInOrderByDisplayOrderAsc(List communities); diff --git a/src/main/java/com/redo/domain/community/service/CommunityService.java b/src/main/java/com/redo/domain/community/service/CommunityService.java index b708d08..ba5975d 100644 --- a/src/main/java/com/redo/domain/community/service/CommunityService.java +++ b/src/main/java/com/redo/domain/community/service/CommunityService.java @@ -41,6 +41,7 @@ import java.util.Arrays; import java.util.List; import java.util.Map; +import java.util.Objects; import java.util.stream.Collectors; @Service @@ -87,7 +88,7 @@ public Page getCommunityPosts(Integer category, Pageable p } // 게시글 상세 조회 로직 - public CommunityDetailResponseDTO getCommunityPost(Long communityId) { + public CommunityDetailResponseDTO getCommunityPost(Long userId, Long communityId) { Community community = communityRepository.findByIdAndDeletedAtIsNull(communityId) .orElseThrow(() -> new CommunityException(CommunityErrorCode.COMMUNITY_NOT_FOUND)); @@ -98,10 +99,24 @@ public CommunityDetailResponseDTO getCommunityPost(Long communityId) { getNickname(profile), getProfileImageUrl(profile), getCharacterCode(profile), - getRepresentativeImageUrl(community) + getImageUrls(community), + communityCommentRepository.countByCommunityAndDeletedAtIsNull(community), + isLiked(userId, community), + isMine(userId, community) ); } + // 조회자가 해당 게시글에 좋아요를 눌렀는지 판별하는 로직 + private boolean isLiked(Long userId, Community community) { + return userId != null + && communityLikeRepository.existsById(new CommunityLikeId(community.getId(), userId)); + } + + // 조회자가 해당 게시글의 작성자인지 판별하는 로직 + private boolean isMine(Long userId, Community community) { + return userId != null && userId.equals(community.getUser().getId()); + } + // 게시글 등록 로직 @Transactional public CommunityCreateResponseDTO createCommunityPost(Long userId, CommunityCreateRequestDTO request) { @@ -312,12 +327,13 @@ private Map getRepresentativeImageKeys(List communities )); } - // 대표 이미지(display_order 최솟값)의 S3 객체 키를 조회용 Presigned URL로 변환하는 로직 - private String getRepresentativeImageUrl(Community community) { - return communityImgRepository.findFirstByCommunityOrderByDisplayOrderAsc(community) + // 상세 조회용: 첨부 이미지 전체의 S3 객체 키를 등록 순서(display_order)대로 Presigned URL로 변환하는 로직 + private List getImageUrls(Community community) { + return communityImgRepository.findByCommunityOrderByDisplayOrderAsc(community).stream() .map(CommunityImg::getImageKey) .map(this::createImageUrl) - .orElse(null); + .filter(Objects::nonNull) + .toList(); } // S3 객체 키를 이미지 조회용 Presigned URL로 변환하는 로직