diff --git a/src/main/java/naughty/tuzamate/domain/post/controller/PostController.java b/src/main/java/naughty/tuzamate/domain/post/controller/PostController.java index 66d5048..402399c 100644 --- a/src/main/java/naughty/tuzamate/domain/post/controller/PostController.java +++ b/src/main/java/naughty/tuzamate/domain/post/controller/PostController.java @@ -38,8 +38,11 @@ public CustomResponse createPost( @GetMapping("/boards/{boardType}/posts/{postId}") @Operation(summary = "단일 게시글 조회", description = "단일 게시글을 조회합니다.") - public CustomResponse getPost(@PathVariable Long postId) { - PostResDTO.PostPreviewDTO resDTO = postQueryService.getPost(postId); + public CustomResponse getPost( + @PathVariable Long postId, + @AuthenticationPrincipal PrincipalDetails principalDetails + ) { + PostResDTO.PostDTO resDTO = postQueryService.getPost(postId, principalDetails); return CustomResponse.onSuccess(GeneralSuccessCode.OK, resDTO); } diff --git a/src/main/java/naughty/tuzamate/domain/post/converter/PostConverter.java b/src/main/java/naughty/tuzamate/domain/post/converter/PostConverter.java index a5d3750..1acdf9f 100644 --- a/src/main/java/naughty/tuzamate/domain/post/converter/PostConverter.java +++ b/src/main/java/naughty/tuzamate/domain/post/converter/PostConverter.java @@ -34,6 +34,23 @@ public static PostResDTO.CreatePostResponseDTO toCreatePostResponseDTO(Post post .build(); } + // Post Entity -> PostDTO + public static PostResDTO.PostDTO toPostDTO(Post post, boolean liked, boolean scraped) { + return PostResDTO.PostDTO.builder() + .id(post.getId()) + .title(post.getTitle()) + .content(post.getContent()) + .likeNum(post.getLikeNum()) + .author(post.getUser().getNickname()) + .commentNum((long) post.getComments().size()) // 댓글 수 + .isRead(post.isRead()) + .liked(liked) + .scraped(scraped) + .createdAt(post.getCreatedAt()) + .updatedAt(post.getUpdatedAt()) + .build(); + } + // Post Entity -> PostPreviewDTO public static PostResDTO.PostPreviewDTO toPostPreviewDTO(Post post) { return PostResDTO.PostPreviewDTO.builder() diff --git a/src/main/java/naughty/tuzamate/domain/post/dto/PostResDTO.java b/src/main/java/naughty/tuzamate/domain/post/dto/PostResDTO.java index 2455831..4201057 100644 --- a/src/main/java/naughty/tuzamate/domain/post/dto/PostResDTO.java +++ b/src/main/java/naughty/tuzamate/domain/post/dto/PostResDTO.java @@ -14,6 +14,23 @@ public record CreatePostResponseDTO( ){ } + @Builder + public record PostDTO( + Long id, + String title, + String content, + Long likeNum, + // 작성자 닉네임, 댓글 수 필드 추가 + String author, + Long commentNum, + boolean isRead, + boolean liked, + boolean scraped, + LocalDateTime createdAt, + LocalDateTime updatedAt + ){ + } + @Builder public record PostPreviewDTO( Long id, diff --git a/src/main/java/naughty/tuzamate/domain/post/service/query/PostQueryService.java b/src/main/java/naughty/tuzamate/domain/post/service/query/PostQueryService.java index 91c63e7..e7c3114 100644 --- a/src/main/java/naughty/tuzamate/domain/post/service/query/PostQueryService.java +++ b/src/main/java/naughty/tuzamate/domain/post/service/query/PostQueryService.java @@ -1,10 +1,11 @@ package naughty.tuzamate.domain.post.service.query; +import naughty.tuzamate.auth.principal.PrincipalDetails; import naughty.tuzamate.domain.post.dto.PostResDTO; import naughty.tuzamate.domain.post.enums.BoardType; public interface PostQueryService { - PostResDTO.PostPreviewDTO getPost(Long postId); + PostResDTO.PostDTO getPost(Long postId, PrincipalDetails principalDetails); PostResDTO.PostPreviewListDTO getPostList(BoardType boardType, Long cursor, int size); } diff --git a/src/main/java/naughty/tuzamate/domain/post/service/query/PostQueryServiceImpl.java b/src/main/java/naughty/tuzamate/domain/post/service/query/PostQueryServiceImpl.java index 8586228..57f6979 100644 --- a/src/main/java/naughty/tuzamate/domain/post/service/query/PostQueryServiceImpl.java +++ b/src/main/java/naughty/tuzamate/domain/post/service/query/PostQueryServiceImpl.java @@ -1,11 +1,16 @@ package naughty.tuzamate.domain.post.service.query; import lombok.RequiredArgsConstructor; +import naughty.tuzamate.auth.principal.PrincipalDetails; import naughty.tuzamate.domain.post.converter.PostConverter; import naughty.tuzamate.domain.post.dto.PostResDTO; import naughty.tuzamate.domain.post.entity.Post; import naughty.tuzamate.domain.post.enums.BoardType; import naughty.tuzamate.domain.post.repository.PostRepository; +import naughty.tuzamate.domain.postLike.repository.PostLikeRepository; +import naughty.tuzamate.domain.postScrap.repository.PostScrapRepository; +import naughty.tuzamate.domain.user.entity.User; +import naughty.tuzamate.domain.user.repository.UserRepository; import naughty.tuzamate.global.error.GeneralErrorCode; import naughty.tuzamate.global.error.exception.CustomException; import org.springframework.data.domain.PageRequest; @@ -14,6 +19,7 @@ import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; +import java.security.Principal; import java.util.List; @Service @@ -21,9 +27,11 @@ public class PostQueryServiceImpl implements PostQueryService { private final PostRepository postRepository; + private final PostLikeRepository postLikeRepository; + private final PostScrapRepository postScrapRepository; @Override - public PostResDTO.PostPreviewDTO getPost(Long postId) { + public PostResDTO.PostDTO getPost(Long postId, PrincipalDetails principalDetails) { Post post = postRepository.findById(postId) .orElseThrow(() -> new CustomException(GeneralErrorCode.NOT_FOUND_404)); @@ -31,7 +39,10 @@ public PostResDTO.PostPreviewDTO getPost(Long postId) { post.setIsRead(); } - return PostConverter.toPostPreviewDTO(post); + boolean liked = postLikeRepository.existsByPostIdAndUserId(postId, principalDetails.getId()); + boolean scraped = postScrapRepository.existsByPostIdAndUserId(postId, principalDetails.getId()); + + return PostConverter.toPostDTO(post, liked, scraped); } @Override diff --git a/src/main/java/naughty/tuzamate/domain/postLike/repository/PostLikeRepository.java b/src/main/java/naughty/tuzamate/domain/postLike/repository/PostLikeRepository.java index 3330412..ce603ac 100644 --- a/src/main/java/naughty/tuzamate/domain/postLike/repository/PostLikeRepository.java +++ b/src/main/java/naughty/tuzamate/domain/postLike/repository/PostLikeRepository.java @@ -13,6 +13,7 @@ public interface PostLikeRepository extends JpaRepository { public boolean existsByPostAndUser(Post post, User user); public PostLike findByPostAndUser(Post post, User user); + public boolean existsByPostIdAndUserId(Long postId, Long userId); // 커서가 없는 경우 최신순 조회 @Query("select pl from PostLike pl join fetch pl.post p where pl.user.id = :userId order by pl.id desc") diff --git a/src/main/java/naughty/tuzamate/domain/postScrap/repository/PostScrapRepository.java b/src/main/java/naughty/tuzamate/domain/postScrap/repository/PostScrapRepository.java index 2e06fc6..c2c9e6f 100644 --- a/src/main/java/naughty/tuzamate/domain/postScrap/repository/PostScrapRepository.java +++ b/src/main/java/naughty/tuzamate/domain/postScrap/repository/PostScrapRepository.java @@ -14,6 +14,7 @@ public interface PostScrapRepository extends JpaRepository { public boolean existsByPostAndUser(Post post, User user); public PostScrap findByPostAndUser(Post post, User user); + public boolean existsByPostIdAndUserId(Long postId, Long userId); // 커서가 없는 경우 최신순 조회 @Query("select ps from PostScrap ps join fetch ps.post p where ps.user.id = :userId order by ps.id desc") diff --git a/src/main/java/naughty/tuzamate/global/config/FireBaseConfig.java b/src/main/java/naughty/tuzamate/global/config/FireBaseConfig.java index dc06844..52b4b2f 100644 --- a/src/main/java/naughty/tuzamate/global/config/FireBaseConfig.java +++ b/src/main/java/naughty/tuzamate/global/config/FireBaseConfig.java @@ -20,32 +20,34 @@ @Configuration public class FireBaseConfig { - // String 대신 Resource 타입으로 주입받음 @Value("${firebase.service-account.path}") - private Resource serviceAccountResource; + private String serviceAccountPath; @Bean public FirebaseApp firebaseApp() { - try { - // 주입받은 Resource에서 바로 InputStream을 얻음 - InputStream serviceAccount = serviceAccountResource.getInputStream(); + try (InputStream serviceAccount = getServiceAccountStream()) { FirebaseOptions options = FirebaseOptions.builder() .setCredentials(GoogleCredentials.fromStream(serviceAccount)) .build(); - // 앱이 이미 초기화되었는지 확인 (중복 초기화 방지) - if (FirebaseApp.getApps().isEmpty()) { - log.info("Successfully initialized firebase app"); - return FirebaseApp.initializeApp(options); - } else { - return FirebaseApp.getInstance(); - } + log.info(" Successfully initialized firebase app"); + return FirebaseApp.initializeApp(options); } catch (IOException exception) { - log.error("Fail to initialize firebase app: {}", exception.getMessage(), exception); - // 초기화 실패 시 null 대신 예외를 던져서 애플리케이션이 문제를 인지하게 하는 것이 더 좋습니다. - throw new RuntimeException("Failed to initialize Firebase app.", exception); + log.error(" Fail to initialize firebase app: {}", exception.getMessage(), exception); + return null; + } + } + + private InputStream getServiceAccountStream() throws IOException { + // 절대경로면 FileInputStream, 아니면 classpath + if (serviceAccountPath.startsWith("/") || serviceAccountPath.contains(":")) { + log.info("Using absolute path for Firebase service account: {}", serviceAccountPath); + return new FileInputStream(serviceAccountPath); + } else { + log.info("Using classpath resource for Firebase service account: {}", serviceAccountPath); + return new ClassPathResource(serviceAccountPath).getInputStream(); } }