-
Notifications
You must be signed in to change notification settings - Fork 1
[refactor] 마이페이지 내 게시글 조회(내가 기록하거나 저장한) API 리팩토링 및 응답 형식 변경 #195
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: develop
Are you sure you want to change the base?
Conversation
|
Important Review skippedAuto reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the You can disable this status message by setting the Walkthrough마이페이지 산책 기록 조회 API의 응답 필드를 재설계하여 작성자와 태그 정보를 제거하고, 지역명 및 산책 시간 필드를 추가합니다. 쿼리 로직을 새로운 서비스 계층으로 이관하고, 좋아요 상태 조회를 위한 새로운 저장소 메서드를 도입합니다. Changes
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes Possibly related issues
Possibly related PRs
Suggested reviewers
Pre-merge checks and finishing touches❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
|
@CodeRabbit review |
✅ Actions performedReview triggered.
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 3
🧹 Nitpick comments (7)
src/main/java/org/sopt/pawkey/backendapi/domain/Post/domain/repository/PostLikeRepository.java (1)
7-8: 도메인 계층에서 인프라 의존성 제거 권장도메인 계층 인터페이스에서 Spring Data JPA 어노테이션 import(
@Query,@Param)는 클린 아키텍처 원칙에 어긋납니다. 이러한 어노테이션은 인프라 계층(SpringDataPostLikeRepository)에만 사용되어야 하며, 도메인 인터페이스는 순수 메서드 시그니처만 선언해야 합니다.🔎 도메인 인터페이스 정리 제안
package org.sopt.pawkey.backendapi.domain.post.domain.repository; import java.util.List; import java.util.Optional; import org.sopt.pawkey.backendapi.domain.post.infra.persistence.entity.PostLikeEntity; -import org.springframework.data.jpa.repository.Query; -import org.springframework.data.repository.query.Param; public interface PostLikeRepository { PostLikeEntity save(PostLikeEntity postLike);src/main/java/org/sopt/pawkey/backendapi/domain/Post/application/dto/result/GetPostCardResult.java (1)
4-6: 사용하지 않는 import 제거 필요
List와AuthorDtoimport가 더 이상 사용되지 않습니다. 리팩토링 과정에서 해당 필드들이 제거되었으므로 import도 정리해주세요.🔎 제안된 수정
package org.sopt.pawkey.backendapi.domain.post.application.dto.result; import java.time.LocalDateTime; -import java.util.List; - -import org.sopt.pawkey.backendapi.domain.user.api.dto.AuthorDto; import lombok.Builder;src/main/java/org/sopt/pawkey/backendapi/domain/user/application/facade/query/UserLikedPostQueryFacade.java (1)
45-48: NPE 위험 및 코드 중복 존재
getRegion(),getParent(),getDuration()접근 시 null 체크가 없어 NPE 발생 가능성이 있습니다.- 이 로직이
UserWrittenPostQueryService(lines 29-32)와 동일하게 중복됩니다.별도 헬퍼 메서드나 공통 유틸로 추출하는 것을 권장합니다.
🔎 안전한 regionName 구성 예시
// 예: PostEntity 또는 RouteEntity에 헬퍼 메서드 추가 public String getFullRegionName() { if (getRegion() == null) return ""; String parent = getRegion().getParent() != null ? getRegion().getParent().getRegionName() + " " : ""; return parent + getRegion().getRegionName(); }참고:
PostQueryService에서는post.getRoute().getRegion().getFullRegionName()을 사용하고 있습니다. 동일한 패턴 적용을 검토해주세요.src/main/java/org/sopt/pawkey/backendapi/domain/user/application/facade/query/UserWrittenPostQueryFacade.java (1)
3-12: 사용하지 않는 import 정리 필요리팩토링 후 더 이상 사용되지 않는 import들이 남아있습니다:
DateTimeFormatter,ComparatorPetEntity,PostLikeRepository,PostRepository,PostEntity🔎 제안된 수정
package org.sopt.pawkey.backendapi.domain.user.application.facade.query; -import java.time.format.DateTimeFormatter; -import java.util.Comparator; import java.util.List; -import org.sopt.pawkey.backendapi.domain.pet.infra.persistence.entity.PetEntity; import org.sopt.pawkey.backendapi.domain.post.api.dto.response.PostCardResponseDto; import org.sopt.pawkey.backendapi.domain.post.application.dto.result.GetPostCardResult; -import org.sopt.pawkey.backendapi.domain.post.domain.repository.PostLikeRepository; -import org.sopt.pawkey.backendapi.domain.post.domain.repository.PostRepository; -import org.sopt.pawkey.backendapi.domain.post.infra.persistence.entity.PostEntity; import org.sopt.pawkey.backendapi.domain.user.application.service.UserWrittenPostQueryService;src/main/java/org/sopt/pawkey/backendapi/domain/user/application/service/UserWrittenPostQueryService.java (3)
10-10: 사용하지 않는 import
PostLikeEntity가 import되어 있지만 사용되지 않습니다.-import org.sopt.pawkey.backendapi.domain.post.infra.persistence.entity.PostLikeEntity;
25-45: List.contains() 성능 개선 권장Line 40의
likedPostIds.contains(post.getPostId())는 각 게시물마다 O(n) 탐색을 수행합니다. 게시물 수가 많아지면 O(n²) 성능 문제가 발생할 수 있습니다.
List를Set으로 변환하여 O(1) 조회로 개선하는 것을 권장합니다.🔎 Set 사용 제안
+import java.util.Set; +import java.util.HashSet; public List<GetPostCardResult> findMyPostResults(UserEntity user, List<Long> likedPostIds) { + Set<Long> likedPostIdSet = new HashSet<>(likedPostIds); return postRepository.findAllByUser(user).stream() .sorted(Comparator.comparing(PostEntity::getPostId).reversed()) .map(post -> { // ... - .isLike(likedPostIds.contains(post.getPostId())) + .isLike(likedPostIdSet.contains(post.getPostId())) // ... }) .toList(); }
26-27: 메모리 정렬 vs DB 정렬현재
postRepository.findAllByUser(user)후 메모리에서 정렬하고 있습니다. 데이터 양이 적다면 문제없지만, 게시물이 많아지면 DB 레벨에서ORDER BY post_id DESC로 정렬하는 것이 더 효율적입니다.
📜 Review details
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (10)
src/main/java/org/sopt/pawkey/backendapi/domain/Post/api/dto/response/PostCardResponseDto.javasrc/main/java/org/sopt/pawkey/backendapi/domain/Post/application/dto/result/GetPostCardResult.javasrc/main/java/org/sopt/pawkey/backendapi/domain/Post/domain/repository/PostLikeRepository.javasrc/main/java/org/sopt/pawkey/backendapi/domain/Post/infra/persistence/repository/PostLikeRepositoryImpl.javasrc/main/java/org/sopt/pawkey/backendapi/domain/Post/infra/persistence/repository/PostQueryRepositoryImpl.javasrc/main/java/org/sopt/pawkey/backendapi/domain/Post/infra/persistence/repository/SpringDataPostLikeRepository.javasrc/main/java/org/sopt/pawkey/backendapi/domain/user/api/controller/UserController.javasrc/main/java/org/sopt/pawkey/backendapi/domain/user/application/facade/query/UserLikedPostQueryFacade.javasrc/main/java/org/sopt/pawkey/backendapi/domain/user/application/facade/query/UserWrittenPostQueryFacade.javasrc/main/java/org/sopt/pawkey/backendapi/domain/user/application/service/UserWrittenPostQueryService.java
🧰 Additional context used
🧠 Learnings (2)
📓 Common learnings
Learnt from: daeun-han
Repo: PAW-KEY/PAWKEY-Backend PR: 55
File: src/main/java/org/sopt/pawkey/backendapi/domain/user/application/facade/query/UserLikedPostQueryFacade.java:55-55
Timestamp: 2025-07-13T11:54:29.024Z
Learning: 좋아요한 게시물 목록을 조회하는 API에서 isLiked 필드를 true로 하드코딩하는 것은 성능상 합리적입니다. PostLikeEntity 리스트가 이미 좋아요를 누른 게시물들만 포함하므로 별도 쿼리로 좋아요 상태를 확인하는 것은 중복이고 비효율적입니다.
📚 Learning: 2025-07-13T11:54:29.024Z
Learnt from: daeun-han
Repo: PAW-KEY/PAWKEY-Backend PR: 55
File: src/main/java/org/sopt/pawkey/backendapi/domain/user/application/facade/query/UserLikedPostQueryFacade.java:55-55
Timestamp: 2025-07-13T11:54:29.024Z
Learning: 좋아요한 게시물 목록을 조회하는 API에서 isLiked 필드를 true로 하드코딩하는 것은 성능상 합리적입니다. PostLikeEntity 리스트가 이미 좋아요를 누른 게시물들만 포함하므로 별도 쿼리로 좋아요 상태를 확인하는 것은 중복이고 비효율적입니다.
Applied to files:
src/main/java/org/sopt/pawkey/backendapi/domain/user/application/facade/query/UserLikedPostQueryFacade.javasrc/main/java/org/sopt/pawkey/backendapi/domain/Post/domain/repository/PostLikeRepository.java
🧬 Code graph analysis (1)
src/main/java/org/sopt/pawkey/backendapi/domain/user/application/service/UserWrittenPostQueryService.java (2)
src/main/java/org/sopt/pawkey/backendapi/domain/user/application/facade/query/UserWrittenPostQueryFacade.java (1)
Service(23-42)src/main/java/org/sopt/pawkey/backendapi/domain/user/application/service/UserLikedPostQueryService.java (1)
Service(12-22)
🔇 Additional comments (11)
src/main/java/org/sopt/pawkey/backendapi/domain/Post/infra/persistence/repository/PostLikeRepositoryImpl.java (1)
39-42: LGTM! 효율적인 좋아요 조회 메서드 추가사용자가 좋아요한 게시글 ID 목록을 반환하는 메서드가 올바르게 구현되었습니다. JPA 리포지토리에 위임하는 방식이 기존 패턴과 일관되며, N+1 문제 해결을 위한 인메모리 매칭 전략을 지원합니다.
src/main/java/org/sopt/pawkey/backendapi/domain/Post/domain/repository/PostLikeRepository.java (1)
19-19: 메서드 시그니처 확인 완료메서드 선언이 명확하며 반환 타입과 파라미터가 적절합니다.
src/main/java/org/sopt/pawkey/backendapi/domain/Post/infra/persistence/repository/SpringDataPostLikeRepository.java (1)
34-35: LGTM! N+1 문제 해결을 위한 효율적인 쿼리필요한 게시글 ID만 조회하는 효율적인 JPQL 쿼리입니다. 전체 엔티티를 조회하지 않고 ID만 가져오므로 성능이 최적화되며, N+1 문제 해결을 위한 인메모리 매칭 전략을 지원합니다.
src/main/java/org/sopt/pawkey/backendapi/domain/Post/infra/persistence/repository/PostQueryRepositoryImpl.java (2)
127-127: 산책 시간 계산 방식 검증 필요정수 나눗셈으로 인해 소수점이 버려집니다(예: 90초 → 1분). 반올림이 필요한지 확인해 주세요.
현재 구현:
(int)(duration / 60)→ 버림 처리
대안:(int)Math.round(duration / 60.0)→ 반올림 처리UI에 표시되는 산책 시간이 버림으로 처리되어도 사용자 경험에 문제가 없는지 검토가 필요합니다.
129-138: 리팩토링된 DTO 구조로 올바르게 변환
regionName,durationMinutes필드 추가 및 불필요한 필드 제거가 피그마 디자인 명세에 맞춰 정확히 구현되었습니다.isLike판단도 인메모리 ID 매칭으로 효율적으로 처리됩니다.src/main/java/org/sopt/pawkey/backendapi/domain/user/api/controller/UserController.java (1)
36-36: LGTM! Swagger 문서에서 인증 파라미터 적절히 숨김 처리JWT 토큰에서 추출되는
userId파라미터를 Swagger 문서에서 숨기는 것은 올바른 구현입니다. API 사용자에게 혼란을 주지 않으며, 내부 인증 메커니즘을 문서에 노출하지 않는 보안상 좋은 관행입니다.Also applies to: 83-83, 111-111
src/main/java/org/sopt/pawkey/backendapi/domain/Post/application/dto/result/GetPostCardResult.java (1)
10-19: Record 구조 적절함새로운 응답 형식에 맞게 필드가 잘 정리되었습니다.
@Builder패턴을 사용하여 가독성 있게 객체를 생성할 수 있습니다.src/main/java/org/sopt/pawkey/backendapi/domain/user/application/facade/query/UserLikedPostQueryFacade.java (1)
56-58: isLiked = true 하드코딩 및 이미지 URL 접근
isLiked = true하드코딩은 좋아요한 게시물 목록이므로 성능상 합리적입니다. (Based on learnings)다만, Line 57의
getTrackingImage().getImageUrl()접근 시getTrackingImage()가 null일 경우 NPE가 발생할 수 있습니다.🔎 null-safe 접근 제안
- post.getRoute().getTrackingImage().getImageUrl() + post.getRoute().getTrackingImage() != null + ? post.getRoute().getTrackingImage().getImageUrl() + : nullsrc/main/java/org/sopt/pawkey/backendapi/domain/user/application/facade/query/UserWrittenPostQueryFacade.java (1)
31-41: Facade-Service 분리 잘 적용됨Facade가 흐름 제어만 담당하고 비즈니스 로직은
UserWrittenPostQueryService에 위임하는 구조가 잘 적용되었습니다. PR 목표에 부합합니다.src/main/java/org/sopt/pawkey/backendapi/domain/user/application/service/UserWrittenPostQueryService.java (1)
47-49: getLikedPostIds 메서드 적절함좋아요한 게시글 ID를 한 번에 조회하여 N+1 문제를 해결하는 In-memory ID 매칭 방식이 PR 목표대로 잘 구현되었습니다.
src/main/java/org/sopt/pawkey/backendapi/domain/Post/api/dto/response/PostCardResponseDto.java (1)
10-30: DTO 구조 및 매핑 로직 적절함새로운 응답 형식에 맞게 lean한 DTO로 잘 리팩토링되었습니다.
from()메서드를 통한 매핑 로직이 깔끔하고, 날짜 포맷팅(yyyy/MM/dd)이UserLikedPostQueryFacade와 일관성 있게 적용되었습니다.
📌 PR 제목
[refactor] 마이페이지 내 게시글 조회(내가 기록하거나 저장한) API 리팩토링 및 응답 형식 변경
✨ 요약 설명
마이페이지의 '내가 작성한 게시글 조회'과 '저장한 게시글' API를 피그마 상의 새로운 응답 형식에 맞춰 리팩토링했습니다. Facade-Service 계층 분리를 통해 아키텍처 완성도를 높였고, In-memory ID 매칭 방식을 도입하여 좋아요 여부 확인 시 발생하는 N+1 문제를 해결했습니다.
🧾 변경 사항
📂 PR 타입
🧪 테스트
✅ 체크리스트
💬 리뷰어에게 전달할 말
이번 작업에서는 DTO 형식 변경을 넘어 '확장 가능한 아키텍처'를 고민해 보았습니다. 기존 Facade에 몰려있던 로직을 Service로 위임하였고, 특히 duration(초)을 durationMinutes(분)로 변환하거나 지역명을 조합하는 등의 비즈니스 규칙을 서비스 내에 캡슐화하여 파사드의 복잡도를 낮췄습니다.
또한, 공통 DTO인 PostCardResponseDto를 수정함에 따라 사이드 이펙트가 발생한 다른 도메인 코드들도 함께 동기화 작업을 진행했습니다. 아키텍처상 서비스와 파사드의 분리 수준이 적절한지, 혹은 가공 로직의 위치에 대해 다른 의견이 있으신지 리뷰 부탁드립니다!
🔗 관련 이슈
Summary by CodeRabbit
릴리스 노트
새 기능
개선 사항
✏️ Tip: You can customize this high-level summary in your review settings.