Conversation
Walkthrough피드 및 방 게시물(기록, 투표)의 좋아요 상태 변경 API가 도입되었습니다. 기존의 댓글 수 관리 인터페이스를 일반적인 카운트 관리 인터페이스로 확장하고, 도메인, 서비스, 정책, 예외, 컨트롤러, 테스트 전반에 걸쳐 좋아요 상태 변경 및 관련 검증 로직이 추가되었습니다. Changes
Sequence Diagram(s)sequenceDiagram
participant User
participant Controller
participant Service
participant Validator
participant PostHandler
participant CommandPort
participant QueryPort
User->>Controller: 좋아요 상태 변경 요청(POST)
Controller->>Service: changeLikeStatusPost(command)
Service->>PostHandler: findPost(type, postId)
PostHandler-->>Service: post
Service->>Validator: validateUserCanAccessPostLike(type, post, userId)
Service->>QueryPort: isLikedPostByUser(userId, postId)
QueryPort-->>Service: alreadyLiked
alt like 요청
Service->>Validator: validateUserCanLike(alreadyLiked)
Service->>CommandPort: save(userId, postId, postType)
else unlike 요청
Service->>Validator: validateUserCanUnLike(alreadyLiked)
Service->>CommandPort: delete(userId, postId)
end
Service->>post: updateLikeCount(like)
Service->>PostHandler: updatePost(type, post)
Service-->>Controller: PostIsLikeResult
Controller-->>User: 응답 반환
Estimated code review effort🎯 4 (Complex) | ⏱️ ~35 minutes Assessment against linked issues
Assessment against linked issues: Out-of-scope changes
Suggested labels
Suggested reviewers
Poem
Note ⚡️ Unit Test Generation is now available in beta!Learn more here, or try it out under "Finishing Touches" below. ✨ Finishing Touches
🧪 Generate unit tests
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. 🪧 TipsChatThere are 3 ways to chat with CodeRabbit:
SupportNeed help? Create a ticket on our support page for assistance with any issues or questions. CodeRabbit Commands (Invoked using PR comments)
Other keywords and placeholders
CodeRabbit Configuration File (
|
There was a problem hiding this comment.
Actionable comments posted: 2
🧹 Nitpick comments (11)
src/main/java/konkuk/thip/common/post/CountUpdatable.java (2)
3-3: TODO 주석을 해결해야 합니다.패키지 구조 충돌 문제를 해결하기 위한 리팩토링 작업이 필요합니다. 이후 PR에서 패키지 구조를 정리할 계획을 수립하는 것을 권장합니다.
이 TODO 항목을 추적할 수 있도록 별도 이슈를 생성하시겠습니까?
4-7: 인터페이스 통합이 잘 이루어졌으나 메서드 명명 일관성을 개선할 수 있습니다.댓글과 좋아요 카운트 관리를 통합한 설계가 좋습니다. 다만
increaseCommentCount/decreaseCommentCount와updateLikeCount의 명명 패턴이 다릅니다.더 일관된 명명을 위해 다음 중 하나를 고려해보세요:
// Option 1: update 패턴으로 통일 -void increaseCommentCount(); -void decreaseCommentCount(); +void updateCommentCount(boolean increase); void updateLikeCount(boolean like); // Option 2: increase/decrease 패턴으로 통일 void increaseCommentCount(); void decreaseCommentCount(); -void updateLikeCount(boolean like); +void increaseLikeCount(); +void decreaseLikeCount();src/main/java/konkuk/thip/post/application/port/in/dto/PostIsLikeResult.java (1)
8-10: 불필요한 정적 팩토리 메서드를 제거하세요.레코드는 이미 동일한 시그니처의 생성자를 제공하므로 정적 팩토리 메서드
of가 불필요합니다. 레코드의 기본 생성자를 직접 사용하는 것이 더 간결합니다.- public static PostIsLikeResult of(Long postId, boolean isLiked) { - return new PostIsLikeResult(postId, isLiked); - }src/main/java/konkuk/thip/room/domain/RoomPostType.java (1)
21-28: 구현이 올바르지만 성능 최적화 고려enum 값이 적을 때는 스트림 대신 직접 비교가 더 효율적일 수 있습니다.
성능 최적화를 위해 다음과 같이 변경할 수 있습니다:
public static RoomPostType from(String type) { - return Arrays.stream(RoomPostType.values()) - .filter(p -> p.getType().equalsIgnoreCase(type)) - .findFirst() - .orElseThrow(() -> - new InvalidStateException(ROOM_POST_TYPE_NOT_MATCH) - ); + for (RoomPostType roomPostType : values()) { + if (roomPostType.getType().equalsIgnoreCase(type)) { + return roomPostType; + } + } + throw new InvalidStateException(ROOM_POST_TYPE_NOT_MATCH); }src/main/java/konkuk/thip/config/PostAccessPolicyConfig.java (1)
35-42: 메서드명 개선 제안
roomPostAccessPolicyMap메서드명이 Room 게시물만 다루는 것처럼 보이지만 실제로는 FEED도 포함하고 있습니다.더 명확한 네이밍을 제안합니다:
@Bean -public Map<PostType, PostLikeAccessPolicy> roomPostAccessPolicyMap() { +public Map<PostType, PostLikeAccessPolicy> postLikeAccessPolicyMap() {src/main/java/konkuk/thip/feed/adapter/in/web/request/FeedIsLikeRequest.java (1)
10-14: 필드명 개선을 고려해보세요.
Boolean type필드명이 다소 모호합니다.isLike,like, 또는likeStatus같은 더 명확한 이름을 고려해보세요.public record FeedIsLikeRequest( - @Schema(description = "좋아요 여부 type (true -> 좋아요, false -> 좋아요 취소)", example = "true") + @Schema(description = "좋아요 여부 (true -> 좋아요, false -> 좋아요 취소)", example = "true") @NotNull(message = "좋아요 여부는 필수입니다.") - Boolean type + Boolean isLike ) {src/main/java/konkuk/thip/room/adapter/in/web/request/RoomPostIsLikeRequest.java (1)
19-21: toCommand 메서드의 매개변수명이 혼동될 수 있습니다.
toCommand(Long userId, Long feedId)메서드에서 두 번째 매개변수명이feedId인데, 실제로는 방 게시물의 postId를 받습니다. 매개변수명을postId로 변경하는 것이 더 명확할 것 같습니다.- public PostIsLikeCommand toCommand(Long userId, Long feedId) { - return new PostIsLikeCommand(userId, feedId, RoomPostType.from(roomPostType).toPostType(), type); + public PostIsLikeCommand toCommand(Long userId, Long postId) { + return new PostIsLikeCommand(userId, postId, RoomPostType.from(roomPostType).toPostType(), type); }src/main/java/konkuk/thip/feed/domain/Feed.java (1)
117-121: validateLike 메서드의 중복 코드를 고려해보세요.
validateLike와validateCreateComment메서드가 동일한 로직을 가지고 있습니다. 공통 검증 로직을 추출하여 중복을 제거하는 것을 고려해보세요.+ private void validatePrivateAccessPermission(Long userId, String action) { + if (!this.isPublic && !this.creatorId.equals(userId)) { + throw new InvalidStateException(FEED_ACCESS_FORBIDDEN, + new IllegalArgumentException("비공개 글은 작성자만 " + action + " 할 수 있습니다.")); + } + } + public void validateCreateComment(Long userId){ - if (!this.isPublic && !this.creatorId.equals(userId)) { - throw new InvalidStateException(FEED_ACCESS_FORBIDDEN, new IllegalArgumentException("비공개 글은 작성자만 댓글을 쓸 수 있습니다.")); - } + validatePrivateAccessPermission(userId, "댓글을 쓸"); } public void validateLike(Long userId){ - if (!this.isPublic && !this.creatorId.equals(userId)) { - throw new InvalidStateException(FEED_ACCESS_FORBIDDEN, new IllegalArgumentException("비공개 글은 작성자만 좋아요 할 수 있습니다.")); - } + validatePrivateAccessPermission(userId, "좋아요"); }src/test/java/konkuk/thip/feed/adapter/in/web/FeedChangeLikeStatusAPITest.java (1)
115-116: 테스트에서 도메인 로직을 우회하고 있습니다.
feed.updateLikeCount(1)을 직접 호출하는 것은 도메인 검증 로직을 우회합니다. 실제 서비스를 통해 좋아요를 추가하거나, 테스트 헬퍼 메서드를 만들어 사용하는 것이 더 안전합니다.- feed.updateLikeCount(1); // 좋아요 1개로 세팅 - feedJpaRepository.save(feed); + // 좋아요 카운트는 PostLike 저장 시 자동으로 반영되도록 하거나 + // 별도의 테스트 헬퍼 메서드 사용 + feed = feedJpaRepository.findById(feed.getPostId()).orElseThrow(); + feed.setLikeCount(1); // setter가 있다면 + feedJpaRepository.save(feed);src/test/java/konkuk/thip/room/adapter/in/web/RoomPostChangeLikeStatusAPITest.java (1)
138-139: 테스트에서 도메인 로직을 우회하고 있습니다.Feed 테스트와 동일하게
updateLikeCount(1)을 직접 호출하는 부분이 있습니다. 일관성을 위해 동일한 방식으로 개선하시기 바랍니다.Also applies to: 220-221
src/test/java/konkuk/thip/room/adapter/in/web/RoomPostChangeLikeStatusControllerTest.java (1)
44-44: 테스트 타입 표기가 부정확합니다.DisplayName에서 "[단위]"로 표기했지만, 이 테스트는
@SpringBootTest와MockMvc를 사용하는 통합 테스트입니다.-@DisplayName("[단위] 방 게시물(기록,투표) 좋아요 api controller 단위 테스트") +@DisplayName("[통합] 방 게시물(기록,투표) 좋아요 api controller 통합 테스트")
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (48)
src/main/java/konkuk/thip/comment/application/service/CommentCreateService.java(2 hunks)src/main/java/konkuk/thip/comment/application/service/CommentDeleteService.java(2 hunks)src/main/java/konkuk/thip/comment/application/service/CommentLikeService.java(2 hunks)src/main/java/konkuk/thip/comment/application/service/policy/CommentAccessPolicy.java(1 hunks)src/main/java/konkuk/thip/comment/application/service/policy/FeedCommentAccessPolicy.java(1 hunks)src/main/java/konkuk/thip/comment/application/service/policy/RoomPostCommentAccessPolicy.java(2 hunks)src/main/java/konkuk/thip/comment/application/service/validator/CommentAuthorizationValidator.java(2 hunks)src/main/java/konkuk/thip/common/exception/code/ErrorCode.java(2 hunks)src/main/java/konkuk/thip/common/post/CommentCountUpdatable.java(0 hunks)src/main/java/konkuk/thip/common/post/CountUpdatable.java(1 hunks)src/main/java/konkuk/thip/common/post/PostType.java(0 hunks)src/main/java/konkuk/thip/common/post/service/PostHandler.java(2 hunks)src/main/java/konkuk/thip/common/swagger/SwaggerResponseDescription.java(2 hunks)src/main/java/konkuk/thip/config/PostAccessPolicyConfig.java(3 hunks)src/main/java/konkuk/thip/feed/adapter/in/web/FeedCommandController.java(3 hunks)src/main/java/konkuk/thip/feed/adapter/in/web/request/FeedIsLikeRequest.java(1 hunks)src/main/java/konkuk/thip/feed/adapter/in/web/response/FeedIsLikeResponse.java(1 hunks)src/main/java/konkuk/thip/feed/adapter/out/jpa/FeedJpaEntity.java(1 hunks)src/main/java/konkuk/thip/feed/domain/Feed.java(4 hunks)src/main/java/konkuk/thip/post/adapter/out/persistence/PostLikeCommandPersistenceAdapter.java(1 hunks)src/main/java/konkuk/thip/post/adapter/out/persistence/PostLikeJpaRepository.java(2 hunks)src/main/java/konkuk/thip/post/adapter/out/persistence/PostLikeQueryPersistenceAdapter.java(1 hunks)src/main/java/konkuk/thip/post/application/port/in/PostLikeUseCase.java(1 hunks)src/main/java/konkuk/thip/post/application/port/in/dto/PostIsLikeCommand.java(1 hunks)src/main/java/konkuk/thip/post/application/port/in/dto/PostIsLikeResult.java(1 hunks)src/main/java/konkuk/thip/post/application/port/out/PostLikeCommandPort.java(1 hunks)src/main/java/konkuk/thip/post/application/port/out/PostLikeQueryPort.java(1 hunks)src/main/java/konkuk/thip/post/application/service/PostLikeService.java(1 hunks)src/main/java/konkuk/thip/post/application/service/policy/FeedLikeAccessPolicy.java(1 hunks)src/main/java/konkuk/thip/post/application/service/policy/PostLikeAccessPolicy.java(1 hunks)src/main/java/konkuk/thip/post/application/service/policy/RoomPostLikeAccessPolicy.java(1 hunks)src/main/java/konkuk/thip/post/application/service/validator/PostLikeAuthorizationValidator.java(1 hunks)src/main/java/konkuk/thip/record/adapter/out/jpa/RecordJpaEntity.java(2 hunks)src/main/java/konkuk/thip/record/domain/Record.java(3 hunks)src/main/java/konkuk/thip/room/adapter/in/web/RoomCommandController.java(3 hunks)src/main/java/konkuk/thip/room/adapter/in/web/request/RoomPostIsLikeRequest.java(1 hunks)src/main/java/konkuk/thip/room/adapter/in/web/response/RoomPostIsLikeResponse.java(1 hunks)src/main/java/konkuk/thip/room/domain/RoomPost.java(1 hunks)src/main/java/konkuk/thip/room/domain/RoomPostType.java(1 hunks)src/main/java/konkuk/thip/vote/adapter/out/jpa/VoteJpaEntity.java(2 hunks)src/main/java/konkuk/thip/vote/domain/Vote.java(3 hunks)src/test/java/konkuk/thip/common/util/TestEntityFactory.java(2 hunks)src/test/java/konkuk/thip/feed/adapter/in/web/FeedChangeLikeStatusAPITest.java(1 hunks)src/test/java/konkuk/thip/feed/domain/FeedTest.java(2 hunks)src/test/java/konkuk/thip/record/domain/RecordTest.java(2 hunks)src/test/java/konkuk/thip/room/adapter/in/web/RoomPostChangeLikeStatusAPITest.java(1 hunks)src/test/java/konkuk/thip/room/adapter/in/web/RoomPostChangeLikeStatusControllerTest.java(1 hunks)src/test/java/konkuk/thip/vote/domain/VoteTest.java(2 hunks)
💤 Files with no reviewable changes (2)
- src/main/java/konkuk/thip/common/post/PostType.java
- src/main/java/konkuk/thip/common/post/CommentCountUpdatable.java
🧰 Additional context used
🧠 Learnings (7)
📓 Common learnings
Learnt from: seongjunnoh
PR: THIP-TextHip/THIP-Server#101
File: src/main/java/konkuk/thip/comment/application/service/CommentCreateService.java:36-39
Timestamp: 2025-07-26T06:09:00.850Z
Learning: THIP 프로젝트에서 Record와 Vote는 Room에 속하지만 Feed는 Room에 속하지 않는 구조이며, 댓글 작성 시 Record/Vote에 대해서만 사용자가 해당 Room의 참가자인지 검증이 필요하다.
📚 Learning: commentcontrollertest는 댓글 생성 api의 검증 로직과 예외 상황만을 테스트하는 단위 테스트이며, 성공 케이스는 별도의 통합 테스트(commentcreateapi...
Learnt from: hd0rable
PR: THIP-TextHip/THIP-Server#101
File: src/test/java/konkuk/thip/comment/adapter/in/web/CommentControllerTest.java:118-265
Timestamp: 2025-07-23T17:41:55.507Z
Learning: CommentControllerTest는 댓글 생성 API의 검증 로직과 예외 상황만을 테스트하는 단위 테스트이며, 성공 케이스는 별도의 통합 테스트(CommentCreateAPITest)에서 다룬다.
Applied to files:
src/main/java/konkuk/thip/comment/application/service/CommentCreateService.javasrc/main/java/konkuk/thip/comment/application/service/CommentLikeService.javasrc/main/java/konkuk/thip/comment/application/service/CommentDeleteService.javasrc/test/java/konkuk/thip/room/adapter/in/web/RoomPostChangeLikeStatusControllerTest.javasrc/test/java/konkuk/thip/record/domain/RecordTest.javasrc/test/java/konkuk/thip/vote/domain/VoteTest.javasrc/test/java/konkuk/thip/feed/domain/FeedTest.javasrc/test/java/konkuk/thip/room/adapter/in/web/RoomPostChangeLikeStatusAPITest.javasrc/test/java/konkuk/thip/feed/adapter/in/web/FeedChangeLikeStatusAPITest.java
📚 Learning: thip 프로젝트에서는 cqrs port 분리 시 다음 컨벤션을 따름: commandport에는 findbyxxx를 통해 도메인 엔티티를 찾아오는 메서드를 추가하고, querypo...
Learnt from: seongjunnoh
PR: THIP-TextHip/THIP-Server#43
File: src/main/java/konkuk/thip/book/application/port/out/BookCommandPort.java:0-0
Timestamp: 2025-07-03T03:05:05.031Z
Learning: THIP 프로젝트에서는 CQRS Port 분리 시 다음 컨벤션을 따름: CommandPort에는 findByXXX를 통해 도메인 엔티티를 찾아오는 메서드를 추가하고, QueryPort에는 조회 API의 response에 해당하는 데이터들을 DB로부터 조회하는 메서드를 추가함.
Applied to files:
src/main/java/konkuk/thip/comment/application/service/CommentLikeService.javasrc/main/java/konkuk/thip/post/application/port/in/PostLikeUseCase.javasrc/main/java/konkuk/thip/post/application/port/in/dto/PostIsLikeCommand.javasrc/main/java/konkuk/thip/common/post/service/PostHandler.javasrc/main/java/konkuk/thip/post/application/port/out/PostLikeCommandPort.javasrc/main/java/konkuk/thip/post/application/service/PostLikeService.javasrc/main/java/konkuk/thip/post/application/port/out/PostLikeQueryPort.java
📚 Learning: thip 프로젝트에서 record와 vote는 room에 속하지만 feed는 room에 속하지 않는 구조이며, 댓글 작성 시 record/vote에 대해서만 사용자가 해당 room...
Learnt from: seongjunnoh
PR: THIP-TextHip/THIP-Server#101
File: src/main/java/konkuk/thip/comment/application/service/CommentCreateService.java:36-39
Timestamp: 2025-07-26T06:09:00.850Z
Learning: THIP 프로젝트에서 Record와 Vote는 Room에 속하지만 Feed는 Room에 속하지 않는 구조이며, 댓글 작성 시 Record/Vote에 대해서만 사용자가 해당 Room의 참가자인지 검증이 필요하다.
Applied to files:
src/main/java/konkuk/thip/comment/application/service/policy/FeedCommentAccessPolicy.javasrc/main/java/konkuk/thip/room/adapter/in/web/request/RoomPostIsLikeRequest.javasrc/main/java/konkuk/thip/comment/application/service/policy/RoomPostCommentAccessPolicy.javasrc/main/java/konkuk/thip/record/domain/Record.javasrc/main/java/konkuk/thip/room/domain/RoomPostType.javasrc/test/java/konkuk/thip/room/adapter/in/web/RoomPostChangeLikeStatusControllerTest.javasrc/main/java/konkuk/thip/config/PostAccessPolicyConfig.javasrc/main/java/konkuk/thip/common/swagger/SwaggerResponseDescription.javasrc/main/java/konkuk/thip/room/adapter/in/web/RoomCommandController.javasrc/main/java/konkuk/thip/common/exception/code/ErrorCode.javasrc/main/java/konkuk/thip/room/domain/RoomPost.javasrc/main/java/konkuk/thip/feed/domain/Feed.javasrc/test/java/konkuk/thip/room/adapter/in/web/RoomPostChangeLikeStatusAPITest.java
📚 Learning: thip 프로젝트에서는 "사용자가 방에 속하는지 검증" 로직을 roomparticipantpolicy 도메인 서비스로 캡슐화하여 재사용성을 높이고 비즈니스 로직의 중복을 방지하는 ...
Learnt from: seongjunnoh
PR: THIP-TextHip/THIP-Server#101
File: src/main/java/konkuk/thip/comment/application/service/CommentCreateService.java:36-39
Timestamp: 2025-07-26T06:09:00.850Z
Learning: THIP 프로젝트에서는 "사용자가 방에 속하는지 검증" 로직을 RoomParticipantPolicy 도메인 서비스로 캡슐화하여 재사용성을 높이고 비즈니스 로직의 중복을 방지하는 방식을 선호한다.
Applied to files:
src/main/java/konkuk/thip/comment/application/service/policy/RoomPostCommentAccessPolicy.javasrc/main/java/konkuk/thip/post/application/service/policy/RoomPostLikeAccessPolicy.java
📚 Learning: room 도메인에서 startdate는 현재 날짜 이후여야 하는 도메인 규칙이 있어서, 테스트에서 만료된 상태를 시뮬레이션하려면 reflection을 사용해야 한다....
Learnt from: hd0rable
PR: THIP-TextHip/THIP-Server#57
File: src/test/java/konkuk/thip/room/domain/RoomTest.java:0-0
Timestamp: 2025-07-08T16:30:33.771Z
Learning: Room 도메인에서 startDate는 현재 날짜 이후여야 하는 도메인 규칙이 있어서, 테스트에서 만료된 상태를 시뮬레이션하려면 reflection을 사용해야 한다.
Applied to files:
src/test/java/konkuk/thip/room/adapter/in/web/RoomPostChangeLikeStatusControllerTest.java
📚 Learning: vote와 voteitem 엔티티는 자주 함께 사용되므로, n+1 문제를 방지하기 위해 양방향 매핑과 fetch join을 고려하는 것이 좋습니다. 특히 기록장 조회 api 등에서...
Learnt from: buzz0331
PR: THIP-TextHip/THIP-Server#75
File: src/main/java/konkuk/thip/vote/adapter/out/persistence/VoteQueryRepositoryImpl.java:50-83
Timestamp: 2025-07-14T14:19:38.796Z
Learning: Vote와 VoteItem 엔티티는 자주 함께 사용되므로, N+1 문제를 방지하기 위해 양방향 매핑과 fetch join을 고려하는 것이 좋습니다. 특히 기록장 조회 API 등에서도 함께 사용될 가능성이 높습니다.
Applied to files:
src/main/java/konkuk/thip/vote/domain/Vote.java
🧬 Code Graph Analysis (4)
src/main/java/konkuk/thip/comment/application/service/policy/FeedCommentAccessPolicy.java (1)
src/main/java/konkuk/thip/comment/application/service/policy/RoomPostCommentAccessPolicy.java (1)
Component(9-21)
src/test/java/konkuk/thip/room/adapter/in/web/RoomPostChangeLikeStatusControllerTest.java (1)
src/test/java/konkuk/thip/common/util/TestEntityFactory.java (1)
TestEntityFactory(34-324)
src/test/java/konkuk/thip/room/adapter/in/web/RoomPostChangeLikeStatusAPITest.java (1)
src/test/java/konkuk/thip/common/util/TestEntityFactory.java (1)
TestEntityFactory(34-324)
src/test/java/konkuk/thip/feed/adapter/in/web/FeedChangeLikeStatusAPITest.java (1)
src/test/java/konkuk/thip/common/util/TestEntityFactory.java (1)
TestEntityFactory(34-324)
⏰ 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 (72)
src/main/java/konkuk/thip/comment/application/service/CommentLikeService.java (1)
12-12: 인터페이스 리팩토링이 적절히 적용되었습니다.
CommentCountUpdatable에서CountUpdatable로의 변경은 댓글과 좋아요 수를 통합적으로 관리할 수 있도록 하는 좋은 추상화입니다. 기존 로직은 그대로 유지되면서 확장성이 향상되었습니다.Also applies to: 35-35
src/main/java/konkuk/thip/comment/application/service/CommentCreateService.java (1)
9-9: 일관된 인터페이스 리팩토링 적용 확인.댓글 생성 서비스에서도
CountUpdatable인터페이스로의 변경이 올바르게 적용되었습니다. 전체 댓글 관련 서비스들에 걸쳐 일관성 있는 리팩토링이 이루어졌습니다.Also applies to: 37-37
src/main/java/konkuk/thip/comment/application/service/CommentDeleteService.java (1)
9-9: 리팩토링 일관성 유지 확인.댓글 삭제 서비스에서도 동일한 인터페이스 변경이 적용되어, 전체 댓글 관련 서비스들의 일관성이 유지되고 있습니다.
Also applies to: 31-31
src/main/java/konkuk/thip/feed/adapter/out/jpa/FeedJpaEntity.java (1)
59-62: 테스트용 메서드 추가가 적절합니다.
updateLikeCount메서드가 기존의updateCommentCount와 동일한 패턴으로 구현되었으며,@VisibleForTesting어노테이션도 적절히 사용되었습니다.src/main/java/konkuk/thip/post/application/port/out/PostLikeQueryPort.java (1)
9-9: CQRS 패턴에 맞는 쿼리 메서드 추가 확인.
isLikedPostByUser메서드가 QueryPort에 적절히 추가되었으며, 사용자의 게시물 좋아요 상태를 확인하는 기능을 제공합니다. CQRS 컨벤션을 올바르게 따르고 있습니다.src/main/java/konkuk/thip/vote/adapter/out/jpa/VoteJpaEntity.java (2)
3-3: LGTM! 테스트 지원을 위한 적절한 import 추가
@VisibleForTesting어노테이션 사용을 위한 import가 적절히 추가되었습니다.
47-50: LGTM! 테스트 목적의 메서드가 적절히 구현됨
@VisibleForTesting어노테이션을 통해 이 메서드가 테스트 목적임을 명확히 하고 있으며, 다른 JPA 엔티티들과 일관된 패턴을 따르고 있습니다.src/main/java/konkuk/thip/record/adapter/out/jpa/RecordJpaEntity.java (2)
3-3: LGTM! 테스트 지원을 위한 적절한 import 추가
@VisibleForTesting어노테이션 사용을 위한 import가 적절히 추가되었습니다.
47-50: LGTM! 테스트 목적의 메서드가 일관되게 구현됨다른 JPA 엔티티들과 동일한 패턴으로 테스트 목적의
updateLikeCount메서드가 구현되었습니다.@VisibleForTesting어노테이션을 통해 의도가 명확합니다.src/main/java/konkuk/thip/post/adapter/out/persistence/PostLikeQueryPersistenceAdapter.java (1)
20-23: LGTM! 적절한 좋아요 상태 조회 메서드 구현사용자가 특정 게시물에 좋아요를 눌렀는지 확인하는 메서드가 적절히 구현되었습니다. JPA 레포지토리에 위임하는 간단한 구조로, 인터페이스 계약을 정확히 따르고 있습니다.
src/test/java/konkuk/thip/common/util/TestEntityFactory.java (2)
12-12: LGTM! 테스트 팩토리 확장을 위한 적절한 import 추가PostLike 엔티티 생성을 위한 import가 적절히 추가되었습니다.
318-323: LGTM! 일관된 패턴의 테스트 팩토리 메서드 추가다른 팩토리 메서드들과 일관된 패턴으로
createPostLike메서드가 구현되었습니다. 사용자와 게시물 연관관계를 적절히 설정하여 좋아요 기능 테스트를 지원합니다.src/main/java/konkuk/thip/post/application/port/in/PostLikeUseCase.java (1)
1-8: LGTM! 헥사고날 아키텍처 패턴에 맞는 적절한 유스케이스 인터페이스좋아요 상태 변경을 위한 유스케이스 인터페이스가 적절히 정의되었습니다. 단일 책임 원칙을 따르고 있으며, Command-Result 패턴을 통해 관심사의 분리가 잘 되어 있습니다. THIP 프로젝트의 기존 포트 분리 컨벤션과도 일치합니다.
src/main/java/konkuk/thip/post/application/service/policy/PostLikeAccessPolicy.java (1)
5-7: 좋아요 접근 정책 인터페이스 설계가 적절합니다.인터페이스가 단일 책임 원칙을 잘 따르고 있으며,
CountUpdatable과Long userId매개변수 타입이 적절합니다. 클린 아키텍처의 정책 패턴을 잘 구현하고 있습니다.src/main/java/konkuk/thip/comment/application/service/policy/CommentAccessPolicy.java (2)
3-3: 인터페이스 통합이 적절하게 이루어졌습니다.
CommentCountUpdatable에서CountUpdatable로의 변경이 댓글과 좋아요 카운트 관리를 통합하는 아키텍처 개선과 일치합니다.
6-6: 매개변수 타입 변경이 통합된 인터페이스와 일관성을 유지합니다.
CountUpdatable매개변수 타입으로 변경하여 댓글과 좋아요 기능 모두에서 재사용 가능한 정책 인터페이스가 되었습니다.src/main/java/konkuk/thip/post/application/port/in/dto/PostIsLikeCommand.java (1)
5-16: Command DTO 설계가 우수합니다.Java record를 사용한 불변 객체 설계가 적절하며,
PostTypeenum을 통한 타입 안전성과boolean isLike를 통한 명확한 의도 표현이 좋습니다. CQRS 패턴의 Command 객체로서 적합합니다.src/main/java/konkuk/thip/post/application/port/out/PostLikeCommandPort.java (1)
5-8: CommandPort 인터페이스 설계가 적절합니다.CQRS 패턴을 잘 따르고 있으며,
save메서드에PostType매개변수를 포함하여 다양한 게시물 타입을 지원하고,delete메서드는 필요한 최소 정보만 요구하는 적절한 설계입니다.src/main/java/konkuk/thip/room/domain/RoomPost.java (1)
3-5: LGTM! 인터페이스 일반화가 잘 되었습니다.
CommentCountUpdatable에서CountUpdatable로의 변경은 댓글과 좋아요 카운트 관리를 통합하는 아키텍처 개선입니다. 이는 전체 코드베이스에서 일관되게 적용된 리팩토링의 일부로 적절합니다.src/main/java/konkuk/thip/comment/application/service/policy/FeedCommentAccessPolicy.java (2)
3-3: LGTM! 인터페이스 일반화에 맞춘 변경입니다.
CountUpdatable인터페이스 사용으로 통일한 변경이 적절합니다.
11-11: LGTM! 메서드 시그니처 변경이 일관됩니다.
CountUpdatable타입으로 변경하면서도 기존 로직은 그대로 유지된 점이 좋습니다.src/main/java/konkuk/thip/post/application/service/policy/RoomPostLikeAccessPolicy.java (1)
9-20: LGTM! 좋아요 접근 정책이 잘 구현되었습니다.기존 댓글 접근 정책과 일관된 패턴을 따르며,
RoomParticipantValidator를 재사용하여 코드 중복을 방지한 점이 좋습니다. 방 참여자만 게시물에 좋아요를 할 수 있도록 하는 접근 제어가 적절하게 구현되었습니다.src/main/java/konkuk/thip/comment/application/service/policy/RoomPostCommentAccessPolicy.java (2)
3-3: LGTM! 인터페이스 통일을 위한 적절한 변경입니다.
CountUpdatable인터페이스로 통일된 변경이 일관됩니다.
16-16: LGTM! 메서드 시그니처가 일관되게 변경되었습니다.기존 검증 로직을 유지하면서 새로운 인터페이스를 사용하도록 변경한 점이 적절합니다.
src/main/java/konkuk/thip/room/domain/RoomPostType.java (1)
30-32: PostType.from 구현 검증 완료
PostType.from(String)은 내부적으로equalsIgnoreCase로 모든PostType.values()를 순회하며 타입 문자열을 대소문자 구분 없이 매칭하고, 일치하는 값이 없을 경우InvalidStateException(POST_TYPE_NOT_MATCH)를 던지도록 구현되어 있습니다.
따라서RoomPostType.toPostType()에서 호출하는PostType.from(this.type)는 의도한 대로 올바르게 작동합니다.src/test/java/konkuk/thip/vote/domain/VoteTest.java (4)
8-8: 새로운 에러 코드 import 추가 확인POST_LIKE_COUNT_UNDERFLOW 에러 코드가 올바르게 추가되었습니다.
128-138: 좋아요 증가 테스트 구현 양호like가 true일 때 likeCount가 올바르게 증가하는 것을 검증하는 테스트가 잘 구현되었습니다.
140-155: 좋아요 감소 테스트 구현 양호like가 false일 때 likeCount가 올바르게 감소하는 것을 검증하는 테스트가 잘 구현되었습니다. 셋업 과정도 명확합니다.
157-168: 언더플로우 예외 테스트 구현 양호좋아요 수가 0 이하로 내려갈 때 적절한 예외가 발생하는지 검증하는 테스트가 올바르게 구현되었습니다.
src/main/java/konkuk/thip/feed/adapter/in/web/response/FeedIsLikeResponse.java (1)
5-12: 깔끔한 응답 DTO 구현Record를 사용한 응답 DTO가 잘 구현되었습니다. 정적 팩토리 메서드를 통한 변환 로직도 명확합니다.
src/main/java/konkuk/thip/config/PostAccessPolicyConfig.java (1)
23-24: 새로운 의존성 주입 확인좋아요 접근 정책을 위한 새로운 의존성들이 올바르게 추가되었습니다.
src/main/java/konkuk/thip/comment/application/service/validator/CommentAuthorizationValidator.java (1)
5-5: 리팩토링이 올바르게 적용되었습니다.
CommentCountUpdatable에서CountUpdatable로의 인터페이스 일반화와 필드명 변경이 일관성 있게 적용되어 좋습니다. 댓글과 좋아요 수 관리를 통합하는 구조 변경에 맞춰 적절히 수정되었습니다.Also applies to: 18-18, 20-20, 25-25
src/main/java/konkuk/thip/post/adapter/out/persistence/PostLikeJpaRepository.java (2)
19-22: 존재 여부 확인 쿼리가 올바르게 구현되었습니다.
CASE WHEN COUNT > 0패턴을 사용한 존재 여부 확인 로직이 적절하며, 파라미터 바인딩도 올바르게 처리되어 있습니다.
24-26: 삭제 쿼리에 @Modifying 어노테이션이 적절히 적용되었습니다.JPA에서 DELETE 쿼리 실행을 위해
@Modifying어노테이션을 올바르게 사용했습니다. 파라미터 바인딩도 정확합니다.src/main/java/konkuk/thip/room/adapter/in/web/response/RoomPostIsLikeResponse.java (1)
5-11: 깔끔하게 구현된 응답 DTO입니다.Record 구조가 적절하고, static factory method를 통한 변환 로직도 명확합니다. 필드명도 의미가 명확해 좋습니다.
src/test/java/konkuk/thip/record/domain/RecordTest.java (3)
128-138: 좋아요 증가 테스트가 적절히 작성되었습니다.
like=true일 때likeCount가 단계별로 증가하는 것을 잘 검증하고 있습니다. 기존 댓글 수 테스트와 일관된 패턴을 따라 좋습니다.
140-155: 좋아요 감소 테스트의 셋업이 명확합니다.감소 테스트를 위해 먼저 좋아요 수를 증가시키는 셋업 과정이 명확하고, 단계별 감소 검증도 적절합니다.
157-168: 언더플로우 예외 처리 테스트가 정확합니다.
likeCount가 0일 때 감소 시도 시 올바른 예외와 에러 코드를 검증하고 있습니다. 경계값 테스트가 잘 구현되었습니다.src/main/java/konkuk/thip/feed/adapter/in/web/request/FeedIsLikeRequest.java (1)
15-17: 변환 메서드가 깔끔하게 구현되었습니다.
toCommand메서드의 파라미터와 변환 로직이 명확하며, static import 사용도 적절합니다.src/main/java/konkuk/thip/record/domain/Record.java (2)
88-96: 좋아요 수 업데이트 로직이 올바르게 구현되었습니다.기존 댓글 수 관리 패턴을 일관성 있게 따르고 있으며, underflow 방지를 위한 검증 로직도 적절히 구현되어 있습니다.
104-108: 언더플로우 검증 로직이 적절합니다.기존 댓글 수 언더플로우 검증과 동일한 패턴으로 구현되어 일관성이 유지되고 있습니다.
src/main/java/konkuk/thip/vote/domain/Vote.java (2)
82-90: 좋아요 수 업데이트 로직이 Record와 동일한 패턴으로 구현되어 일관성이 유지됩니다.boolean 파라미터를 통한 증가/감소 로직과 언더플로우 검증이 적절히 구현되어 있습니다.
98-102: 언더플로우 검증 로직이 다른 도메인 엔티티와 일관성을 유지합니다.POST_LIKE_COUNT_UNDERFLOW 에러 코드 사용이 적절합니다.
src/main/java/konkuk/thip/feed/adapter/in/web/FeedCommandController.java (1)
81-92: 피드 좋아요 API 엔드포인트가 올바르게 구현되었습니다.기존 컨트롤러 패턴을 일관성 있게 따르고 있으며, 유효성 검증, 의존성 주입, Swagger 문서화가 적절히 적용되어 있습니다.
src/main/java/konkuk/thip/common/swagger/SwaggerResponseDescription.java (2)
107-116: 방 게시물 좋아요 상태 변경에 대한 포괄적인 에러 시나리오가 문서화되었습니다.사용자/게시물 존재 여부, 좋아요 상태 충돌, 언더플로우, 접근 권한, 게시물 타입 불일치 등 모든 주요 에러 케이스가 적절히 포함되어 있습니다.
187-194: 피드 좋아요 상태 변경에 대한 에러 문서화가 적절합니다.방 게시물과 유사한 에러 시나리오를 다루면서도 피드 특성에 맞는 에러 코드들이 포함되어 있습니다.
src/main/java/konkuk/thip/common/post/service/PostHandler.java (2)
22-28: PostHandler의 findPost 메서드가 CountUpdatable 인터페이스로 올바르게 업데이트되었습니다.기존 로직을 유지하면서 새로운 인터페이스를 지원하도록 적절히 변경되었습니다.
30-36: updatePost 메서드의 인터페이스 변경이 적절합니다.CountUpdatable 인터페이스를 사용하면서도 타입 캐스팅을 통해 기존 기능을 안전하게 유지하고 있습니다.
src/main/java/konkuk/thip/room/adapter/in/web/request/RoomPostIsLikeRequest.java (1)
9-18: DTO 설계가 적절하게 구현되었습니다.요청 DTO의 필드 검증과 Swagger 문서화가 잘 구현되어 있습니다. Boolean과 String 타입 사용도 적절합니다.
src/main/java/konkuk/thip/feed/domain/Feed.java (3)
21-21: 인터페이스 변경이 적절합니다.
CommentCountUpdatable에서CountUpdatable로 변경하여 댓글과 좋아요 카운트를 통합 관리하는 것이 좋은 설계입니다.
176-184: 좋아요 카운트 업데이트 로직이 잘 구현되었습니다.증감 로직과 언더플로우 검증이 적절히 구현되어 있습니다.
192-196: 언더플로우 검증이 적절합니다.좋아요 카운트가 0 이하로 내려가지 않도록 하는 검증 로직이 잘 구현되어 있습니다.
src/test/java/konkuk/thip/feed/domain/FeedTest.java (4)
113-113: 테스트 assertion이 개선되었습니다.메시지 검증이 더 정확하게 수정되어 테스트의 신뢰성이 향상되었습니다.
271-297: 좋아요 카운트 업데이트 테스트가 잘 구현되었습니다.증가/감소 로직에 대한 테스트가 적절히 작성되어 있으며, 여러 번 호출하는 케이스도 검증하고 있습니다.
299-310: 언더플로우 예외 처리 테스트가 적절합니다.좋아요 카운트가 0일 때 추가로 감소시키는 경우의 예외 처리가 올바르게 테스트되고 있습니다.
312-339: 좋아요 접근 권한 테스트가 체계적으로 구현되었습니다.공개 피드, 비공개 피드의 작성자/비작성자별 접근 권한이 모두 테스트되어 있어 완전한 테스트 커버리지를 제공합니다.
src/main/java/konkuk/thip/room/adapter/in/web/RoomCommandController.java (2)
10-10: 의존성 추가가 적절합니다.좋아요 기능을 위한
PostLikeUseCase의존성과 관련 DTO들의 import가 올바르게 추가되었습니다.Also applies to: 13-14, 37-37
93-104: 방 게시물 좋아요 API가 잘 구현되었습니다.Swagger 문서화, 유효성 검증, 매개변수 처리가 모두 적절히 구현되어 있습니다. 기존 엔드포인트들과 일관된 패턴을 따르고 있습니다.
src/main/java/konkuk/thip/post/adapter/out/persistence/PostLikeCommandPersistenceAdapter.java (4)
24-29: 의존성 주입이 적절합니다.필요한 모든 Repository와 Mapper가 올바르게 주입되어 있습니다.
32-40: 좋아요 저장 로직이 잘 구현되었습니다.사용자와 포스트의 존재를 검증한 후 저장하는 로직이 적절합니다. 예외 처리도 올바르게 구현되어 있습니다.
42-45: 삭제 메서드가 간결하게 구현되었습니다.좋아요 삭제 로직이 명확하고 간단하게 구현되어 있습니다.
48-57: PostType별 엔티티 조회 로직이 우수합니다.switch expression을 사용하여 각 포스트 타입별로 적절한 Repository에서 엔티티를 조회하고, 적절한 예외를 던지는 로직이 잘 구현되어 있습니다.
src/main/java/konkuk/thip/common/exception/code/ErrorCode.java (1)
94-94: 에러 코드 추가가 적절합니다.새로 추가된 에러 코드들이 기존 컨벤션을 잘 따르고 있으며, 메시지가 명확합니다.
Also applies to: 175-177
src/main/java/konkuk/thip/post/application/service/validator/PostLikeAuthorizationValidator.java (1)
14-43: PostLikeAuthorizationValidator 구현이 깔끔합니다.Strategy 패턴을 활용한 정책 위임 구조가 잘 설계되어 있으며, 중복 좋아요 검증 로직을 중앙화한 것이 좋은 선택입니다.
src/test/java/konkuk/thip/feed/adapter/in/web/FeedChangeLikeStatusAPITest.java (1)
66-152: 통합 테스트가 체계적으로 작성되었습니다.성공 케이스와 실패 케이스를 모두 다루고 있으며, API 응답과 데이터베이스 상태를 모두 검증하는 점이 좋습니다.
src/test/java/konkuk/thip/room/adapter/in/web/RoomPostChangeLikeStatusAPITest.java (1)
91-253: Room 게시물 타입별 테스트가 충실합니다.RECORD와 VOTE 타입 모두에 대해 동일한 시나리오를 테스트하고 있으며, Room 참가자 설정도 적절히 포함되어 있습니다.
src/test/java/konkuk/thip/room/adapter/in/web/RoomPostChangeLikeStatusControllerTest.java (6)
1-38: 임포트 구성이 적절합니다.통합 테스트에 필요한 모든 의존성이 올바르게 임포트되어 있고, 정적 임포트도 적절히 사용되었습니다.
47-66: 필드 선언이 적절합니다.테스트에 필요한 모든 리포지토리와 엔티티가 적절히 선언되어 있고, API 경로를 상수로 관리하는 것도 좋은 방식입니다.
68-79: 테스트 설정이 적절합니다.
TestEntityFactory를 사용한 엔티티 생성과 방 참여자 설정이 테스트 시나리오를 잘 지원합니다. user1은 호스트로, user2는 비참여자로 설정하여 양쪽 케이스를 모두 테스트할 수 있도록 구성되었습니다.
81-97: 헬퍼 메서드가 잘 구현되었습니다.요청 생성과 공통 검증 로직을 헬퍼 메서드로 분리하여 코드 중복을 줄이고 가독성을 향상시켰습니다.
99-104: 잘못된 RoomPostType 검증 테스트가 적절합니다.방 게시물은 RECORD 또는 VOTE 타입만 허용해야 하므로, FEED 타입으로 요청했을 때 적절한 에러 응답을 확인하는 테스트가 올바릅니다.
106-119: 비참여자 접근 제어 테스트가 올바릅니다.방에 참여하지 않은 사용자(user2)의 좋아요 시도를 403 Forbidden으로 처리하는 것이 적절하며, 도메인 학습 내용과 일치합니다. Record와 Vote는 Room에 속하므로 참여자만 접근할 수 있어야 합니다.
src/main/java/konkuk/thip/post/application/service/policy/FeedLikeAccessPolicy.java
Show resolved
Hide resolved
buzz0331
left a comment
There was a problem hiding this comment.
수고하셨습니다~!! 전략 패턴 + 헬퍼 서비스를 자유자재로 사용하시는 것이 역시 고수시네요 🥇
| void increaseCommentCount(); | ||
| void decreaseCommentCount(); | ||
| void updateLikeCount(boolean like); | ||
| Long getId(); |
There was a problem hiding this comment.
p3: getId는 어차피 공통으로 getter를 사용하는 것 같아서 굳이 없어도 될 것 같습니다!
There was a problem hiding this comment.
앗 이거 공통로직시에 updatable에서 뽑아올때 필요합니닷!!
There was a problem hiding this comment.
아하 흐음 이건 어쩔수 없이 열어둬야 될 것 같네여 🤔
| @Schema(description = "피드 좋아요 상태 변경 요청 DTO") | ||
| public record FeedIsLikeRequest( | ||
| @Schema(description = "좋아요 여부 type (true -> 좋아요, false -> 좋아요 취소)", example = "true") | ||
| @NotNull(message = "좋아요 여부는 필수입니다.") | ||
| Boolean type | ||
| ) { | ||
| public PostIsLikeCommand toCommand(Long userId, Long feedId) { | ||
| return new PostIsLikeCommand(userId, feedId, FEED ,type); | ||
| } | ||
| } No newline at end of file |
There was a problem hiding this comment.
오호 이렇게 상수로 넣어주었군요!! 굿굿 좋습니다!
| @Override | ||
| @Transactional | ||
| public PostIsLikeResult changeLikeStatusPost(PostIsLikeCommand command) { | ||
|
|
||
| // 1. 게시물 타입에 맞게 검증 및 조회 | ||
| CountUpdatable post = postHandler.findPost(command.postType(), command.postId()); | ||
| // 1-1. 게시글 타입에 따른 게시물 좋아요 권한 검증 | ||
| postLikeAuthorizationValidator.validateUserCanAccessPostLike(command.postType(), post, command.userId()); | ||
|
|
||
| // 2. 유저가 해당 게시물에 대해 좋아요 했는지 조회 | ||
| boolean alreadyLiked = postLikeQueryPort.isLikedPostByUser(command.userId(), command.postId()); | ||
|
|
||
| // 3. 좋아요 상태변경 | ||
| //TODO 게시물의 좋아요 수 증가/감소 동시성 제어 로직 추가해야됨 | ||
| if (command.isLike()) { | ||
| postLikeAuthorizationValidator.validateUserCanLike(alreadyLiked); // 좋아요 가능 여부 검증 | ||
| postLikeCommandPort.save(command.userId(), command.postId(),command.postType()); | ||
| } else { | ||
| postLikeAuthorizationValidator.validateUserCanUnLike(alreadyLiked); // 좋아요 취소 가능 여부 검증 | ||
| postLikeCommandPort.delete(command.userId(), command.postId()); | ||
| } | ||
|
|
||
| // 4. 게시물 좋아요 수 업데이트 | ||
| post.updateLikeCount(command.isLike()); | ||
| postHandler.updatePost(command.postType(), post); |
| @Override | ||
| public void updateLikeCount(boolean like) { | ||
| if (like) { | ||
| likeCount++; | ||
| } else { | ||
| checkLikeCountNotUnderflow(); | ||
| likeCount--; | ||
| } | ||
| } | ||
|
|
There was a problem hiding this comment.
pr을 확인해보니 Feed, Record, Vote 모두 여기서 중복 로직을 사용하는 것 같아서 의문이 들었다고 하셔서 리팩토링에 대해 고민을 해봤습니다. 마침 저희가 읽었던 책의 내용을 적용해볼 수 있는 기회라고 생각되어, 조심스럽게 제안을 드려봅니다.!
책에서 언급되었던 것처럼, 여러 도메인에서 공통적으로 사용되는 도메인 로직이 있는 경우에 도메인 객체에서 도메인 서비스를 파라미터로 주입받아 사용하는 방식을 생각해보았습니다. 물론 현재 저희가 사용중인 PostHandler는 헬퍼 서비스이긴 하지만 내부적으로 외부 port를 호출하지 않는 메서드라면 결국 도메인 서비스로 간주해도 무방하다고 생각했습니다.
제가 생각한 리팩토링 예시 코드입니다! 실제로 구현하고 postman으로 테스트까지 완료했습니다!
public interface CountUpdatable {
void updateLikeCount(PostHandler postHandler, boolean isLike);
}
public class PostHandler {
public int updatePostLikeCount(boolean isLike, int likeCount) {
if (isLike) {
return ++likeCount;
} else {
checkLikeCountNotUnderflow(likeCount);
return --likeCount;
}
}
private void checkLikeCountNotUnderflow(int likeCount) {
if (likeCount <= 0) {
throw new InvalidStateException(POST_LIKE_COUNT_UNDERFLOW);
}
}
}
public class Feed {
@Override
public void updateLikeCount(PostHandler postHandler, boolean isLike) {
likeCount = postHandler.updatePostLikeCount(isLike, likeCount);
}
}
public class PostLikeService {
// 4. 게시물 좋아요 수 업데이트
post.updateLikeCount(postHandler, command.isLike());
postHandler.updatePost(command.postType(), post);
}물론, 도메인에서 헬퍼 서비스를 호출하는 구조에 대해 의존성 역전이 발생할 수 있다는 우려도 있을 수 있지만, 말씀드린 것처럼 포트를 호출하고 있지는 않으므로 큰 문제는 없다고 판단했습니다. 혹시나 의존성 역전이 우려되신다면 PostLikeHandler와 같이 도메인 계층 내에서 책임을 갖는 별도 클래스를 따로 정의하는 것도 괜찮다고 생각합니다. 이렇게 도메인 계층이 하나 생긴다면 CommentCount를 업데이트 하는 쪽도 마찬가지로 리팩토링이 가능하다고 봅니다..!
There was a problem hiding this comment.
오호 좋은 방법인것 같습니다 다만 제가 처음 구현했을때 PostHandler의 의도는 게시물을 타입별로 조회,업데이트할때 사용하는 헬퍼서비스라고 생각하고 구현했기때문에 게시물의 공통 인터페이스도 CountUpdatable로 변경되었으니 PostCountHandler를 따로 정의해서 사용하는 것이 더 나을것같네요!!
There was a problem hiding this comment.
엇 그렇다면 PostCountHandler는 정말 어떤 컴포넌트도 의존하지 않게 될 것 같아서 완전한 도메인 서비스가 될 것 같네요! 그렇다면, PostDomainService 또는 PostCountService라고 하고 도메인 계층에 넣는 것도 좋을 것 같아요~
There was a problem hiding this comment.
Feed, Vote, Record 가 공통으로 담당하는 도메인 로직을 도메인 서비스를 정의하여 얘한테 책임을 미루고, 이 도메인 서비스를 의존하는 의견 너무 좋습니다!!
| public interface PostLikeUseCase { | ||
| PostIsLikeResult changeLikeStatusPost(PostIsLikeCommand PostIsLikeCommand); | ||
| } No newline at end of file |
| boolean alreadyLiked = postLikeQueryPort.isLikedPostByUser(command.userId(), command.postId()); | ||
|
|
||
| // 3. 좋아요 상태변경 | ||
| //TODO 게시물의 좋아요 수 증가/감소 동시성 제어 로직 추가해야됨 |
| @Bean | ||
| public Map<PostType, PostLikeAccessPolicy> postLikeAccessPolicyMap() { | ||
| return Map.of( | ||
| PostType.FEED, feedLikePolicy, | ||
| PostType.RECORD, roomPostLikePolicy, | ||
| PostType.VOTE, roomPostLikePolicy | ||
| ); | ||
| } | ||
| } No newline at end of file |
| // 댓글 작성 권한 검증 | ||
| public void validateCreateComment(Long userId){ | ||
| validatePrivateAccessPermission(userId, "댓글을 쓸 수"); | ||
| } | ||
|
|
||
| // 좋아요 권한 검증 | ||
| public void validateLike(Long userId){ | ||
| validatePrivateAccessPermission(userId, "좋아요 할 수"); | ||
| } |
There was a problem hiding this comment.
ㅋㅋㅋㅋㅋㅋ 신기한 방식의 에러 메시지 만드는 방식이네요
There was a problem hiding this comment.
사실 공통 검증인데 예외메세지를 다르게 만들고싶어서,,ㅎ 해봣습니닷
| @GetMapping("/rooms/{roomId}/playing") | ||
| public BaseResponse<RoomPlayingDetailViewResponse> getPlayingRoomDetailView( | ||
| @UserId final Long userId, | ||
| @Parameter(hidden = true) @UserId final Long userId, |
| @Schema(description = "게시물 타입 (RECORD, VOTE)", example = "RECORD") | ||
| @NotBlank(message = "게시물 타입은 필수입니다.") | ||
| String roomPostType |
| @Getter | ||
| @RequiredArgsConstructor | ||
| public enum RoomPostType { | ||
|
|
||
| RECORD("RECORD"), | ||
| VOTE("VOTE"); |
| @SpringBootTest | ||
| @ActiveProfiles("test") | ||
| @AutoConfigureMockMvc(addFilters = false) | ||
| @Transactional | ||
| @DisplayName("[통합] 피드 좋아요 api 통합 테스트") | ||
| class FeedChangeLikeStatusAPITest { |
There was a problem hiding this comment.
Actionable comments posted: 0
♻️ Duplicate comments (2)
src/main/java/konkuk/thip/record/domain/Record.java (1)
89-92: 도메인 서비스 패턴 적용이 우수합니다.이전 리뷰에서 논의된 도메인 서비스 패턴이 잘 적용되었습니다.
PostCountService를 파라미터로 받아 좋아요 수 업데이트 로직을 위임하는 방식은 코드 중복을 방지하고 도메인 로직을 중앙화하는 좋은 접근입니다.src/main/java/konkuk/thip/post/domain/service/PostCountService.java (1)
8-9: 서비스 구조가 명확합니다.
PostCountService는 게시물 수 관리 책임을 명확하게 분리한 좋은 도메인 서비스입니다. 이전 리뷰에서 제안된 대로 공통 로직을 중앙화하여 Feed, Record, Vote 간의 코드 중복을 해결했습니다.
🧹 Nitpick comments (1)
src/main/java/konkuk/thip/post/domain/service/PostCountService.java (1)
11-19: 전위 증감 연산자 사용을 재고해보세요.현재
++likeCount,--likeCount를 사용하고 있는데, 매개변수는 값으로 전달되므로 원본에 영향을 주지 않습니다. 의도는 명확하지만 가독성을 위해 다음과 같이 변경하는 것이 좋겠습니다:public int updatePostLikeCount(boolean isLike, int likeCount) { if (isLike) { - return ++likeCount; + return likeCount + 1; } else { checkLikeCountNotUnderflow(likeCount); - return --likeCount; + return likeCount - 1; } }
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (9)
src/main/java/konkuk/thip/common/post/CountUpdatable.java(1 hunks)src/main/java/konkuk/thip/feed/domain/Feed.java(4 hunks)src/main/java/konkuk/thip/post/application/service/PostLikeService.java(1 hunks)src/main/java/konkuk/thip/post/domain/service/PostCountService.java(1 hunks)src/main/java/konkuk/thip/record/domain/Record.java(3 hunks)src/main/java/konkuk/thip/vote/domain/Vote.java(3 hunks)src/test/java/konkuk/thip/feed/domain/FeedTest.java(4 hunks)src/test/java/konkuk/thip/record/domain/RecordTest.java(2 hunks)src/test/java/konkuk/thip/vote/domain/VoteTest.java(2 hunks)
🚧 Files skipped from review as they are similar to previous changes (7)
- src/test/java/konkuk/thip/vote/domain/VoteTest.java
- src/main/java/konkuk/thip/common/post/CountUpdatable.java
- src/main/java/konkuk/thip/vote/domain/Vote.java
- src/test/java/konkuk/thip/record/domain/RecordTest.java
- src/test/java/konkuk/thip/feed/domain/FeedTest.java
- src/main/java/konkuk/thip/feed/domain/Feed.java
- src/main/java/konkuk/thip/post/application/service/PostLikeService.java
🧰 Additional context used
🧠 Learnings (3)
📓 Common learnings
Learnt from: seongjunnoh
PR: THIP-TextHip/THIP-Server#101
File: src/main/java/konkuk/thip/comment/application/service/CommentCreateService.java:36-39
Timestamp: 2025-07-26T06:09:00.850Z
Learning: THIP 프로젝트에서 Record와 Vote는 Room에 속하지만 Feed는 Room에 속하지 않는 구조이며, 댓글 작성 시 Record/Vote에 대해서만 사용자가 해당 Room의 참가자인지 검증이 필요하다.
📚 Learning: seongjunnoh는 코드 중복 문제에 대한 리팩토링 제안을 적극적으로 수용하고 함수형 인터페이스를 활용한 해결책을 선호한다....
Learnt from: seongjunnoh
PR: THIP-TextHip/THIP-Server#93
File: src/main/java/konkuk/thip/room/adapter/out/persistence/RoomQueryPersistenceAdapter.java:49-114
Timestamp: 2025-07-28T16:44:31.224Z
Learning: seongjunnoh는 코드 중복 문제에 대한 리팩토링 제안을 적극적으로 수용하고 함수형 인터페이스를 활용한 해결책을 선호한다.
Applied to files:
src/main/java/konkuk/thip/record/domain/Record.java
📚 Learning: thip 프로젝트에서 record와 vote는 room에 속하지만 feed는 room에 속하지 않는 구조이며, 댓글 작성 시 record/vote에 대해서만 사용자가 해당 room...
Learnt from: seongjunnoh
PR: THIP-TextHip/THIP-Server#101
File: src/main/java/konkuk/thip/comment/application/service/CommentCreateService.java:36-39
Timestamp: 2025-07-26T06:09:00.850Z
Learning: THIP 프로젝트에서 Record와 Vote는 Room에 속하지만 Feed는 Room에 속하지 않는 구조이며, 댓글 작성 시 Record/Vote에 대해서만 사용자가 해당 Room의 참가자인지 검증이 필요하다.
Applied to files:
src/main/java/konkuk/thip/record/domain/Record.java
⏰ 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 (2)
src/main/java/konkuk/thip/record/domain/Record.java (1)
5-6: 인터페이스 변경이 적절합니다.
CommentCountUpdatable에서CountUpdatable로 변경한 것은 좋아요와 댓글 수 관리를 통합하는 좋은 설계 결정입니다. 인터페이스명도 더 범용적이고 명확해졌습니다.Also applies to: 16-16
src/main/java/konkuk/thip/post/domain/service/PostCountService.java (1)
21-25: 언더플로우 검증 로직이 적절합니다.좋아요 수가 0 이하일 때 감소를 방지하는 검증 로직이 올바르게 구현되었습니다. 예외 타입과 에러 코드도 적절하며, private 메서드로 캡슐화된 것도 좋습니다.
|
|
||
| import static konkuk.thip.common.exception.code.ErrorCode.POST_LIKE_COUNT_UNDERFLOW; | ||
|
|
||
| @Service |
There was a problem hiding this comment.
오호 이것도 DomainService하나 만드는게 좋아보이네여 나중에 만들죠.!
#️⃣ 연관된 이슈
📝 작업 내용
CommentCountUpdatable의 이름을CountUpdatable으로 변경하여 게시물의 공통 인터페이스의 역할을 명확히했습니다.📸 스크린샷
💬 리뷰 요구사항
사용자가 게시물 좋아요 중복 좋아요 검증에 대해서 검증을 책임지는
PostLikeAuthorizationValidator이 검증하도록했는데, 해당 클래스가 게시물 좋아요에대한 권한 검증을 하는 정책 클래스이기도하고, 각 도메인에서 한다면 똑같은 중복 코드가 3개의 클래스에 생성되는 것같아 위와같이 구현했습니다. 좋은 의견있으면 부탁드립니다. (여기서 의문이 드는게 CountUpdatable의 같은 경우에도 단순히 숫자만 증가/감소 하는 역할을 하다보니 각 도메인에대해 중복되는 코드가 생성되는 감이있는데.. 후에 리펙해보도록하겠습니다)방 게시물의 경우 현재 방아이디를 받지않고 바로 게시글 조회한 후 좋아요 권한 검증을 진행하는데, 어차피 방 게시글에 대한 좋아요를 누르려면 사용자가 해당 방에 참여중인지 권한 검증도 진행하고, 해당 방이 만료되거나 삭제된 방이라면 방과 관련된 정보들이 삭제 될것이기때문에 이측에서 필터링이 될것같아 방을 따로 조회하지않았는데 이와관련해서 더 좋은 방안이 있다면 리뷰 부탁드립니다
📌 PR 진행 시 이러한 점들을 참고해 주세요
Summary by CodeRabbit
신규 기능
버그 수정
리팩터링
테스트
문서화