-
Notifications
You must be signed in to change notification settings - Fork 0
Feat/#42 학생회 게시글 좋아요 기능 구현 #44
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
Conversation
Walkthrough게시글 좋아요 기능을 추가합니다. 새로운 LikePost 엔티티와 LikePostRepository를 도입하고, 관련 DTO(GetLikedPostResponse, LikePostResponse, GetPostForUserResponse)를 정의하며, 서비스와 컨트롤러에 좋아요 토글, 좋아요한 게시글 조회 등의 기능을 구현합니다. Changes
Sequence Diagram(s)sequenceDiagram
participant User as 사용자
participant Controller as StudentCouncilPostForUserController
participant Service as StudentCouncilPostForUserService
participant Repository as LikePostRepository
participant DB as 데이터베이스
User->>Controller: POST /{postId}/like
activate Controller
Controller->>Service: toggleLikePost(userId, postId)
deactivate Controller
activate Service
Service->>Repository: existsByUserIdAndPost_Id(userId, postId)
activate Repository
Repository->>DB: 좋아요 존재 여부 조회
DB-->>Repository: 결과 반환
deactivate Repository
alt 좋아요 존재
Service->>Repository: deleteByUserIdAndPostId(userId, postId)
activate Repository
Repository->>DB: 좋아요 삭제
deactivate Repository
Service->>Service: toLikePostResponse(user, post, false)
else 좋아요 미존재
Service->>Repository: save(LikePost)
activate Repository
Repository->>DB: 좋아요 저장
DB-->>Repository: 저장 완료
deactivate Repository
Service->>Service: toLikePostResponse(user, post, true)
end
Service-->>Controller: LikePostResponse
deactivate Service
Controller-->>User: CommonResponse<LikePostResponse>
sequenceDiagram
participant User as 사용자
participant Controller as StudentCouncilPostForUserController
participant Service as StudentCouncilPostForUserService
participant Repository as LikePostRepository
participant PostRepository as StudentCouncilPostRepository
participant DB as 데이터베이스
User->>Controller: GET /likes?category=...&page=...
activate Controller
Controller->>Service: findLikedPosts(category, page, size, userId)
deactivate Controller
activate Service
Service->>Repository: findLikedPosts(userId, category, pageable)
activate Repository
Repository->>DB: 사용자의 좋아요한 게시글 조회
DB-->>Repository: LikePost 페이지 반환
deactivate Repository
Service->>Service: mapPostsWithLikes(posts, userId)
Service->>Repository: findLikedPostIds(userId, postIds)
activate Repository
Repository->>DB: 게시글 ID별 좋아요 상태 조회
DB-->>Repository: 좋아요한 게시글 ID 목록
deactivate Repository
Service->>Service: toGetLikedPostResponse 매핑
Service-->>Controller: Page<GetLikedPostResponse>
deactivate Service
Controller-->>User: CommonResponse<Page<GetLikedPostResponse>>
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~20 minutes Possibly related PRs
Suggested reviewers
Poem
🚥 Pre-merge checks | ✅ 2 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing touches
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 |
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: 4
🤖 Fix all issues with AI agents
In
@src/main/java/com/campus/campus/domain/councilpost/application/dto/response/LikePostResponse.java:
- Around line 6-13: In LikePostResponse, replace the @Schema(name=...) usages on
the fields userId, postId, and liked with @Schema(description=...) and update
the liked field's example to a boolean literal (e.g., example = "true" or
"false"); specifically edit the annotations on the fields userId, postId, and
liked in the LikePostResponse class so they use description for human-readable
text and set liked's example to a proper boolean value.
In
@src/main/java/com/campus/campus/domain/councilpost/application/service/StudentCouncilPostService.java:
- Around line 143-163: The toggleLikePost method in StudentCouncilPostService
has a race condition when two requests concurrently try to create a LikePost;
add a unique DB constraint on LikePost (annotate the LikePost entity @Table with
@UniqueConstraint(columnNames = {"user_id","post_id"}) to enforce uniqueness)
and modify toggleLikePost to catch DataIntegrityViolationException (thrown by
likePostRepository.save) and treat it as "already liked" instead of failing
(i.e., on catch, set isLike = true and continue to return the LikePostResponse);
keep existing delete path unchanged and reference methods/classes:
toggleLikePost, LikePost entity, likePostRepository, studentCouncilPostMapper,
StudentCouncilPostService.
- Around line 148-149: In toggleLikePost in StudentCouncilPostService replace
the use of postRepository.findById(postId) with
postRepository.findByIdWithFullInfo(postId) and keep throwing
PostNotFoundException if empty so the method uses the same full-info loading
path as findById/update/delete; update any variable names or expectations around
the loaded StudentCouncilPost accordingly, and ensure any callers still get the
same behavior (no changes to exception type).
In
@src/main/java/com/campus/campus/domain/councilpost/domain/entity/LikePost.java:
- Around line 23-41: Add a unique constraint on the (user_id, post_id) pair to
the LikePost entity to prevent duplicate likes under concurrent requests: modify
the @Table annotation on class LikePost to include a UniqueConstraint for
columns "user_id" and "post_id" and import jakarta.persistence.UniqueConstraint;
no other logic changes needed—this enforces uniqueness at the DB level for the
LikePost entity (fields: likePostId, user, post).
🧹 Nitpick comments (2)
src/main/java/com/campus/campus/domain/councilpost/presentation/StudentCouncilPostController.java (1)
189-201: 좋아요 게시글 목록 조회 엔드포인트가 잘 구현되어 있습니다.기능 구현은 정상적이며, 페이지네이션과 필터링 로직이 적절합니다.
다만, Line 200에서
POST_LIST_READ_SUCCESS응답 코드를 사용하고 있는데, 이는 일반 게시글 목록 조회와 동일한 메시지입니다. 더 명확한 사용자 경험을 위해 "관심 게시글 목록 조회에 성공했습니다." 같은 전용 응답 코드를 고려해볼 수 있습니다.♻️ 선택적 개선사항: 전용 응답 코드 추가
StudentCouncilPostResponseCode에 새로운 상수를 추가:POST_LIKED_LIST_READ_SUCCESS(200, HttpStatus.OK, "관심 게시글 목록 조회에 성공했습니다.")그리고 컨트롤러에서 사용:
- return CommonResponse.success(StudentCouncilPostResponseCode.POST_LIST_READ_SUCCESS, responses); + return CommonResponse.success(StudentCouncilPostResponseCode.POST_LIKED_LIST_READ_SUCCESS, responses);src/main/java/com/campus/campus/domain/councilpost/domain/entity/LikePost.java (1)
3-10: Import 및 FetchType 사용 방식이 일관성이 없습니다.라인 3에서
FetchType.*을 static import하고 있으나, 라인 10에서 다시 명시적으로 import하고 있습니다. 또한 라인 35에서는FetchType.LAZY를, 라인 39에서는LAZY를 사용하여 일관성이 없습니다.♻️ 일관성 있는 사용 제안
static import를 제거하고 명시적 사용으로 통일하거나, 반대로 모두 static import 방식으로 통일하세요:
옵션 1: 명시적 사용으로 통일
-import static jakarta.persistence.FetchType.*; - import com.campus.campus.domain.user.domain.entity.User;그리고 라인 39를
FetchType.LAZY로 변경옵션 2: static import로 통일
-import jakarta.persistence.FetchType;그리고 라인 35를
LAZY로 변경Also applies to: 35-41
📜 Review details
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (8)
src/main/java/com/campus/campus/domain/councilpost/application/dto/response/GetLikedPostResponse.javasrc/main/java/com/campus/campus/domain/councilpost/application/dto/response/LikePostResponse.javasrc/main/java/com/campus/campus/domain/councilpost/application/mapper/StudentCouncilPostMapper.javasrc/main/java/com/campus/campus/domain/councilpost/application/service/StudentCouncilPostService.javasrc/main/java/com/campus/campus/domain/councilpost/domain/entity/LikePost.javasrc/main/java/com/campus/campus/domain/councilpost/domain/repository/LikePostRepository.javasrc/main/java/com/campus/campus/domain/councilpost/presentation/StudentCouncilPostController.javasrc/main/java/com/campus/campus/domain/councilpost/presentation/StudentCouncilPostResponseCode.java
🧰 Additional context used
🧬 Code graph analysis (4)
src/main/java/com/campus/campus/domain/councilpost/application/dto/response/GetLikedPostResponse.java (7)
src/main/java/com/campus/campus/domain/councilpost/application/dto/response/PostListItemResponse.java (1)
PostListItemResponse(8-18)src/main/java/com/campus/campus/domain/manager/application/dto/response/CouncilApproveOrDenyResponse.java (1)
CouncilApproveOrDenyResponse(5-12)src/main/java/com/campus/campus/domain/manager/application/dto/response/CertifyRequestCouncilResponse.java (1)
CertifyRequestCouncilResponse(5-15)src/main/java/com/campus/campus/domain/school/application/dto/response/CollegeFindResponse.java (1)
CollegeFindResponse(5-12)src/main/java/com/campus/campus/domain/manager/application/dto/response/CertifyRequestCouncilListResponse.java (1)
CertifyRequestCouncilListResponse(7-17)src/main/java/com/campus/campus/domain/place/application/dto/response/LikeResponse.java (1)
LikeResponse(3-7)src/main/java/com/campus/campus/domain/councilnotice/application/dto/response/NoticeListItemResponse.java (1)
Builder(7-15)
src/main/java/com/campus/campus/domain/councilpost/application/dto/response/LikePostResponse.java (7)
src/main/java/com/campus/campus/domain/councilpost/application/dto/response/PostListItemResponse.java (1)
PostListItemResponse(8-18)src/main/java/com/campus/campus/domain/place/application/dto/response/LikeResponse.java (1)
LikeResponse(3-7)src/main/java/com/campus/campus/domain/manager/application/dto/response/CouncilApproveOrDenyResponse.java (1)
CouncilApproveOrDenyResponse(5-12)src/main/java/com/campus/campus/domain/councilpost/application/dto/request/PostRequest.java (1)
PostRequest(14-42)src/main/java/com/campus/campus/domain/school/application/dto/response/CollegeFindResponse.java (1)
CollegeFindResponse(5-12)src/main/java/com/campus/campus/domain/manager/application/dto/response/CertifyRequestCouncilListResponse.java (1)
CertifyRequestCouncilListResponse(7-17)src/main/java/com/campus/campus/domain/councilnotice/application/dto/response/NoticeListItemResponse.java (1)
Builder(7-15)
src/main/java/com/campus/campus/domain/councilpost/application/service/StudentCouncilPostService.java (3)
src/main/java/com/campus/campus/domain/user/application/exception/UserNotFoundException.java (1)
UserNotFoundException(5-9)src/main/java/com/campus/campus/domain/councilpost/application/exception/PostNotFoundException.java (1)
PostNotFoundException(5-9)src/main/java/com/campus/campus/domain/councilpost/domain/entity/StudentCouncilPost.java (1)
Entity(25-98)
src/main/java/com/campus/campus/domain/councilpost/application/mapper/StudentCouncilPostMapper.java (1)
src/main/java/com/campus/campus/domain/councilpost/domain/entity/StudentCouncilPost.java (1)
Entity(25-98)
🔇 Additional comments (7)
src/main/java/com/campus/campus/domain/councilpost/presentation/StudentCouncilPostResponseCode.java (1)
18-19: LGTM!새로운 응답 코드가 기존 패턴을 잘 따르고 있으며, 좋아요 기능에 적합한 메시지입니다.
src/main/java/com/campus/campus/domain/councilpost/application/dto/response/GetLikedPostResponse.java (1)
7-22: LGTM!DTO 정의가 명확하며, Swagger 문서화도 적절합니다.
src/main/java/com/campus/campus/domain/councilpost/domain/repository/LikePostRepository.java (1)
15-27: LGTM!리포지토리 구현이 잘 되어 있습니다:
@EntityGraph를 통해 N+1 쿼리 문제를 방지하고 있습니다.- 카테고리 필터링이 선택적으로 동작하도록
IS NULL조건을 잘 활용했습니다.- 생성 시간 기준 내림차순 정렬이 적절합니다.
src/main/java/com/campus/campus/domain/councilpost/presentation/StudentCouncilPostController.java (1)
180-187: LGTM!좋아요 토글 엔드포인트가 잘 구현되어 있습니다:
- USER 권한 검증이 적절합니다.
@CurrentUserId를 통한 사용자 식별이 명확합니다.- 응답 코드가 적절합니다.
src/main/java/com/campus/campus/domain/councilpost/application/mapper/StudentCouncilPostMapper.java (2)
66-82: LGTM! 새로운 매퍼 메서드들이 기존 패턴을 잘 따르고 있습니다.
toLikePostResponse와toGetLikedPostResponse메서드가 깔끔하게 구현되어 있습니다. 특히toGetLikedPostResponse의isEvent()삼항 연산자 로직이toPostListItemResponse(라인 32-34)와 일관성 있게 작성되었습니다.
106-111: LGTM!createLikePost메서드가 적절하게 구현되었습니다.Builder 패턴을 사용하여 LikePost 엔티티를 생성하며, 기존
createPostImage메서드와 동일한 패턴을 따르고 있습니다.src/main/java/com/campus/campus/domain/councilpost/application/service/StudentCouncilPostService.java (1)
165-177: 정렬이 이미 Repository 쿼리에서 처리되고 있습니다.
LikePostRepository.findLikedPosts()메서드의 @query 주석에ORDER BY lp.createdAt DESC가 명시되어 있어 "좋아요 누른 순서 내림차순" 요구사항이 이미 충족되고 있습니다. 서비스 레이어에서 추가 정렬 지정은 필요하지 않습니다.Likely an incorrect or invalid review comment.
...ain/java/com/campus/campus/domain/councilpost/application/dto/response/LikePostResponse.java
Outdated
Show resolved
Hide resolved
| @Transactional | ||
| public LikePostResponse toggleLikePost(Long userId, Long postId) { | ||
| User user = userRepository.findByIdAndDeletedAtIsNull(userId) | ||
| .orElseThrow(UserNotFoundException::new); | ||
|
|
||
| StudentCouncilPost post = postRepository.findById(postId) | ||
| .orElseThrow(PostNotFoundException::new); | ||
|
|
||
| Optional<LikePost> likePost = likePostRepository.findByUserIdAndPost_Id(userId, postId); | ||
| boolean isLike; | ||
| if (likePost.isPresent()) { | ||
| likePostRepository.delete(likePost.get()); | ||
| isLike = false; | ||
| } else { | ||
| LikePost newLikePost = studentCouncilPostMapper.createLikePost(user, post); | ||
| likePostRepository.save(newLikePost); | ||
| isLike = true; | ||
| } | ||
|
|
||
| return studentCouncilPostMapper.toLikePostResponse(user, post, isLike); | ||
| } |
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.
🧩 Analysis chain
🏁 Script executed:
fd -t f "LikePostRepository.java"Repository: our-campUS/our-campUS-BE
Length of output: 160
🏁 Script executed:
fd -t f "LikePost.java"Repository: our-campUS/our-campUS-BE
Length of output: 146
🏁 Script executed:
fd -t f "StudentCouncilPostService.java" --exec wc -l {} \;Repository: our-campUS/our-campUS-BE
Length of output: 175
🏁 Script executed:
cat -n src/main/java/com/campus/campus/domain/councilpost/domain/repository/LikePostRepository.javaRepository: our-campUS/our-campUS-BE
Length of output: 1331
🏁 Script executed:
cat -n src/main/java/com/campus/campus/domain/councilpost/domain/entity/LikePost.javaRepository: our-campUS/our-campUS-BE
Length of output: 1550
🏁 Script executed:
sed -n '140,170p' src/main/java/com/campus/campus/domain/councilpost/application/service/StudentCouncilPostService.javaRepository: our-campUS/our-campUS-BE
Length of output: 1118
🏁 Script executed:
fd -t f "\.sql" -o -t f "*migration*" -o -t f "*schema*" | head -20Repository: our-campUS/our-campUS-BE
Length of output: 184
🏁 Script executed:
rg "UniqueConstraint" src/main/java/com/campus/campus/domain/councilpost/domain/entity/Repository: our-campUS/our-campUS-BE
Length of output: 50
🏁 Script executed:
sed -n '1,50p' src/main/java/com/campus/campus/domain/councilpost/application/service/StudentCouncilPostService.javaRepository: our-campUS/our-campUS-BE
Length of output: 2931
🏁 Script executed:
find . -name "*.sql" -type f 2>/dev/null | head -20Repository: our-campUS/our-campUS-BE
Length of output: 50
🏁 Script executed:
find . -path "*/db/migration*" -type f 2>/dev/null | head -20Repository: our-campUS/our-campUS-BE
Length of output: 50
🏁 Script executed:
rg "like_post" -i --type sql | head -30Repository: our-campUS/our-campUS-BE
Length of output: 216
🏁 Script executed:
find . -name "*Flyway*" -o -name "*flyway*" -o -name "*liquibase*" 2>/dev/null | head -10Repository: our-campUS/our-campUS-BE
Length of output: 50
🏁 Script executed:
rg "DataIntegrityViolationException|UniqueConstraint" src/main/java/ -A 2 -B 2Repository: our-campUS/our-campUS-BE
Length of output: 3306
🏁 Script executed:
rg "toggleLikePost|LikePost" src/main/java/com/campus/campus/domain/councilpost/application/controller/ -A 5 -B 2Repository: our-campUS/our-campUS-BE
Length of output: 182
🏁 Script executed:
find src/main/java -path "*councilpost*controller*" -type fRepository: our-campUS/our-campUS-BE
Length of output: 50
🏁 Script executed:
find src/main/java -path "*council*controller*" -type fRepository: our-campUS/our-campUS-BE
Length of output: 50
🏁 Script executed:
rg "toggleLikePost" src/main/java/ -A 5 -B 2Repository: our-campUS/our-campUS-BE
Length of output: 2318
toggleLikePost 메서드의 동시성 문제를 해결하세요.
현재 구현은 race condition이 발생할 수 있습니다. 두 요청이 동시에 좋아요 존재 여부를 조회한 후, 둘 다 새로운 좋아요를 생성하려 할 수 있습니다.
이를 해결하려면:
LikePost엔티티의@Table에UniqueConstraint를 추가하세요 (예:@UniqueConstraint(columnNames = {"user_id", "post_id"}))- 중복 생성 시 발생하는
DataIntegrityViolationException을toggleLikePost메서드에서 처리하세요
참고로 LikedPlace 엔티티와 PlaceService에서 이와 동일한 패턴을 사용하고 있습니다.
🤖 Prompt for AI Agents
In
@src/main/java/com/campus/campus/domain/councilpost/application/service/StudentCouncilPostService.java
around lines 143 - 163, The toggleLikePost method in StudentCouncilPostService
has a race condition when two requests concurrently try to create a LikePost;
add a unique DB constraint on LikePost (annotate the LikePost entity @Table with
@UniqueConstraint(columnNames = {"user_id","post_id"}) to enforce uniqueness)
and modify toggleLikePost to catch DataIntegrityViolationException (thrown by
likePostRepository.save) and treat it as "already liked" instead of failing
(i.e., on catch, set isLike = true and continue to return the LikePostResponse);
keep existing delete path unchanged and reference methods/classes:
toggleLikePost, LikePost entity, likePostRepository, studentCouncilPostMapper,
StudentCouncilPostService.
...java/com/campus/campus/domain/councilpost/application/service/StudentCouncilPostService.java
Outdated
Show resolved
Hide resolved
src/main/java/com/campus/campus/domain/councilpost/domain/entity/LikePost.java
Show resolved
Hide resolved
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: 0
🧹 Nitpick comments (1)
src/main/java/com/campus/campus/domain/councilpost/domain/entity/LikePost.java (1)
3-3: FetchType 사용 스타일 통일을 권장합니다.Line 3에서
LAZY를 static import하고 있지만, Line 38에서는FetchType.LAZY를, Line 42에서는LAZY를 사용하여 일관성이 없습니다. 다음 중 하나의 스타일로 통일하는 것을 권장합니다:
- 옵션 1: static import를 제거하고 모든 곳에서
FetchType.LAZY사용- 옵션 2: 모든 곳에서 static import된
LAZY사용♻️ 옵션 2 (static import 사용) 적용 예시
@ManyToOne(fetch = FetchType.LAZY) @JoinColumn(name = "user_id") private User user;위 코드를 아래와 같이 변경:
- @ManyToOne(fetch = FetchType.LAZY) + @ManyToOne(fetch = LAZY) @JoinColumn(name = "user_id") private User user;Also applies to: 10-10, 38-38, 42-42
📜 Review details
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (2)
src/main/java/com/campus/campus/domain/councilpost/application/dto/response/LikePostResponse.javasrc/main/java/com/campus/campus/domain/councilpost/domain/entity/LikePost.java
🚧 Files skipped from review as they are similar to previous changes (1)
- src/main/java/com/campus/campus/domain/councilpost/application/dto/response/LikePostResponse.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/com/campus/campus/domain/councilpost/domain/entity/LikePost.java (2)
25-27: 유니크 제약 조건이 올바르게 적용되었습니다.
(user_id, post_id)조합에 대한 유니크 제약 조건이 적절히 설정되어 있어, 동일한 사용자가 같은 게시글에 중복으로 좋아요를 등록하는 것을 데이터베이스 레벨에서 방지합니다. 토글 방식의 좋아요 기능 구현에 필수적인 제약 조건입니다.
24-45: 엔티티 구조가 잘 설계되었습니다.전체적인 엔티티 구조가 JPA 및 도메인 모델 설계 모범 사례를 따르고 있습니다:
- Builder 패턴과 적절한 접근 제어자 사용
- 양방향 관계에서 LAZY 페치 전략으로 N+1 쿼리 방지
- BaseEntity 상속으로 감사(audit) 필드 관리
- IDENTITY 전략은 like_posts 테이블에 적합한 선택
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
🤖 Fix all issues with AI agents
In
@src/main/java/com/campus/campus/domain/councilpost/application/mapper/StudentCouncilPostMapper.java:
- Around line 29-41: The mapper's parameter name councilId is misleading because
callers pass a userId, causing post.isWrittenByCouncil(councilId) to compare the
wrong id; rename the parameter in
StudentCouncilPostMapper.toPostListItemResponse from councilId to a neutral name
like entityId (or authorId/writerId) and update all callers (e.g.,
StudentCouncilPostForUserService.mapPostsWithLikes) to pass the correct id
semantics or add an explicit boolean/overload for "writtenByCouncil" so the
method either accepts a councilId when checking post.isWrittenByCouncil(...) or
is clearly labeled for other id types.
In
@src/main/java/com/campus/campus/domain/councilpost/presentation/StudentCouncilPostForUserController.java:
- Line 39: Remove the redundant method-level @PreAuthorize("hasRole('USER')")
annotations inside the StudentCouncilPostForUserController class since the class
itself already declares @PreAuthorize("hasRole('USER')"); locate any methods in
that controller that repeat the same @PreAuthorize annotation (the ones flagged
in the review) and delete those method-level annotations, leaving the
class-level security annotation intact.
🧹 Nitpick comments (3)
src/main/java/com/campus/campus/domain/councilpost/application/dto/response/PostListItemResponse.java (1)
16-17: 타입 일관성을 검토해주세요.
isWriter는Boolean(nullable)인 반면liked는boolean(primitive)입니다.liked가 항상 값을 가져야 한다면 현재 구현이 적절하지만, DTO 내 일관성을 위해 의도적인 선택인지 확인하시기 바랍니다.♻️ 일관성을 위한 제안
만약 두 필드 모두 항상 값이 있다면:
- Boolean isWriter, - boolean liked + boolean isWriter, + boolean liked또는 nullable이 필요하다면:
- Boolean isWriter, - boolean liked + Boolean isWriter, + Boolean likedsrc/main/java/com/campus/campus/domain/councilpost/presentation/StudentCouncilPostForUserController.java (1)
38-60: 메서드 위치를 필드 선언 이후로 이동하세요.Java 코딩 컨벤션에 따르면 필드 선언(Line 61)은 메서드보다 앞에 위치해야 합니다. 새로 추가된 메서드들을 필드 선언 다음으로 이동하는 것이 좋습니다.
♻️ 권장하는 구조
@Slf4j public class StudentCouncilPostForUserController { + private final StudentCouncilPostForUserService postService; + @PostMapping("/{postId}/like") @Operation(summary = "학생회 게시글 좋아요 토글") public CommonResponse<LikePostResponse> togglePostLike(@PathVariable Long postId, @CurrentUserId Long userId) { ... } @GetMapping("/likes") @Operation(summary = "관심 학생회 게시글 목록 조회") public CommonResponse<Page<GetLikedPostResponse>> getLikedPosts(...) { ... } - - private final StudentCouncilPostForUserService postService; @GetMapping("/school") @Operation(...)src/main/java/com/campus/campus/domain/councilpost/application/mapper/StudentCouncilPostMapper.java (1)
100-124: 사용되지 않는 파라미터
currentUserId파라미터가 선언되었지만 메서드 본문에서 사용되지 않습니다. 필요하지 않다면 제거하거나, 향후 사용 계획이 있다면 주석으로 의도를 명시하세요.♻️ 수정 제안
- public GetPostResponseForUser toGetPostResponseForUser(StudentCouncilPost post, List<String> images, - Long currentUserId, boolean isLiked) { + public GetPostResponseForUser toGetPostResponseForUser(StudentCouncilPost post, List<String> images, + boolean isLiked) {호출부도 함께 수정이 필요합니다:
- return studentCouncilPostMapper.toGetPostResponseForUser(post, imageUrls, userId, isLiked); + return studentCouncilPostMapper.toGetPostResponseForUser(post, imageUrls, isLiked);
📜 Review details
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (10)
src/main/java/com/campus/campus/domain/councilpost/application/dto/response/GetPostResponse.javasrc/main/java/com/campus/campus/domain/councilpost/application/dto/response/GetPostResponseForUser.javasrc/main/java/com/campus/campus/domain/councilpost/application/dto/response/PostListItemResponse.javasrc/main/java/com/campus/campus/domain/councilpost/application/mapper/StudentCouncilPostMapper.javasrc/main/java/com/campus/campus/domain/councilpost/application/service/StudentCouncilPostForUserService.javasrc/main/java/com/campus/campus/domain/councilpost/application/service/StudentCouncilPostService.javasrc/main/java/com/campus/campus/domain/councilpost/domain/repository/LikePostRepository.javasrc/main/java/com/campus/campus/domain/councilpost/presentation/StudentCouncilPostController.javasrc/main/java/com/campus/campus/domain/councilpost/presentation/StudentCouncilPostForUserController.javasrc/main/java/com/campus/campus/domain/councilpost/presentation/StudentCouncilPostResponseCode.java
🚧 Files skipped from review as they are similar to previous changes (1)
- src/main/java/com/campus/campus/domain/councilpost/presentation/StudentCouncilPostController.java
🧰 Additional context used
🧬 Code graph analysis (3)
src/main/java/com/campus/campus/domain/councilpost/application/dto/response/GetPostResponseForUser.java (2)
src/main/java/com/campus/campus/domain/councilpost/application/dto/request/PostRequest.java (1)
PostRequest(14-42)src/main/java/com/campus/campus/domain/councilpost/application/dto/response/PostResponse.java (1)
Builder(13-35)
src/main/java/com/campus/campus/domain/councilpost/presentation/StudentCouncilPostForUserController.java (1)
src/main/java/com/campus/campus/domain/councilpost/presentation/StudentCouncilPostController.java (1)
RestController(32-167)
src/main/java/com/campus/campus/domain/councilpost/application/service/StudentCouncilPostForUserService.java (4)
src/main/java/com/campus/campus/domain/councilpost/application/exception/CollegeNotSetException.java (1)
CollegeNotSetException(5-9)src/main/java/com/campus/campus/domain/councilpost/application/exception/MajorNotSetException.java (1)
MajorNotSetException(5-9)src/main/java/com/campus/campus/domain/councilpost/application/exception/PostNotFoundException.java (1)
PostNotFoundException(5-9)src/main/java/com/campus/campus/domain/user/application/exception/UserNotFoundException.java (1)
UserNotFoundException(5-9)
⏰ 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 (16)
src/main/java/com/campus/campus/domain/councilpost/presentation/StudentCouncilPostResponseCode.java (1)
21-22: 토글(좋아요/취소) 동작과POST_LIKE_SUCCESS의미가 정확히 맞는지 확인 필요현재 메시지가 “좋아요 처리”로 되어 있어 토글(좋아요/취소) 모두를 포괄하는 건 자연스럽습니다. 다만 클라이언트가 “최종 상태(좋아요됨/취소됨)”를 응답코드로 구분해야 하는 요구사항이 있다면, (1) 응답 바디에 최종 상태를 명시하거나 (2) 코드 상수를 like/unlike로 분리하는 편이 더 명확합니다.
src/main/java/com/campus/campus/domain/councilpost/application/dto/response/GetPostResponseForUser.java (1)
13-36: 새로운 사용자 전용 응답 DTO가 적절하게 구현되었습니다.
GetPostResponseForUser는 사용자별 좋아요 상태(isLiked)를 포함하여 기존PostResponse와 명확하게 분리되어 있습니다. 구조와 어노테이션 사용이 적절합니다.src/main/java/com/campus/campus/domain/councilpost/application/dto/response/GetPostResponse.java (1)
15-15: 네이밍 변경이 적절합니다.
PostResponse에서GetPostResponse로의 리네이밍은 응답 DTO의 용도를 더 명확하게 표현합니다.src/main/java/com/campus/campus/domain/councilpost/application/service/StudentCouncilPostService.java (1)
18-18: 반환 타입 변경이 일관되게 적용되었습니다.
PostResponse에서GetPostResponse로의 변경이 모든 메서드(create,findById,update)와 매퍼 호출에 일관되게 적용되었습니다.Also applies to: 53-53, 86-86, 90-90, 100-100, 181-181, 233-233
src/main/java/com/campus/campus/domain/councilpost/presentation/StudentCouncilPostForUserController.java (3)
38-45: 좋아요 토글 엔드포인트가 적절하게 구현되었습니다.토글 방식으로 좋아요/좋아요 취소를 하나의 엔드포인트로 처리하는 것은 RESTful API 설계에서 일반적인 패턴입니다.
47-59: 관심 게시글 조회 엔드포인트가 적절하게 구현되었습니다.카테고리 필터링과 페이지네이션을 지원하는 구조가 잘 설계되어 있습니다.
113-119: 사용자 전용 응답 타입으로의 변경이 적절합니다.
GetPostResponseForUser를 사용하여 사용자별 좋아요 상태를 포함한 게시글 상세 정보를 제공하는 것은 적절한 분리입니다.src/main/java/com/campus/campus/domain/councilpost/domain/repository/LikePostRepository.java (2)
21-30: LGTM! EntityGraph와 JPQL 쿼리가 적절하게 구성되었습니다.N+1 문제를 방지하기 위해
post와post.writer를 함께 로딩하는 EntityGraph 설정이 잘 되어 있습니다. 카테고리 필터링을 위한 nullable 파라미터 처리도 적절합니다.
32-37: 빈 리스트는 이미 적절히 처리되고 있습니다
postIds가 빈 리스트일 경우의 잠재적 이슈는 이미 서비스 레이어에서 명시적으로 처리되고 있습니다.mapPostsWithLikes메서드의 267-269줄에서if (postIds.isEmpty())가드 절이 빈 리스트일 때findLikedPostIds호출을 방지하므로, 현재 구현은 안전하고 추가 수정은 불필요합니다.src/main/java/com/campus/campus/domain/councilpost/application/service/StudentCouncilPostForUserService.java (4)
79-90: LGTM!사용자 존재 여부 검증 후 관심 게시글을 조회하는 로직이 명확합니다. 페이지네이션 처리도 적절합니다.
92-109: LGTM!게시글 상세 조회 로직이 잘 구현되었습니다. 권한 검증, 이미지 조회, 좋아요 상태 확인이 적절하게 처리됩니다.
262-276: 잘 설계된 헬퍼 메서드입니다.N+1 문제를 방지하기 위해 배치 쿼리로 좋아요 상태를 조회하고,
HashSet으로 변환하여 O(1) 조회 성능을 확보한 점이 좋습니다. 빈 리스트에 대한 early return 처리도 적절합니다.
119-119: 일관된 리팩토링 패턴모든 게시글 목록 조회 메서드에서
mapPostsWithLikes를 사용하여 좋아요 상태를 일관되게 처리하는 패턴이 잘 적용되었습니다.src/main/java/com/campus/campus/domain/councilpost/application/mapper/StudentCouncilPostMapper.java (3)
75-98: LGTM!빌더 패턴 사용과 null 처리가 적절합니다. 이벤트/제휴 유형에 따른 날짜 필드 분기 처리도 일관성 있게 구현되었습니다.
126-142: LGTM!
toLikePostResponse와toGetLikedPostResponse매핑 메서드가 간결하고 명확하게 구현되었습니다.
166-171: LGTM!
LikePost엔티티 생성 로직이 적절합니다.createdAt필드는 엔티티의@CreatedDate어노테이션으로 자동 설정될 것으로 예상됩니다.
...n/java/com/campus/campus/domain/councilpost/application/mapper/StudentCouncilPostMapper.java
Outdated
Show resolved
Hide resolved
...a/com/campus/campus/domain/councilpost/presentation/StudentCouncilPostForUserController.java
Outdated
Show resolved
Hide resolved
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: 2
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
src/main/java/com/campus/campus/domain/councilpost/application/mapper/StudentCouncilPostMapper.java (1)
99-123: 게시글 작성자 null 체크 필요 (사용자용 응답)
toGetPostResponse와 동일하게, Line 101에서post.getWriter()의 결과를 null 체크 없이 Lines 104-105에서 사용하고 있습니다. NPE 위험이 있으므로 null 체크 로직을 추가하거나 엔티티 레벨에서 제약을 보장해야 합니다.🛡️ NPE 방지를 위한 수정 제안
public GetPostForUserResponse toGetPostForUserResponse(StudentCouncilPost post, List<String> images, Long currentUserId, boolean isLiked) { var writer = post.getWriter(); + if (writer == null) { + throw new IllegalStateException("게시글에 작성자 정보가 없습니다."); + } var builder = GetPostForUserResponse.builder()
🤖 Fix all issues with AI agents
In
@src/main/java/com/campus/campus/domain/councilpost/application/mapper/StudentCouncilPostMapper.java:
- Around line 74-97: The toGetPostResponse method uses post.getWriter() without
null-check, which can NPE on writer.getId() / writer.getCouncilName(); update
toGetPostResponse to safely handle a null writer by fetching var writer =
post.getWriter(); then check if writer == null and set builder.writerId(null)
and builder.writerName(null) or a sensible default (e.g., "Unknown") before
calling writer.getId() / writer.getCouncilName(); ensure
isWriter(post.isWrittenByCouncil(currentCouncilId)) still works when writer is
null (guard or compute using post.isWrittenByCouncil which should tolerate
null), and return builder.build().
In
@src/main/java/com/campus/campus/domain/councilpost/application/service/StudentCouncilPostForUserService.java:
- Around line 58-77: The toggleLikePost method in
StudentCouncilPostForUserService uses a delete-then-insert pattern and currently
treats any DataIntegrityViolationException as a unique-constraint conflict;
update the exception handling to explicitly detect a unique-constraint violation
before assuming the like already exists (e.g., inspect SQLState, vendor-specific
error code, or constraint name from the DataIntegrityViolationException/cause),
rethrow or wrap other constraint violations (foreign key, not-null, etc.), and
keep the existing delete-then-insert flow otherwise; ensure changes are applied
around the likePostRepository.saveAndFlush call and in the catch for
DataIntegrityViolationException so only true unique key violations return the
"true" response while other DB errors propagate as errors.
🧹 Nitpick comments (1)
src/main/java/com/campus/campus/domain/councilpost/presentation/StudentCouncilPostForUserController.java (1)
38-44: 경로 변수에 대한 유효성 검증 추가를 권장합니다.좋아요 토글 엔드포인트가 잘 구현되었습니다. 다만
@PathVariable에@Positive같은 검증 어노테이션을 추가하면 잘못된 ID 값에 대한 처리가 더 명확해집니다.♻️ 제안하는 개선 사항
@PostMapping("/{postId}/like") @Operation(summary = "학생회 게시글 좋아요 토글") -public CommonResponse<LikePostResponse> togglePostLike(@PathVariable Long postId, @CurrentUserId Long userId) { +public CommonResponse<LikePostResponse> togglePostLike(@PathVariable @Positive Long postId, @CurrentUserId Long userId) { LikePostResponse response = postService.toggleLikePost(userId, postId); return CommonResponse.success(StudentCouncilPostResponseCode.POST_LIKE_SUCCESS, response); }
📜 Review details
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (6)
src/main/java/com/campus/campus/domain/councilpost/application/dto/response/GetPostForUserResponse.javasrc/main/java/com/campus/campus/domain/councilpost/application/dto/response/PostListItemResponse.javasrc/main/java/com/campus/campus/domain/councilpost/application/mapper/StudentCouncilPostMapper.javasrc/main/java/com/campus/campus/domain/councilpost/application/service/StudentCouncilPostForUserService.javasrc/main/java/com/campus/campus/domain/councilpost/domain/repository/LikePostRepository.javasrc/main/java/com/campus/campus/domain/councilpost/presentation/StudentCouncilPostForUserController.java
🧰 Additional context used
🧬 Code graph analysis (5)
src/main/java/com/campus/campus/domain/councilpost/application/dto/response/PostListItemResponse.java (2)
src/main/java/com/campus/campus/domain/councilnotice/application/dto/response/NoticeListItemResponse.java (1)
Builder(7-15)src/main/java/com/campus/campus/domain/place/application/dto/response/LikeResponse.java (1)
LikeResponse(3-7)
src/main/java/com/campus/campus/domain/councilpost/application/dto/response/GetPostForUserResponse.java (4)
src/main/java/com/campus/campus/domain/councilpost/application/dto/request/PostRequest.java (1)
PostRequest(14-42)src/main/java/com/campus/campus/domain/councilpost/application/dto/response/PostResponse.java (1)
Builder(13-35)src/main/java/com/campus/campus/domain/councilpost/domain/entity/StudentCouncilPost.java (1)
Entity(25-98)src/main/java/com/campus/campus/domain/councilpost/domain/entity/PostImage.java (1)
Entity(18-36)
src/main/java/com/campus/campus/domain/councilpost/presentation/StudentCouncilPostForUserController.java (1)
src/main/java/com/campus/campus/domain/place/presentation/PlaceController.java (1)
PostMapping(36-42)
src/main/java/com/campus/campus/domain/councilpost/application/mapper/StudentCouncilPostMapper.java (7)
src/main/java/com/campus/campus/domain/council/application/util/CouncilNameGenerator.java (1)
Component(8-34)src/main/java/com/campus/campus/domain/councilnotice/application/mapper/StudentCouncilNoticeMapper.java (1)
Component(16-58)src/main/java/com/campus/campus/global/oci/mapper/PresignedUrlMapper.java (1)
Component(14-45)src/main/java/com/campus/campus/domain/councilpost/domain/entity/StudentCouncilPost.java (1)
Entity(25-98)src/main/java/com/campus/campus/domain/councilpost/application/service/StudentCouncilPostService.java (3)
post(110-111)Slf4j(37-255)post(128-129)src/main/java/com/campus/campus/domain/councilpost/presentation/StudentCouncilPostController.java (2)
PatchMapping(113-124)PostMapping(40-111)src/main/java/com/campus/campus/domain/councilpost/application/service/UserPostService.java (1)
post(84-84)
src/main/java/com/campus/campus/domain/councilpost/application/service/StudentCouncilPostForUserService.java (4)
src/main/java/com/campus/campus/domain/councilpost/application/exception/CollegeNotSetException.java (1)
CollegeNotSetException(5-9)src/main/java/com/campus/campus/domain/councilpost/application/exception/MajorNotSetException.java (1)
MajorNotSetException(5-9)src/main/java/com/campus/campus/domain/councilpost/application/exception/PostNotFoundException.java (1)
PostNotFoundException(5-9)src/main/java/com/campus/campus/domain/user/application/exception/UserNotFoundException.java (1)
UserNotFoundException(5-9)
⏰ 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 (16)
src/main/java/com/campus/campus/domain/councilpost/application/dto/response/PostListItemResponse.java (1)
8-17: LGTM! 좋아요 상태를 나타내는 필드로 적절하게 변경되었습니다.
isWriter에서liked로 변경하여 좋아요 기능을 지원하도록 DTO를 업데이트했습니다. primitive boolean 타입 사용도 적절합니다.src/main/java/com/campus/campus/domain/councilpost/application/dto/response/GetPostForUserResponse.java (1)
13-36: LGTM! 사용자 전용 게시글 응답 DTO가 잘 설계되었습니다.좋아요 상태(
isLiked)와 이미지 목록을 포함하여 사용자 관점의 게시글 정보를 적절히 표현하고 있습니다.@JsonInclude(NON_NULL)사용도 적절합니다.src/main/java/com/campus/campus/domain/councilpost/presentation/StudentCouncilPostForUserController.java (2)
46-57: LGTM! 관심 게시글 조회 엔드포인트가 잘 구현되었습니다.카테고리 필터링과 페이지네이션이 적절히 구현되어 있습니다.
size기본값이 3으로 다른 엔드포인트(20)보다 작은데, 관심 게시글 목록 특성상 의도적인 것으로 보입니다.
109-118: LGTM! 게시글 상세 조회가 좋아요 상태를 포함하도록 업데이트되었습니다.
GetPostForUserResponse로 응답 타입을 변경하여 사용자별 좋아요 상태를 함께 반환할 수 있게 되었습니다.src/main/java/com/campus/campus/domain/councilpost/domain/repository/LikePostRepository.java (4)
18-23: LGTM! 삭제 쿼리가 올바르게 구현되었습니다.
@Modifying(clearAutomatically = true)를 사용하여 삭제 후 영속성 컨텍스트를 적절히 관리하고 있으며, 반환 타입int를 통해 삭제 여부를 확인할 수 있습니다.
25-25: LGTM! 존재 여부 확인 메서드가 적절합니다.Spring Data JPA의 derived query 규칙에 따라 올바르게 작성되었으며, 좋아요 상태 확인에 효율적입니다.
38-43: LGTM! 배치 조회 쿼리가 효율적으로 구현되었습니다.여러 게시글의 좋아요 상태를 한 번의 쿼리로 확인할 수 있어 N+1 문제를 방지합니다. 서비스의
mapPostsWithLikes에서 효과적으로 활용되고 있습니다.
27-36: @entitygraph에서 post.writer 제거를 권장합니다.
GetLikedPostResponse는 게시글의 id, title, place, 시간, thumbnailImageUrl만 포함하며 작성자 정보를 사용하지 않습니다. 따라서 현재post.writer를 로드하는 것은 불필요한 N+1 쿼리를 발생시킵니다.@EntityGraph(attributePaths = {"post"})로 단순화하거나, 작성자 정보가 실제로 필요 없다면 @entitygraph 자체를 제거하고 레이지 로딩에 의존하는 것이 좋습니다.Likely an incorrect or invalid review comment.
src/main/java/com/campus/campus/domain/councilpost/application/service/StudentCouncilPostForUserService.java (4)
79-90: LGTM! 관심 게시글 조회가 깔끔하게 구현되었습니다.사용자 검증, 페이지네이션 처리, 응답 매핑이 모두 적절합니다.
Math.max(page - 1, 0)로 1-based 페이지 번호를 안전하게 처리하고 있습니다.
92-109: LGTM! 게시글 상세 조회가 좋아요 상태를 포함하도록 개선되었습니다.권한 검증, 이미지 로딩, 좋아요 상태 확인이 모두 적절히 구현되어 있습니다. 단건 조회이므로
isLiked체크를 위한 추가 쿼리의 오버헤드는 허용 가능한 수준입니다.
262-276: LGTM! 좋아요 상태를 효율적으로 배치 처리하고 있습니다.게시글 목록 조회 시 N+1 문제를 방지하기 위해 좋아요한 게시글 ID를 배치로 조회하고,
HashSet을 사용하여 O(1) 시간 복잡도로 조회하는 최적화가 훌륭합니다.
119-119: LGTM! 모든 목록 조회 메서드에 좋아요 상태가 일관되게 적용되었습니다.
mapPostsWithLikes헬퍼 메서드를 활용하여 학교/단과대/전공 게시글 및 임박한 행사 조회 모두에 좋아요 상태를 일관되게 제공하고 있습니다.Also applies to: 134-134, 145-145, 165-165, 193-193, 221-221
src/main/java/com/campus/campus/domain/councilpost/application/mapper/StudentCouncilPostMapper.java (4)
125-131: LGTM!좋아요 응답 매핑 로직이 명확하고 올바르게 구현되었습니다.
133-141: LGTM!좋아요한 게시글 응답 매핑이 올바르게 구현되었습니다. Line 138의 날짜 선택 로직(행사는 startDateTime, 제휴는 endDateTime)도 다른 메서드들과 일관성 있게 작성되었습니다.
165-170: LGTM!
LikePost엔티티 생성 로직이 빌더 패턴을 사용하여 명확하게 구현되었습니다.
29-40: 메서드 시그니처 변경이 모든 호출부에서 올바르게 적용되었습니다.StudentCouncilPostForUserService의 두 호출 지점 모두boolean isLiked파라미터를 올바르게 전달하고 있으며, 호환성 문제는 없습니다.
| public GetPostResponse toGetPostResponse(StudentCouncilPost post, List<String> images, Long currentCouncilId) { | ||
| var writer = post.getWriter(); | ||
| var builder = GetPostResponse.builder() | ||
| .id(post.getId()) | ||
| .writerId(writer.getId()) | ||
| .writerName(writer.getCouncilName()) | ||
| .isWriter(post.isWrittenByCouncil(currentCouncilId)) | ||
| .category(post.getCategory()) | ||
| .title(post.getTitle()) | ||
| .content(post.getContent()) | ||
| .place(post.getPlace()) | ||
| .thumbnailImageUrl(post.getThumbnailImageUrl()) | ||
| .thumbnailIcon(post.getThumbnailIcon()) | ||
| .images(images != null ? images : Collections.emptyList()); | ||
|
|
||
| if (post.isEvent()) { | ||
| builder.startDateTime(post.getStartDateTime()); | ||
| } else { | ||
| builder.startDate(post.getDisplayStartDate()); | ||
| builder.endDate(post.getDisplayEndDate()); | ||
| } | ||
|
|
||
| return builder.build(); | ||
| } |
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.
게시글 작성자 null 체크 필요
Line 75에서 post.getWriter()의 결과를 null 체크 없이 사용하고 있습니다. StudentCouncilPost 엔티티의 writer 필드는 @ManyToOne으로 정의되어 있으나 @NotNull 제약이 없어 null일 가능성이 있습니다. Lines 78-79에서 writer.getId() 및 writer.getCouncilName() 호출 시 NPE가 발생할 수 있습니다.
🛡️ NPE 방지를 위한 수정 제안
public GetPostResponse toGetPostResponse(StudentCouncilPost post, List<String> images, Long currentCouncilId) {
var writer = post.getWriter();
+ if (writer == null) {
+ throw new IllegalStateException("게시글에 작성자 정보가 없습니다.");
+ }
var builder = GetPostResponse.builder()
.id(post.getId())
.writerId(writer.getId())또는 서비스 계층에서 writer가 항상 존재함을 보장하는 경우, 엔티티에 @NotNull 제약을 추가하는 것을 권장합니다.
🤖 Prompt for AI Agents
In
@src/main/java/com/campus/campus/domain/councilpost/application/mapper/StudentCouncilPostMapper.java
around lines 74 - 97, The toGetPostResponse method uses post.getWriter() without
null-check, which can NPE on writer.getId() / writer.getCouncilName(); update
toGetPostResponse to safely handle a null writer by fetching var writer =
post.getWriter(); then check if writer == null and set builder.writerId(null)
and builder.writerName(null) or a sensible default (e.g., "Unknown") before
calling writer.getId() / writer.getCouncilName(); ensure
isWriter(post.isWrittenByCouncil(currentCouncilId)) still works when writer is
null (guard or compute using post.isWrittenByCouncil which should tolerate
null), and return builder.build().
🔀 변경 내용
✅ 작업 항목
📸 스크린샷 (선택)
좋아요 등록
관심 게시물 목록 조회
📎 참고 이슈
관련 이슈 번호 #42
Summary by CodeRabbit
릴리스 노트
✏️ Tip: You can customize this high-level summary in your review settings.