Conversation
- postLike, postStatistics 을 통해 최적화 - 좋아요 수 증감 비동기 처리
|
""" Walkthrough게시글 하트(좋아요) 기능이 전체적으로 도입되었습니다. 하트 개수 및 사용자의 하트 여부가 게시글 상세 및 목록 조회 응답에 포함되고, 하트 토글을 위한 API가 추가되었습니다. 하트 데이터 저장, 통계 관리, 비동기 처리, 예외 처리, 테스트 코드가 함께 구현되었습니다. Changes
Sequence Diagram(s)sequenceDiagram
participant User
participant PostLikeController
participant PostLikeService
participant PostLikeEntityRepository
participant AsyncPostStatisticsService
participant PostStatisticsEntityRepository
User->>PostLikeController: POST /api/posts/{postId}/like
PostLikeController->>PostLikeService: toggleLike(userId, postId)
PostLikeService->>PostLikeEntityRepository: existsByUserIdAndPostId
alt 이미 하트함
PostLikeService->>PostLikeEntityRepository: deleteByUserIdAndPostId
PostLikeService->>AsyncPostStatisticsService: decrementLikeCount(postId)
else 하트 안함
PostLikeService->>PostLikeEntityRepository: save(PostLikeEntity)
PostLikeService->>AsyncPostStatisticsService: incrementLikeCount(postId)
end
sequenceDiagram
participant User
participant PostController
participant PostService
participant PostLikeService
User->>PostController: GET /api/posts/{id}
PostController->>PostService: getPost(id, userId)
PostService->>PostLikeService: getLikeCount(postId)
PostService->>PostLikeService: isLikedByUser(userId, postId)
PostService->>PostController: GetPostResponse(likeCount, isLikedByCurrentUser)
PostController->>User: 응답 반환
Assessment against linked issues
Poem
📜 Recent review detailsConfiguration used: CodeRabbit UI 📒 Files selected for processing (1)
✅ Files skipped from review due to trivial changes (1)
🪧 TipsChatThere are 3 ways to chat with CodeRabbit:
SupportNeed help? Create a ticket on our support page for assistance with any issues or questions. Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments. CodeRabbit Commands (Invoked using PR comments)
Other keywords and placeholders
CodeRabbit Configuration File (
|
There was a problem hiding this comment.
Summary of Changes
Hello @goalSetter09, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!
This pull request introduces a new 'heart' (like) feature for posts, enabling users to express their appreciation for content. The implementation includes a robust data model for tracking likes and their aggregated counts, an asynchronous service to update these statistics efficiently, and updates to existing post viewing functionalities to display like information. A new API endpoint is provided for users to toggle their like status on posts, with a specific check to prevent self-liking.
Highlights
- Post Like Functionality: Implemented a new 'heart' (like) feature for posts, allowing users to toggle their like status on a given post.
- Dedicated Data Models: Introduced two new entities:
PostLikeEntityto record individual user-post like relationships andPostStatisticsEntityto store aggregated like counts for posts, optimized for performance and utilizing optimistic locking. - Asynchronous Statistics Updates: Developed
AsyncPostStatisticsServiceto handle the incrementing, decrementing, and initialization of post like counts in the background, ensuring that user interactions remain responsive. - Enhanced Post Views: Modified
PostServiceand related DTOs (GetPostResponse,GetPostsResponse) to include the total like count and a boolean indicating whether the current authenticated user has liked a post, visible in both single post detail and post list views. - New API Endpoint: Added a dedicated
POST /api/posts/{postId}/likeendpoint inPostLikeControllerfor users to easily toggle their like status on a specific post. - Self-Like Prevention: Introduced a new error code (
CANNOT_LIKE_OWN_POST_EXCEPTION) and corresponding logic to prevent users from liking their own posts.
Using Gemini Code Assist
The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.
Invoking Gemini
You can request assistance from Gemini at any point in your pull request via creating an issue comment (i.e. comment on the pull request page) using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands.
| Feature | Command | Description |
|---|---|---|
| Code Review | /gemini review |
Performs a code review for the current pull request in its current state. |
| Pull Request Summary | /gemini summary |
Provides a summary of the current pull request in its current state. |
| Comment | @gemini-code-assist | Responds in comments when explicitly tagged, both in issue comments and review comments. |
| Help | /gemini help |
Displays a list of available commands. |
Customization
To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.
Limitations & Feedback
Gemini Code Assist is currently in preview and may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments to provide feedback.
You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.
Footnotes
-
Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution. ↩
There was a problem hiding this comment.
Pull Request Overview
Adds a post “heart” (like) feature, including toggling likes, tracking counts, and indicating if the current user has liked a post.
- Introduces a new PostLikeController and PostLikeService with asynchronous statistics updates
- Extends GetPostResponse and GetPostsResponse DTOs with
likeCount(andisLikedByCurrentUserfor single-post) - Updates PostService and PostController to integrate like counts and user-specific like status
- Adds repositories and entities for persisting like relations and aggregated statistics
Reviewed Changes
Copilot reviewed 14 out of 14 changed files in this pull request and generated 1 comment.
Show a summary per file
| File | Description |
|---|---|
| src/test/java/hanium/modic/backend/web/post/controller/PostControllerTest.java | Verifies new likeCount and isLikedByCurrentUser fields in single-post API |
| src/test/java/hanium/modic/backend/domain/post/service/PostServiceTest.java | Tests service’s getPost overloads for liked/not-liked cases |
| src/main/java/hanium/modic/backend/web/postLike/controller/PostLikeController.java | New REST endpoint for toggling a post’s like |
| src/main/java/hanium/modic/backend/web/post/dto/response/GetPostsResponse.java | Add likeCount to posts-list response |
| src/main/java/hanium/modic/backend/web/post/dto/response/GetPostResponse.java | Add likeCount and isLikedByCurrentUser |
| src/main/java/hanium/modic/backend/web/post/controller/PostController.java | Wire user ID into getPost and getPosts methods |
| src/main/java/hanium/modic/backend/domain/postLike/service/PostLikeService.java | Business logic for toggling likes and querying counts |
| src/main/java/hanium/modic/backend/domain/postLike/service/AsyncPostStatisticsService.java | Async updates for like statistics |
| src/main/java/hanium/modic/backend/domain/postLike/repository/PostStatisticsEntityRepository.java | CRUD for aggregated like statistics |
| src/main/java/hanium/modic/backend/domain/postLike/repository/PostLikeEntityRepository.java | CRUD for individual user-post likes |
| src/main/java/hanium/modic/backend/domain/postLike/entity/PostStatisticsEntity.java | Entity for per-post like counts with optimistic locking |
| src/main/java/hanium/modic/backend/domain/postLike/entity/PostLikeEntity.java | Entity for user-post like relationship |
| src/main/java/hanium/modic/backend/domain/post/service/PostService.java | Fetch and inject like data into post APIs; init stats on create |
| src/main/java/hanium/modic/backend/common/error/ErrorCode.java | New error code PL-001 for liking own post |
Comments suppressed due to low confidence (3)
src/main/java/hanium/modic/backend/web/post/controller/PostController.java:59
- Requiring a non-null @currentuser for the getPost endpoint prevents unauthenticated reads and breaks backward compatibility. Consider accepting an optional user or overloading the endpoint so that unauthenticated clients can still retrieve posts (with isLikedByCurrentUser == null).
public ResponseEntity<AppResponse<GetPostResponse>> getPost(@PathVariable Long id, @CurrentUser UserEntity user) {
src/main/java/hanium/modic/backend/web/postLike/controller/PostLikeController.java:31
- The new POST /api/posts/{postId}/like endpoint is not covered by any controller tests; add tests for successful toggles and error cases (liking own post, non-existent post, unauthenticated).
@PostMapping("/{postId}/like")
src/main/java/hanium/modic/backend/domain/post/service/PostService.java:76
- This asynchronous initialization of post statistics on createPost is new behavior but lacks unit tests; consider verifying that initializeStatistics is invoked.
asyncPostStatisticsService.initializeStatistics(post.getId());
src/main/java/hanium/modic/backend/domain/post/service/PostService.java
Outdated
Show resolved
Hide resolved
There was a problem hiding this comment.
Code Review
The code introduces a new 'like' feature. However, there are critical concurrency issues in AsyncPostStatisticsService.java and PostLikeService.java that must be addressed to prevent data inconsistencies. The deleteByUserIdAndPostId method in PostLikeEntityRepository.java should return the number of deleted rows.
| } catch (Exception e) { | ||
| log.error("하트 수 증가 실패: postId={}", postId, e); | ||
| // 비동기 처리이므로 예외를 던지지 않고 로깅만 수행 | ||
| } |
There was a problem hiding this comment.
The generic catch (Exception e) block can hide ObjectOptimisticLockingFailureException exceptions, leading to silent data loss during concurrent updates. Consider handling this specific exception explicitly to implement a retry mechanism or log a more specific warning.
} catch (ObjectOptimisticLockingFailureException e) {
log.warn("Like count increment failed due to concurrency conflict for postId={}. An update was lost. A retry mechanism should be implemented.", postId, e);
} catch (Exception e) {
log.error("하트 수 증가 실패: postId={}", postId, e);
}| } catch (Exception e) { | ||
| log.error("하트 수 감소 실패: postId={}", postId, e); | ||
| // 비동기 처리이므로 예외를 던지지 않고 로깅만 수행 | ||
| } |
There was a problem hiding this comment.
Similar to incrementLikeCount, the generic catch (Exception e) block can hide ObjectOptimisticLockingFailureException exceptions, leading to silent data loss during concurrent unlike actions. Handle the optimistic locking exception specifically.
} catch (ObjectOptimisticLockingFailureException e) {
log.warn("Like count decrement failed due to concurrency conflict for postId={}. An update was lost. A retry mechanism should be implemented.", postId, e);
} catch (Exception e) {
log.error("하트 수 감소 실패: postId={}", postId, e);
}
src/main/java/hanium/modic/backend/domain/postLike/service/PostLikeService.java
Outdated
Show resolved
Hide resolved
src/main/java/hanium/modic/backend/domain/postLike/repository/PostLikeEntityRepository.java
Outdated
Show resolved
Hide resolved
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (7)
src/main/java/hanium/modic/backend/domain/postLike/repository/PostLikeEntityRepository.java (1)
22-24: 커스텀 삭제 쿼리에서 반환값 고려해보세요.
@Modifying어노테이션을 사용한 커스텀 삭제 쿼리가 적절하게 구현되었습니다. 다만 삭제된 행의 수를 반환하도록 하면 삭제 성공 여부를 확인할 수 있어 더 좋을 것 같습니다.-void deleteByUserIdAndPostId(@Param("userId") Long userId, @Param("postId") Long postId); +int deleteByUserIdAndPostId(@Param("userId") Long userId, @Param("postId") Long postId);src/main/java/hanium/modic/backend/domain/postLike/entity/PostLikeEntity.java (1)
33-37: 필드 validation 추가를 고려해보세요.현재 nullable = false로 데이터베이스 제약은 있지만, 애플리케이션 레벨에서의 검증도 추가하면 더 안전합니다.
+@NotNull(message = "사용자 ID는 필수입니다") +@Positive(message = "사용자 ID는 양수여야 합니다") @Column(name = "user_id", nullable = false) private Long userId; +@NotNull(message = "게시글 ID는 필수입니다") +@Positive(message = "게시글 ID는 양수여야 합니다") @Column(name = "post_id", nullable = false) private Long postId;src/main/java/hanium/modic/backend/web/postLike/controller/PostLikeController.java (1)
31-31: 토글 API의 명확성을 향상시키는 것을 고려해보세요.현재 POST 방식의 토글은 RESTful하지만, API 소비자 입장에서는 현재 상태를 알기 어렵습니다. 응답에 토글 후 상태를 포함하는 것을 고려해보세요.
-public ResponseEntity<AppResponse<Void>> togglePostLike( +public ResponseEntity<AppResponse<PostLikeToggleResponse>> togglePostLike( @PathVariable @Positive(message = "게시글 ID는 양수여야 합니다.") Long postId, @CurrentUser UserEntity user) { - postLikeService.toggleLike(user.getId(), postId); - return ResponseEntity.ok(AppResponse.ok(null)); + boolean isLiked = postLikeService.toggleLike(user.getId(), postId); + return ResponseEntity.ok(AppResponse.ok(new PostLikeToggleResponse(isLiked))); }src/main/java/hanium/modic/backend/web/post/controller/PostController.java (1)
59-61: 사용자 파라미터 추가로 좋아요 기능 통합이 잘 되었습니다.현재 사용자의 좋아요 상태를 포함한 게시글 조회가 가능해졌습니다. 하지만 비인증 사용자도 게시글을 조회할 수 있어야 한다면, 사용자 파라미터를 Optional로 처리하는 것을 고려해보세요.
-public ResponseEntity<AppResponse<GetPostResponse>> getPost(@PathVariable Long id, @CurrentUser UserEntity user) { - GetPostResponse response = postService.getPost(id, user.getId()); +public ResponseEntity<AppResponse<GetPostResponse>> getPost(@PathVariable Long id, @CurrentUser(required = false) UserEntity user) { + Long userId = user != null ? user.getId() : null; + GetPostResponse response = postService.getPost(id, userId); return ResponseEntity.ok(AppResponse.ok(response)); }src/main/java/hanium/modic/backend/domain/postLike/entity/PostStatisticsEntity.java (2)
34-35: likeCount 데이터 타입을 재검토해보세요.현재 Long 타입을 사용하고 있는데, 게시글 좋아요 수가 Integer 범위를 넘을 가능성이 낮다면 Integer 사용을 고려해보세요. 이는 인덱스 크기와 메모리 사용량을 줄일 수 있습니다.
-@Column(name = "like_count", nullable = false) -private Long likeCount; +@Column(name = "like_count", nullable = false) +private Integer likeCount;단, 향후 확장성을 고려한다면 현재 Long 타입도 적절합니다.
37-38: version 필드 초기화를 명시적으로 처리하세요.JPA에서 @Version 필드는 보통 0으로 초기화되지만, 명시적으로 처리하면 더 명확합니다.
@Version -private Long version; // 낙관적 락으로 동시성 제어 +private Long version = 0L; // 낙관적 락으로 동시성 제어src/main/java/hanium/modic/backend/domain/postLike/service/PostLikeService.java (1)
95-105: 효율적인 배치 조회 구현입니다.빈 리스트 체크와 단일 쿼리로 여러 게시글의 통계를 조회하는 점이 좋습니다.
다만 통계 데이터가 없는 게시글의 경우 Map에 포함되지 않으므로, 호출하는 쪽에서 기본값 처리가 필요할 수 있습니다.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (14)
src/main/java/hanium/modic/backend/common/error/ErrorCode.java(1 hunks)src/main/java/hanium/modic/backend/domain/post/service/PostService.java(9 hunks)src/main/java/hanium/modic/backend/domain/postLike/entity/PostLikeEntity.java(1 hunks)src/main/java/hanium/modic/backend/domain/postLike/entity/PostStatisticsEntity.java(1 hunks)src/main/java/hanium/modic/backend/domain/postLike/repository/PostLikeEntityRepository.java(1 hunks)src/main/java/hanium/modic/backend/domain/postLike/repository/PostStatisticsEntityRepository.java(1 hunks)src/main/java/hanium/modic/backend/domain/postLike/service/AsyncPostStatisticsService.java(1 hunks)src/main/java/hanium/modic/backend/domain/postLike/service/PostLikeService.java(1 hunks)src/main/java/hanium/modic/backend/web/post/controller/PostController.java(1 hunks)src/main/java/hanium/modic/backend/web/post/dto/response/GetPostResponse.java(2 hunks)src/main/java/hanium/modic/backend/web/post/dto/response/GetPostsResponse.java(2 hunks)src/main/java/hanium/modic/backend/web/postLike/controller/PostLikeController.java(1 hunks)src/test/java/hanium/modic/backend/domain/post/service/PostServiceTest.java(5 hunks)src/test/java/hanium/modic/backend/web/post/controller/PostControllerTest.java(4 hunks)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
- GitHub Check: build
🔇 Additional comments (32)
src/main/java/hanium/modic/backend/common/error/ErrorCode.java (1)
45-47: 새로운 에러 코드 추가가 적절합니다.
CANNOT_LIKE_OWN_POST_EXCEPTION에러 코드가 기존 패턴을 잘 따르고 있습니다. HTTP 상태 코드, 에러 코드 네이밍, 한국어 메시지 모두 적절하게 구현되었습니다.src/main/java/hanium/modic/backend/domain/postLike/repository/PostLikeEntityRepository.java (1)
12-35: 리포지토리 인터페이스가 잘 설계되었습니다.하트 기능에 필요한 모든 메서드가 적절히 정의되어 있고, 각 메서드의 용도가 주석으로 명확히 설명되어 있습니다. 미래 기능용과 백업용 메서드도 고려하여 설계한 점이 좋습니다.
src/main/java/hanium/modic/backend/web/post/dto/response/GetPostsResponse.java (1)
18-46: 하위 호환성을 고려한 DTO 확장이 훌륭합니다.
likeCount필드 추가와 함께 기존 코드와의 호환성을 유지하기 위한 오버로드 메서드를 제공한 것이 좋습니다. 기본값으로 0L을 사용하는 것도 적절합니다.src/test/java/hanium/modic/backend/domain/post/service/PostServiceTest.java (3)
56-59: 새로운 의존성 모킹이 적절합니다.하트 기능과 관련된
PostLikeService와AsyncPostStatisticsService모킹이 올바르게 추가되었습니다.
111-183: 하트 기능 테스트 케이스가 포괄적입니다.사용자가 좋아요를 한 경우와 하지 않은 경우를 모두 테스트하고, 각각의 응답값과 서비스 호출을 검증하고 있어 테스트 커버리지가 우수합니다. 테스트 메서드명도 명확하고 한국어로 잘 작성되었습니다.
185-200: 예외 상황 테스트가 잘 구현되었습니다.존재하지 않는 게시글에 대한 예외 처리 테스트가
currentUserId파라미터를 포함하여 올바르게 업데이트되었습니다.src/main/java/hanium/modic/backend/domain/post/service/PostService.java (5)
42-44: 새로운 의존성 주입이 적절합니다.하트 기능 관련 서비스들이 명확한 주석과 함께 올바르게 주입되었습니다.
75-77: 비동기 통계 초기화가 좋은 설계입니다.게시글 생성 후 통계를 비동기로 초기화하는 것은 성능상 좋은 접근입니다. 게시글 생성 작업이 블로킹되지 않아 사용자 경험이 향상됩니다.
116-126: 배치 쿼리 최적화가 우수합니다.여러 게시글의 하트 수를 한 번의 쿼리로 조회하는 최적화가 잘 구현되었습니다. N+1 쿼리 문제를 방지하고 성능을 크게 향상시킬 것입니다.
94-101: 하트 기능 통합이 잘 구현되었습니다.하트 수 조회와 사용자의 좋아요 여부 확인 로직이 깔끔하게 통합되었고, 응답 DTO에 필요한 정보를 모두 전달하고 있습니다.
82-82: getPost 호출부 일관성 검증 완료메서드 시그니처 변경 이후
PostController와 모든 관련 테스트(PostControllerTest,PostServiceTest)에서getPost(id, userId)형태로 정상적으로 두 개의 파라미터를 전달하고 있음을 확인했습니다. 추가 수정은 필요 없습니다.src/main/java/hanium/modic/backend/domain/postLike/repository/PostStatisticsEntityRepository.java (1)
13-41: 전반적인 리포지토리 설계가 우수합니다.JPA 표준을 잘 따르고 있으며, 배치 조회와 개별 조회를 적절히 분리하여 성능을 고려한 설계입니다.
src/main/java/hanium/modic/backend/domain/postLike/entity/PostLikeEntity.java (2)
22-24: 유니크 제약조건 설계가 적절합니다.사용자가 동일한 게시글에 중복으로 좋아요를 누르는 것을 데이터베이스 레벨에서 방지하는 좋은 설계입니다.
48-53: 정적 팩토리 메서드가 잘 구현되었습니다.빌더 패턴을 활용한 깔끔한 객체 생성 방법입니다.
src/main/java/hanium/modic/backend/web/postLike/controller/PostLikeController.java (2)
31-43: 컨트롤러 설계가 REST 원칙을 잘 따르고 있습니다.적절한 HTTP 메서드 사용, 파라미터 검증, 인증 처리가 잘 구현되어 있습니다.
32-37: API 문서화가 상세하고 명확합니다.Swagger 어노테이션을 통해 API 사용법과 예상 응답 코드가 잘 설명되어 있습니다.
src/main/java/hanium/modic/backend/web/post/controller/PostController.java (1)
44-46: 어노테이션 및 메서드 시그니처 포맷팅이 깔끔해졌습니다.가독성을 해치지 않으면서 코드가 더 간결해졌습니다.
Also applies to: 65-66, 76-78, 85-87
src/main/java/hanium/modic/backend/domain/postLike/entity/PostStatisticsEntity.java (3)
37-38: 낙관적 락을 통한 동시성 제어가 잘 구현되었습니다.@Version 어노테이션을 사용해 동시 수정 시 충돌을 방지하는 좋은 설계입니다.
56-58: 하트 수 감소 시 음수 방지가 잘 구현되었습니다.Math.max를 사용해 안전하게 음수 방지 처리가 되어 있습니다.
63-68: 정적 팩토리 메서드가 명확하고 사용하기 쉽습니다.새 게시글용 통계 엔티티 생성이 직관적이고 초기값 설정이 적절합니다.
src/test/java/hanium/modic/backend/web/post/controller/PostControllerTest.java (3)
138-159: 테스트 케이스가 좋아요 기능을 적절히 검증합니다.인증된 사용자의 게시글 조회 테스트가 새로운 좋아요 관련 필드(
likeCount,isLikedByCurrentUser)를 올바르게 검증하고 있습니다.
168-169: 실패 테스트도 일관성 있게 업데이트되었습니다.서비스 메서드 시그니처 변경에 맞춰 적절히 수정되었습니다.
183-185: 게시물 목록 응답에 좋아요 수가 적절히 포함되었습니다.목록 조회 시에도 각 게시물의 좋아요 수를 확인할 수 있도록 테스트가 업데이트되었습니다.
Also applies to: 212-212
src/main/java/hanium/modic/backend/web/post/dto/response/GetPostResponse.java (2)
27-30: 좋아요 관련 필드가 적절히 추가되었습니다.
isLikedByCurrentUser를 nullable로 처리하여 비로그인 사용자를 고려한 점이 좋습니다.
32-71: 하위 호환성을 유지한 훌륭한 설계입니다.기존 API를 사용하는 코드에 영향을 주지 않으면서 새로운 기능을 추가했습니다.
src/main/java/hanium/modic/backend/domain/postLike/service/PostLikeService.java (4)
46-74: 좋아요 토글 로직이 잘 구현되었습니다.게시글 존재 확인과 자기 게시글 좋아요 방지 검증이 적절합니다. PR 설명대로 동시성 처리는 별도 PR에서 다루실 예정이니 현재 구현은 적절합니다.
82-86: 안전한 좋아요 수 조회 구현입니다.통계 데이터가 없을 때 0을 반환하는 처리가 적절합니다.
124-129: 향후 기능을 위한 적절한 준비입니다.사용자가 좋아요한 게시글 목록 조회 기능이 잘 구현되었습니다.
138-160: 통계 정합성 검증 로직이 안전하게 구현되었습니다.불일치 발견 시 로깅과 함께 통계를 재생성하는 방식이 데이터 정합성 보장에 적절합니다.
src/main/java/hanium/modic/backend/domain/postLike/service/AsyncPostStatisticsService.java (3)
28-43: 비동기 좋아요 증가 로직이 안정적으로 구현되었습니다.통계 데이터가 없을 때 새로 생성하는 처리와 예외 발생 시 로깅만 하고 전파하지 않는 점이 적절합니다.
50-66: 좋아요 감소 로직이 안전하게 구현되었습니다.
ifPresentOrElse를 사용한 깔끔한 처리가 좋습니다. 통계 데이터가 없는 경우 경고 로그를 남기는 것도 적절합니다.
73-85: 통계 초기화 로직이 적절합니다.이미 존재하는지 확인 후 생성하는 방어적 프로그래밍이 좋습니다.
| @Modifying | ||
| @Query("UPDATE PostStatisticsEntity p SET p.likeCount = p.likeCount + :delta WHERE p.postId = :postId") | ||
| void updateLikeCount(@Param("postId") Long postId, @Param("delta") int delta); |
There was a problem hiding this comment.
🛠️ Refactor suggestion
updateLikeCount 메서드의 안전성을 향상시키세요.
현재 구현에서 delta 파라미터에 대한 제약이 없어 잘못된 값으로 인한 데이터 오염 가능성이 있습니다.
@Modifying
+@Query("UPDATE PostStatisticsEntity p SET p.likeCount = CASE WHEN (p.likeCount + :delta) < 0 THEN 0 ELSE (p.likeCount + :delta) END WHERE p.postId = :postId")
-@Query("UPDATE PostStatisticsEntity p SET p.likeCount = p.likeCount + :delta WHERE p.postId = :postId")
void updateLikeCount(@Param("postId") Long postId, @Param("delta") int delta);또한 이 메서드를 호출하는 서비스 계층에서 @transactional 어노테이션이 있는지 확인하세요.
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| @Modifying | |
| @Query("UPDATE PostStatisticsEntity p SET p.likeCount = p.likeCount + :delta WHERE p.postId = :postId") | |
| void updateLikeCount(@Param("postId") Long postId, @Param("delta") int delta); | |
| @Modifying | |
| @Query("UPDATE PostStatisticsEntity p SET p.likeCount = CASE WHEN (p.likeCount + :delta) < 0 THEN 0 ELSE (p.likeCount + :delta) END WHERE p.postId = :postId") | |
| void updateLikeCount(@Param("postId") Long postId, @Param("delta") int delta); |
🤖 Prompt for AI Agents
In
src/main/java/hanium/modic/backend/domain/postLike/repository/PostStatisticsEntityRepository.java
around lines 28 to 30, the updateLikeCount method lacks validation on the delta
parameter, risking data corruption from invalid values. Add validation to ensure
delta is only positive or negative as intended, preventing unintended increments
or decrements. Also, verify that the service layer method calling
updateLikeCount is annotated with @Transactional to maintain transaction
integrity during the update.
There was a problem hiding this comment.
Code Review
This pull request introduces a 'like' feature for posts, utilizing a separate statistics table and asynchronous updates for performance. There's a critical race condition in PostLikeService.toggleLike that needs to be addressed to prevent data corruption, and the error handling in AsyncPostStatisticsService could be improved to prevent silent failures.
src/main/java/hanium/modic/backend/domain/postLike/service/PostLikeService.java
Show resolved
Hide resolved
src/main/java/hanium/modic/backend/domain/postLike/service/AsyncPostStatisticsService.java
Show resolved
Hide resolved
|
#113 해결 필요 |
There was a problem hiding this comment.
Actionable comments posted: 1
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (1)
src/test/java/hanium/modic/backend/domain/post/service/PostServiceTest.java(8 hunks)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
- GitHub Check: build
🔇 Additional comments (9)
src/test/java/hanium/modic/backend/domain/post/service/PostServiceTest.java (9)
12-12: 새로운 의존성 import가 적절하게 추가되었습니다.좋아요 기능과 관련된 서비스들과 DTO가 올바르게 import되었습니다.
Also applies to: 38-39, 45-45
58-61: Mock 객체 설정이 적절합니다.PostLikeService와 AsyncPostStatisticsService에 대한 Mock 객체가 올바르게 설정되었습니다.
113-154: 좋아요 기능이 포함된 단일 게시글 조회 테스트가 잘 구현되었습니다.테스트 로직이 명확하고 모든 필요한 검증이 포함되어 있습니다. currentUserId 파라미터가 추가되고 좋아요 상태 검증이 적절히 구현되었습니다.
156-185: 좋아요하지 않은 사용자에 대한 테스트 케이스가 적절히 추가되었습니다.사용자가 좋아요하지 않은 경우의 시나리오를 잘 커버하고 있습니다.
187-202: 예외 처리 테스트가 업데이트되었습니다.currentUserId 파라미터가 추가되어 실제 서비스 메서드 시그니처와 일치합니다.
204-255: 배치 조회 최적화 테스트가 잘 구현되었습니다.좋아요 수와 이미지를 배치로 조회하는 최적화 로직이 올바르게 테스트되고 있습니다. 성능 개선을 위한 중요한 테스트 케이스입니다.
268-268: 기존 테스트가 배치 조회 방식으로 업데이트되었습니다.개별 조회에서 배치 조회로 변경된 것이 올바르게 테스트에 반영되었습니다.
Also applies to: 276-284, 297-298
316-316: 빈 결과에 대한 테스트가 배치 조회 방식으로 업데이트되었습니다.배치 조회 메서드들이 호출되지 않아야 하는 경우에 대한 검증이 적절히 추가되었습니다.
Also applies to: 320-322
324-426: 단순 게시글 조회 기능에 대한 포괄적인 테스트가 추가되었습니다.다양한 시나리오(게시글 존재, 게시글 없음, 이미지 없는 게시글)를 모두 커버하는 테스트가 잘 구현되었습니다. 특히 이미지가 없는 경우 null 처리에 대한 테스트도 포함되어 있어 견고합니다.
src/test/java/hanium/modic/backend/domain/post/service/PostServiceTest.java
Show resolved
Hide resolved
- 분산락 적용 - act-then-check 적용
There was a problem hiding this comment.
Actionable comments posted: 3
🧹 Nitpick comments (2)
src/main/java/hanium/modic/backend/common/redis/distributedLock/LockManager.java (1)
101-101: 키 생성 방식 개선을 고려해보세요.현재 문자열 연결을 사용하고 있는데, 가독성과 일관성을 위해
String.format()이나 formatted string을 사용하는 것이 좋을 것 같습니다.-String key = REDISSON_POST_LIKE_LOCK_PREFIX + userId + ":" + postId; +String key = String.format("%s%d:%d", REDISSON_POST_LIKE_LOCK_PREFIX, userId, postId);src/test/java/hanium/modic/backend/domain/postLike/service/PostLikeServiceIntegrationTest.java (1)
125-129: Awaitility 타임아웃 설정을 확인하세요.현재 3초로 설정되어 있는데, CI/CD 환경에서 간헐적으로 실패할 가능성이 있습니다. 필요시 더 긴 타임아웃을 고려해보세요.
-await().atMost(Duration.ofSeconds(3)) +await().atMost(Duration.ofSeconds(5))
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (6)
src/main/java/hanium/modic/backend/common/error/ErrorCode.java(1 hunks)src/main/java/hanium/modic/backend/common/redis/distributedLock/LockManager.java(2 hunks)src/main/java/hanium/modic/backend/domain/postLike/repository/PostLikeEntityRepository.java(1 hunks)src/main/java/hanium/modic/backend/domain/postLike/service/PostLikeService.java(1 hunks)src/main/java/hanium/modic/backend/web/postLike/controller/PostLikeController.java(1 hunks)src/test/java/hanium/modic/backend/domain/postLike/service/PostLikeServiceIntegrationTest.java(1 hunks)
✅ Files skipped from review due to trivial changes (1)
- src/main/java/hanium/modic/backend/domain/postLike/repository/PostLikeEntityRepository.java
🚧 Files skipped from review as they are similar to previous changes (3)
- src/main/java/hanium/modic/backend/common/error/ErrorCode.java
- src/main/java/hanium/modic/backend/web/postLike/controller/PostLikeController.java
- src/main/java/hanium/modic/backend/domain/postLike/service/PostLikeService.java
🧰 Additional context used
🧬 Code Graph Analysis (1)
src/test/java/hanium/modic/backend/domain/postLike/service/PostLikeServiceIntegrationTest.java (2)
src/test/java/hanium/modic/backend/domain/post/entityfactory/PostFactory.java (1)
PostFactory(10-36)src/test/java/hanium/modic/backend/domain/user/factory/UserFactory.java (1)
UserFactory(9-29)
🔇 Additional comments (3)
src/main/java/hanium/modic/backend/common/redis/distributedLock/LockManager.java (2)
29-30: 락 타임아웃 설정이 적절합니다.좋아요 토글은 빠른 작업이므로 더 짧은 대기 시간(2초)과 유지 시간(1초)을 설정한 것이 합리적입니다.
95-120: 분산 락 구현이 잘 되어 있습니다.기존 패턴을 잘 따르고 있으며, 예외 처리와 트랜잭션 관리가 적절히 구현되어 있습니다. 동시성 제어를 위한 분산 락 사용이 PR 목표에 부합합니다.
src/test/java/hanium/modic/backend/domain/postLike/service/PostLikeServiceIntegrationTest.java (1)
26-187: 통합 테스트가 전반적으로 잘 작성되었습니다.동시성 시나리오를 포괄적으로 다루고 있으며, 결과적 일관성(eventual consistency)을 적절히 검증하고 있습니다. 분산 락과 비동기 통계 업데이트가 올바르게 동작하는지 확인하는 좋은 테스트입니다.
src/test/java/hanium/modic/backend/domain/postLike/service/PostLikeServiceIntegrationTest.java
Show resolved
Hide resolved
src/test/java/hanium/modic/backend/domain/postLike/service/PostLikeServiceIntegrationTest.java
Show resolved
Hide resolved
src/test/java/hanium/modic/backend/domain/postLike/service/PostLikeServiceIntegrationTest.java
Show resolved
Hide resolved
There was a problem hiding this comment.
Actionable comments posted: 0
♻️ Duplicate comments (1)
src/test/java/hanium/modic/backend/domain/postLike/service/PostLikeServiceIntegrationTest.java (1)
91-131: 다중 사용자 동시성 테스트가 잘 구현되어 있지만 자기 게시글 좋아요 방지 검증이 누락되었습니다.다중 사용자 동시 좋아요 테스트 로직이 적절하고, 결과적 일관성을 위한 Awaitility 사용도 올바릅니다. 하지만 주석에서 "user1은 자신의 게시글이므로 제외"라고 명시했음에도 불구하고, 실제로 자기 게시글에 좋아요를 할 수 없는지 검증하는 별도 테스트가 없습니다.
다음과 같은 테스트 메서드 추가를 고려해보세요:
@Test @DisplayName("자신의 게시글에는 좋아요를 할 수 없다") void cannotLikeOwnPostTest() { // 예외가 발생하거나 좋아요가 추가되지 않음을 검증 assertThatThrownBy(() -> postLikeService.toggleLike(user1.getId(), post.getId())) .isInstanceOf(IllegalArgumentException.class); }
🧹 Nitpick comments (1)
src/test/java/hanium/modic/backend/domain/postLike/service/PostLikeServiceIntegrationTest.java (1)
26-188: 테스트 메서드명에 일관성을 위한 개선을 제안합니다.테스트 메서드명과 DisplayName이 일관성 있게 작성되었지만, 더 명확한 네이밍을 위해 개선을 제안드립니다.
메서드명을 더 명확하게 개선할 수 있습니다:
-void sameUserConcurrentToggleLikeTest() throws InterruptedException { +void sameUser_ConcurrentToggleLike_ShouldBeProcessedSequentially() throws InterruptedException { -void multipleUsersConcurrentLikeTest() throws InterruptedException { +void multipleUsers_ConcurrentLike_ShouldAllBeProcessedSuccessfully() throws InterruptedException { -void concurrentLikeToggleStatisticsConsistencyTest() throws InterruptedException { +void concurrentLikeToggle_ShouldMaintainStatisticsConsistency() throws InterruptedException {
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (1)
src/test/java/hanium/modic/backend/domain/postLike/service/PostLikeServiceIntegrationTest.java(1 hunks)
🧰 Additional context used
🧬 Code Graph Analysis (1)
src/test/java/hanium/modic/backend/domain/postLike/service/PostLikeServiceIntegrationTest.java (2)
src/test/java/hanium/modic/backend/domain/post/entityfactory/PostFactory.java (1)
PostFactory(10-36)src/test/java/hanium/modic/backend/domain/user/factory/UserFactory.java (1)
UserFactory(9-29)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
- GitHub Check: build
🔇 Additional comments (4)
src/test/java/hanium/modic/backend/domain/postLike/service/PostLikeServiceIntegrationTest.java (4)
1-25: 패키지 구조와 임포트가 적절합니다.테스트 클래스의 패키지 구조와 필요한 의존성 임포트가 올바르게 설정되어 있습니다. 동시성 테스트를 위한 ExecutorService, CountDownLatch, Awaitility 등의 도구들이 적절히 임포트되었습니다.
26-59: 테스트 셋업이 잘 구성되어 있습니다.BaseIntegrationTest를 상속받아 통합 테스트 환경을 구성하고, 필요한 리포지토리와 서비스를 주입받았습니다. @beforeeach에서 데이터 초기화 순서도 올바르게 설정되어 있습니다.
61-89: 동시성 테스트 로직이 정확하고 ExecutorService 종료 처리가 개선되었습니다.단일 사용자의 동시 좋아요 토글 테스트가 올바르게 구현되어 있습니다. 홀수 개의 요청(5번)으로 최종 상태를 예측 가능하게 만들었고, ExecutorService 종료 처리도 이전 리뷰 피드백을 반영하여 추가되었습니다.
133-188: 복잡한 동시성 시나리오 테스트가 우수합니다.사전 설정 후 동시 토글 작업을 통한 통계 일관성 검증이 잘 구현되어 있습니다. 실제 데이터와 통계 데이터의 일치성을 확인하는 로직이 적절하며, ExecutorService 종료 처리도 포함되어 있습니다. 최종 상태 검증 로직도 명확합니다.
개요
close #16, #113
작업사항
Summary by CodeRabbit
Summary by CodeRabbit
신규 기능
버그 수정
테스트
문서화