Sisc1 151 be 게시물목록조회#88
Hidden character warning
Conversation
…-151-BE-게시물목록조회
Walkthrough새로운 게시판 기능을 추가합니다. REST 컨트롤러, JPA 엔티티, DTO, 저장소, 서비스 구현으로 구성되어 게시물 CRUD, 댓글, 좋아요, 북마크, 파일 업로드 기능을 지원합니다. Changes
Sequence Diagram(s)sequenceDiagram
actor User
participant Controller as PostController
participant Service as PostServiceImpl
participant Repo as Repositories
participant FileService as FileUploadService
participant DB as Database
User->>Controller: 파일 포함 게시물 생성<br/>(multipart/form-data)
Controller->>Service: savePost(request, userId)
Service->>Repo: findById(userId)
Repo-->>Service: User entity
Service->>FileService: store(file)
FileService->>FileService: UUID 접두사 생성<br/>파일명 정규화
FileService-->>Service: 저장된 파일명
Service->>Repo: save(Post)
Service->>Repo: save(PostAttachment)
Repo->>DB: INSERT
DB-->>Repo: 성공
Service-->>Controller: void
Controller-->>User: 200 OK
sequenceDiagram
actor User as User (Non-Owner)
participant Controller as PostController
participant Service as PostServiceImpl
participant Repo as PostRepository
participant DB as Database
User->>Controller: updatePost(postId)
Controller->>Service: updatePost(request, postId, userId)
Service->>Repo: findById(postId)
Repo->>DB: SELECT
DB-->>Repo: Post
Repo-->>Service: Post
alt 소유자 불일치
Service-->>Controller: CustomException<br/>(INVALID_POST_OWNER)
Controller-->>User: 403 Forbidden
else 소유자 일치
Service->>Service: 필드 업데이트
Service->>Repo: save(Post)
Repo->>DB: UPDATE
DB-->>Service: 성공
Service-->>Controller: void
Controller-->>User: 200 OK
end
sequenceDiagram
actor User
participant Controller as PostController
participant Service as PostServiceImpl
participant Repo as PostLikeRepository<br/>& PostRepository
participant DB as Database
User->>Controller: toggleLike(postId, userId)
Controller->>Service: toggleLike(postId, userId)
Service->>Repo: existsByUserUserIdAndPostPostId()
Repo->>DB: SELECT
alt 좋아요 미등록
DB-->>Repo: false
Repo-->>Service: false
Service->>Repo: save(PostLike)
Service->>Repo: 좋아요 수 + 1
Service->>Repo: save(Post)
else 좋아요 등록됨
DB-->>Repo: true
Repo-->>Service: true
Service->>Repo: delete(PostLike)
Service->>Repo: 좋아요 수 - 1
Service->>Repo: save(Post)
end
Repo->>DB: INSERT/UPDATE/DELETE
DB-->>Service: 성공
Service-->>Controller: void
Controller-->>User: 200 OK
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes 추가 검토 필요 영역:
Possibly related PRs
Suggested reviewers
Poem
Pre-merge checks and finishing touches❌ Failed checks (2 warnings)
✅ Passed checks (1 passed)
✨ Finishing touches
🧪 Generate unit tests (beta)
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.
Actionable comments posted: 9
🧹 Nitpick comments (6)
backend/src/main/java/org/sejongisc/backend/board/entity/Post.java (3)
6-6: 사용되지 않는 import를 제거하세요.
GenericGenerator가 코드에서 사용되지 않고 있습니다.GenerationType.UUID를 사용하고 있어 필요하지 않은 import입니다.-import org.hibernate.annotations.GenericGenerator;
26-28: @joincolumn 명시를 권장합니다.User 관계에
@JoinColumn을 명시하지 않으면 JPA가 기본 네이밍을 사용합니다. 명확성을 위해 조인 컬럼을 명시적으로 지정하는 것이 좋습니다.// 작성자 @ManyToOne(fetch = FetchType.LAZY) +@JoinColumn(name = "user_id") private User user;
30-44: enum 필드에 nullable 제약 조건을 명시하세요.
boardType과postType필드에nullable속성이 명시되어 있지 않습니다. 이 필드들이 필수인지 선택적인지 명확하게 표시하는 것이 좋습니다.// 게시판 타입 @Enumerated(EnumType.STRING) +@Column(nullable = false) private BoardType boardType; // 제목 @Column(nullable = false) private String title; // 내용 @Column(columnDefinition = "TEXT", nullable = false) private String content; // 게시글 타입 @Enumerated(EnumType.STRING) +@Column(nullable = false) private PostType postType;backend/src/main/java/org/sejongisc/backend/board/controller/PostController.java (2)
52-59: 반환 타입 일관성을 개선하세요.
deletePost메서드가void를 직접 반환하고 있지만, 같은 컨트롤러의 다른 변경 메서드들(createPost,updatePost,toggleLike,toggleBookmark)은ResponseEntity<Void>를 반환합니다. API 일관성을 위해 동일한 반환 타입을 사용하는 것이 좋습니다.// 게시글 삭제 @DeleteMapping("/{postId}") -public void deletePost( +public ResponseEntity<Void> deletePost( @PathVariable UUID postId, @AuthenticationPrincipal CustomUserDetails customUserDetails) { UUID userId = customUserDetails.getUserId(); postService.deletePost(postId, userId); + return ResponseEntity.ok().build(); }
119-127: 반환 타입 일관성을 개선하세요.
updateComment와deleteComment메서드들이void를 직접 반환하고 있지만,createComment는ResponseEntity<Void>를 반환합니다. 일관성을 위해 모든 변경 메서드가 동일한 반환 타입을 사용하도록 수정하세요.// 댓글 수정 @PutMapping("/comment/{commentId}") -public void updateComment( +public ResponseEntity<Void> updateComment( @PathVariable UUID commentId, @RequestBody CommentRequest request, @AuthenticationPrincipal CustomUserDetails customUserDetails) { UUID userId = customUserDetails.getUserId(); postService.updateComment(request, commentId, userId); + return ResponseEntity.ok().build(); } // 댓글 삭제 @DeleteMapping("/comment/{commentId}") -public void deleteComment( +public ResponseEntity<Void> deleteComment( @PathVariable UUID commentId, @AuthenticationPrincipal CustomUserDetails customUserDetails) { UUID userId = customUserDetails.getUserId(); postService.deleteComment(commentId, userId); + return ResponseEntity.ok().build(); }backend/src/main/java/org/sejongisc/backend/board/service/PostServiceImpl.java (1)
139-142: JPA cascade 설정을 사용하여 관련 엔티티 삭제를 자동화하세요.댓글, 좋아요, 북마크를 수동으로 삭제하고 있습니다. JPA의 cascade 설정을 활용하면 코드가 더 간결해지고 실수를 줄일 수 있습니다.
Post엔티티에 다음과 같은 관계를 추가하세요:@OneToMany(cascade = CascadeType.ALL, orphanRemoval = true) @JoinColumn(name = "post_id") private List<Comment> comments = new ArrayList<>(); @OneToMany(cascade = CascadeType.ALL, orphanRemoval = true) @JoinColumn(name = "post_id") private List<PostLike> likes = new ArrayList<>(); @OneToMany(cascade = CascadeType.ALL, orphanRemoval = true) @JoinColumn(name = "post_id") private List<PostBookmark> bookmarks = new ArrayList<>(); @OneToMany(cascade = CascadeType.ALL, orphanRemoval = true) @JoinColumn(name = "post_id") private List<PostAttachment> attachments = new ArrayList<>();그러면
deletePost메서드에서 수동 삭제 코드를 제거할 수 있습니다.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (28)
backend/src/main/java/org/sejongisc/backend/board/controller/PostController.java(1 hunks)backend/src/main/java/org/sejongisc/backend/board/dto/CommentRequest.java(1 hunks)backend/src/main/java/org/sejongisc/backend/board/dto/CommentResponse.java(1 hunks)backend/src/main/java/org/sejongisc/backend/board/dto/PostAttachmentResponse.java(1 hunks)backend/src/main/java/org/sejongisc/backend/board/dto/PostRequest.java(1 hunks)backend/src/main/java/org/sejongisc/backend/board/dto/PostResponse.java(1 hunks)backend/src/main/java/org/sejongisc/backend/board/dto/PostSummaryResponse.java(1 hunks)backend/src/main/java/org/sejongisc/backend/board/entity/BoardType.java(1 hunks)backend/src/main/java/org/sejongisc/backend/board/entity/Comment.java(1 hunks)backend/src/main/java/org/sejongisc/backend/board/entity/Post.java(1 hunks)backend/src/main/java/org/sejongisc/backend/board/entity/PostAttachment.java(1 hunks)backend/src/main/java/org/sejongisc/backend/board/entity/PostBookmark.java(1 hunks)backend/src/main/java/org/sejongisc/backend/board/entity/PostLike.java(1 hunks)backend/src/main/java/org/sejongisc/backend/board/entity/PostType.java(1 hunks)backend/src/main/java/org/sejongisc/backend/board/repository/CommentRepository.java(1 hunks)backend/src/main/java/org/sejongisc/backend/board/repository/PostAttachmentRepository.java(1 hunks)backend/src/main/java/org/sejongisc/backend/board/repository/PostBookmarkRepository.java(1 hunks)backend/src/main/java/org/sejongisc/backend/board/repository/PostLikeRepository.java(1 hunks)backend/src/main/java/org/sejongisc/backend/board/repository/PostRepository.java(1 hunks)backend/src/main/java/org/sejongisc/backend/board/service/FileUploadService.java(1 hunks)backend/src/main/java/org/sejongisc/backend/board/service/PostService.java(1 hunks)backend/src/main/java/org/sejongisc/backend/board/service/PostServiceImpl.java(1 hunks)backend/src/main/java/org/sejongisc/backend/common/config/PrimaryDataSourceConfig.java(1 hunks)backend/src/main/java/org/sejongisc/backend/common/exception/ErrorCode.java(1 hunks)backend/src/main/java/org/sejongisc/backend/user/entity/User.java(2 hunks)backend/src/test/java/org/sejongisc/backend/backtest/controller/BacktestControllerTest.java(1 hunks)backend/src/test/java/org/sejongisc/backend/board/service/FileUploadServiceTest.java(1 hunks)backend/src/test/java/org/sejongisc/backend/board/service/PostServiceImplTest.java(1 hunks)
🧰 Additional context used
🧬 Code graph analysis (14)
backend/src/main/java/org/sejongisc/backend/board/entity/PostLike.java (2)
backend/src/main/java/org/sejongisc/backend/board/entity/Post.java (1)
Entity(12-57)backend/src/main/java/org/sejongisc/backend/board/entity/PostBookmark.java (1)
Entity(10-27)
backend/src/main/java/org/sejongisc/backend/board/entity/PostBookmark.java (3)
backend/src/main/java/org/sejongisc/backend/board/entity/Comment.java (1)
Entity(11-31)backend/src/main/java/org/sejongisc/backend/board/entity/Post.java (1)
Entity(12-57)backend/src/main/java/org/sejongisc/backend/board/entity/PostLike.java (1)
Entity(10-27)
backend/src/main/java/org/sejongisc/backend/board/entity/Comment.java (4)
backend/src/main/java/org/sejongisc/backend/board/entity/Post.java (1)
Entity(12-57)backend/src/main/java/org/sejongisc/backend/user/entity/User.java (1)
Entity(13-67)backend/src/main/java/org/sejongisc/backend/board/entity/PostBookmark.java (1)
Entity(10-27)backend/src/main/java/org/sejongisc/backend/board/entity/PostLike.java (1)
Entity(10-27)
backend/src/main/java/org/sejongisc/backend/board/dto/PostResponse.java (2)
backend/src/main/java/org/sejongisc/backend/board/dto/PostAttachmentResponse.java (1)
Getter(10-26)backend/src/main/java/org/sejongisc/backend/board/dto/CommentResponse.java (1)
ToString(14-36)
backend/src/main/java/org/sejongisc/backend/board/entity/PostAttachment.java (5)
backend/src/main/java/org/sejongisc/backend/board/entity/Comment.java (1)
Entity(11-31)backend/src/main/java/org/sejongisc/backend/board/entity/Post.java (1)
Entity(12-57)backend/src/main/java/org/sejongisc/backend/board/entity/PostBookmark.java (1)
Entity(10-27)backend/src/main/java/org/sejongisc/backend/board/entity/PostLike.java (1)
Entity(10-27)backend/src/main/java/org/sejongisc/backend/board/dto/PostAttachmentResponse.java (1)
Getter(10-26)
backend/src/test/java/org/sejongisc/backend/board/service/PostServiceImplTest.java (1)
backend/src/test/java/org/sejongisc/backend/board/service/FileUploadServiceTest.java (1)
ExtendWith(20-98)
backend/src/main/java/org/sejongisc/backend/board/dto/CommentRequest.java (3)
backend/src/main/java/org/sejongisc/backend/board/dto/PostRequest.java (1)
ToString(16-37)backend/src/main/java/org/sejongisc/backend/board/dto/PostResponse.java (1)
ToString(19-40)backend/src/main/java/org/sejongisc/backend/board/dto/CommentResponse.java (1)
ToString(14-36)
backend/src/main/java/org/sejongisc/backend/board/dto/PostRequest.java (2)
backend/src/main/java/org/sejongisc/backend/board/dto/PostAttachmentResponse.java (1)
Getter(10-26)backend/src/main/java/org/sejongisc/backend/board/dto/PostResponse.java (1)
ToString(19-40)
backend/src/main/java/org/sejongisc/backend/board/controller/PostController.java (1)
backend/src/main/java/org/sejongisc/backend/common/auth/springsecurity/CustomUserDetailsService.java (1)
RequiredArgsConstructor(16-38)
backend/src/main/java/org/sejongisc/backend/board/service/PostServiceImpl.java (1)
backend/src/main/java/org/sejongisc/backend/board/service/FileUploadService.java (1)
Service(16-92)
backend/src/main/java/org/sejongisc/backend/board/dto/CommentResponse.java (2)
backend/src/main/java/org/sejongisc/backend/board/dto/CommentRequest.java (1)
ToString(13-26)backend/src/main/java/org/sejongisc/backend/board/dto/PostResponse.java (1)
ToString(19-40)
backend/src/main/java/org/sejongisc/backend/board/service/FileUploadService.java (1)
backend/src/main/java/org/sejongisc/backend/board/service/PostServiceImpl.java (1)
Service(24-368)
backend/src/main/java/org/sejongisc/backend/board/entity/Post.java (5)
backend/src/main/java/org/sejongisc/backend/board/entity/Comment.java (1)
Entity(11-31)backend/src/main/java/org/sejongisc/backend/user/entity/User.java (1)
Entity(13-67)backend/src/main/java/org/sejongisc/backend/board/entity/PostAttachment.java (1)
Entity(10-33)backend/src/main/java/org/sejongisc/backend/board/entity/PostBookmark.java (1)
Entity(10-27)backend/src/main/java/org/sejongisc/backend/board/entity/PostLike.java (1)
Entity(10-27)
backend/src/test/java/org/sejongisc/backend/board/service/FileUploadServiceTest.java (1)
backend/src/test/java/org/sejongisc/backend/board/service/PostServiceImplTest.java (1)
ExtendWith(31-288)
🔇 Additional comments (19)
backend/src/test/java/org/sejongisc/backend/backtest/controller/BacktestControllerTest.java (9)
1-207: PR 요약과 파일 내용 간의 불일치PR 제목과 요약에서는 "게시물목록조회" 및 Post/Comment 엔티티를 포함한 게시판 기능 구현을 언급하고 있으나, 이 파일은 BacktestController 테스트로 게시판 기능과 전혀 무관합니다. 잘못된 브랜치에 커밋되었거나 PR 설명이 잘못된 것으로 보입니다.
43-56: 테스트 클래스 설정이 적절합니다
@WebMvcTest와 SecurityConfig import, 필요한 MockBean 선언이 올바르게 구성되어 있습니다.
59-68: 인증 토큰 헬퍼 메서드가 잘 구현되었습니다테스트 전반에서 재사용 가능한 인증 토큰 생성 로직을 헬퍼 메서드로 추출하여 중복을 제거한 점이 좋습니다.
96-117: 상세 조회 테스트가 적절합니다서비스 모킹과 응답 검증이 올바르게 구현되어 있습니다.
119-141: 백테스트 실행 테스트가 잘 작성되었습니다요청 본문 직렬화, 인증 헤더 설정, 응답 검증이 모두 적절합니다.
143-155: 삭제 테스트가 올바르게 구현되었습니다void 메서드에 대한
doNothing()사용과 204 상태 코드 검증이 적절합니다.
157-177: 템플릿 저장 테스트가 적절합니다PATCH 엔드포인트 테스트가 올바르게 구현되어 있습니다.
179-198: 템플릿 삭제 테스트가 적절합니다서비스 메서드 모킹과 204 응답 검증이 올바릅니다.
200-206: 테스트 설정이 올바릅니다JPA Auditing을 위한 고정된 auditor 제공자 구현이 적절합니다.
backend/src/main/java/org/sejongisc/backend/common/config/PrimaryDataSourceConfig.java (1)
66-66: LGTM!board 엔티티 패키지를 JPA 스캔 대상에 올바르게 추가했습니다.
backend/src/main/java/org/sejongisc/backend/board/dto/PostSummaryResponse.java (1)
9-17: LGTM!게시물 요약 정보를 담는 DTO 구조가 간결하고 적절합니다.
backend/src/main/java/org/sejongisc/backend/board/entity/PostType.java (1)
3-6: LGTM!게시글 타입을 나타내는 enum이 명확하게 정의되었습니다.
backend/src/main/java/org/sejongisc/backend/user/entity/User.java (1)
3-3: LGTM!Hibernate 지연 로딩 프록시의 JSON 직렬화 문제를 올바르게 처리했습니다. board 기능에서 User 엔티티가 응답 DTO에 포함될 때 발생할 수 있는 직렬화 오류를 방지합니다.
Also applies to: 20-20
backend/src/main/java/org/sejongisc/backend/board/entity/BoardType.java (1)
3-7: LGTM!게시판 타입을 나타내는 enum이 명확하게 정의되었습니다.
backend/src/main/java/org/sejongisc/backend/board/dto/PostAttachmentResponse.java (1)
10-26: LGTM!정적 팩토리 메서드를 사용한 DTO 변환 패턴이 깔끔하고 적절합니다.
backend/src/main/java/org/sejongisc/backend/board/service/PostServiceImpl.java (4)
37-70: 게시물 저장 로직이 잘 구현되었습니다.게시물을 먼저 저장하여 ID를 생성한 후 첨부파일을 연결하는 로직이 올바르게 구현되어 있습니다. null 체크도 적절히 수행되고 있습니다.
148-179: 게시물 조회 및 검색 로직이 잘 구현되었습니다.페이징, 정렬, DTO 매핑이 올바르게 구현되어 있습니다.
274-282: 관리자 권한 확인 로직이 잘 구현되었습니다.관리자(PRESIDENT, VICE_PRESIDENT)가 다른 사용자의 댓글을 삭제할 수 있도록 하는 로직이 적절히 구현되어 있습니다.
247-248: 댓글 수 증가 시 동시성 문제를 명확히 해야 합니다.
commentCount를 수동으로 증가시킬 때 동시성 제어가 없습니다.@Transactional어노테이션은 각 메서드 호출별로 별도의 트랜잭션을 생성하기 때문에, 다음과 같은 레이스 컨디션이 발생합니다:
- 스레드 A: post.getCommentCount() = 5 읽음
- 스레드 B: post.getCommentCount() = 5 읽음
- 스레드 A: 6으로 설정 후 커밋
- 스레드 B: 6으로 설정 후 커밋 (예상값: 7)
동일한 문제가
deleteComment()에서도 발생합니다(라인 288). Post 엔티티에@Version필드나 데이터베이스 레벨의 원자적 업데이트(UPDATE ... SET count = count + 1)가 없으므로, 여러 사용자가 동시에 댓글을 작성/삭제할 때 정확한 카운트를 보장할 수 없습니다.동시에 여러 댓글이 생성되는 시나리오에 대한 통합 테스트를 수행하여 카운트 정합성을 검증하고, 데이터베이스 레벨의 원자적 작업 또는 낙관적 잠금(
@Version) 도입을 고려하세요.
backend/src/main/java/org/sejongisc/backend/board/entity/Comment.java
Outdated
Show resolved
Hide resolved
backend/src/main/java/org/sejongisc/backend/board/entity/PostBookmark.java
Outdated
Show resolved
Hide resolved
backend/src/main/java/org/sejongisc/backend/board/entity/PostLike.java
Outdated
Show resolved
Hide resolved
backend/src/main/java/org/sejongisc/backend/board/service/FileUploadService.java
Show resolved
Hide resolved
backend/src/main/java/org/sejongisc/backend/board/service/PostServiceImpl.java
Outdated
Show resolved
Hide resolved
backend/src/main/java/org/sejongisc/backend/board/service/PostServiceImpl.java
Show resolved
Hide resolved
backend/src/main/java/org/sejongisc/backend/board/service/PostServiceImpl.java
Outdated
Show resolved
Hide resolved
backend/src/test/java/org/sejongisc/backend/backtest/controller/BacktestControllerTest.java
Outdated
Show resolved
Hide resolved
There was a problem hiding this comment.
Actionable comments posted: 7
♻️ Duplicate comments (3)
backend/src/main/java/org/sejongisc/backend/board/entity/PostBookmark.java (1)
11-28: 중복 북마크 방지를 위한 유니크 제약 조건 추가 필요이전 리뷰에서 지적된 내용과 동일하게, (post, user) 조합에 대한 데이터베이스 레벨의 유니크 제약 조건이 필요합니다. 동시 토글 요청 시 중복 레코드가 생성되어
bookmarkCount집계가 틀어질 수 있습니다.다음과 같이 수정하세요:
-@Entity -@Getter -@Setter -@NoArgsConstructor -@AllArgsConstructor -@Builder -public class PostBookmark extends BasePostgresEntity { +@Entity +@Table( + name = "post_bookmark", + uniqueConstraints = @UniqueConstraint( + name = "uk_post_bookmark_post_user", + columnNames = {"post_id", "user_id"})) +@Getter +@Setter +@NoArgsConstructor +@AllArgsConstructor +@Builder +public class PostBookmark extends BasePostgresEntity { @Id @GeneratedValue(strategy = GenerationType.UUID) private UUID postBookmarkId; - @ManyToOne(fetch = FetchType.LAZY) + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "post_id") private Post post; - @ManyToOne(fetch = FetchType.LAZY) + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "user_id") private User user;backend/src/main/java/org/sejongisc/backend/board/service/PostServiceImpl.java (2)
90-96: 이미 지적된 첨부파일 업데이트 문제입니다.기존 리뷰에서 이미 첨부파일을 전체 삭제 후 재업로드하는 파괴적 로직이 지적되었습니다. 선택적 삭제 및 추가 방식으로 개선이 필요합니다.
303-303: 이미 지적된 댓글 수 감소 시 동시성 문제입니다.기존 리뷰에서 이미
commentCount감소 시 race condition 문제가 지적되었습니다.createComment와 동일한 방식으로 원자적 업데이트로 개선이 필요합니다.
🧹 Nitpick comments (8)
backend/src/main/java/org/sejongisc/backend/board/entity/PostAttachment.java (1)
5-9: 사용되지 않는 import 제거CreationTimestamp, LocalDateTime, BasePostgresEntity, User 등의 import가 실제로 사용되지 않습니다.
backend/src/main/java/org/sejongisc/backend/board/entity/PostLike.java (1)
5-9: 사용되지 않는 import 제거CreationTimestamp, LocalDateTime import가 실제로 사용되지 않습니다.
backend/src/main/java/org/sejongisc/backend/board/entity/PostBookmark.java (1)
5-9: 사용되지 않는 import 제거CreationTimestamp, LocalDateTime import가 실제로 사용되지 않습니다.
backend/src/main/java/org/sejongisc/backend/board/entity/Comment.java (1)
5-9: 사용되지 않는 import 제거CreationTimestamp, LocalDateTime import가 실제로 사용되지 않습니다.
backend/src/main/java/org/sejongisc/backend/board/repository/PostLikeRepository.java (1)
13-15: 메서드 파라미터 순서 불일치Line 13의
existsByUserUserIdAndPostPostId(UUID userId, UUID postId)와 Line 15의findByPostPostIdAndUserUserId(UUID postId, UUID userId)에서 파라미터 순서가 다릅니다. 메서드 이름의 순서와 파라미터 순서를 일치시키는 것이 좋지만, 호출하는 측에서 일관된 순서를 사용하도록 주의가 필요합니다.backend/src/main/java/org/sejongisc/backend/board/repository/PostBookmarkRepository.java (1)
13-15: 메서드 파라미터 순서 불일치PostLikeRepository와 동일하게, Line 13의
existsByUserUserIdAndPostPostId(UUID userId, UUID postId)와 Line 15의findByPostPostIdAndUserUserId(UUID postId, UUID userId)에서 파라미터 순서가 다릅니다. 일관성을 위해 동일한 순서를 사용하는 것을 권장합니다.backend/src/main/java/org/sejongisc/backend/board/repository/CommentRepository.java (1)
14-16: 중복된 메서드 정의
findByPostPostId와findAllByPostPostId는 Spring Data JPA에서 동일한 쿼리를 생성합니다.findBy와findAllBy는 List 반환 타입에서 차이가 없으므로, 둘 중 하나만 유지하는 것이 좋습니다.다음과 같이 하나의 메서드만 유지하세요:
- List<Comment> findByPostPostId(UUID postId); - List<Comment> findAllByPostPostId(UUID postId);backend/src/main/java/org/sejongisc/backend/board/service/PostServiceImpl.java (1)
27-30: 클래스 레벨@Transactional과 메서드 레벨@Transactional이 중복됩니다.클래스 레벨에
@Transactional을 선언하면 모든 public 메서드가 기본적으로 트랜잭션을 갖게 됩니다. 일부 메서드(예:savePost,updatePost,deletePost)에서 메서드 레벨@Transactional을 중복으로 선언하고 있어 불필요합니다.다음 중 하나를 선택하는 것을 권장합니다:
- 클래스 레벨
@Transactional을 제거하고 각 메서드에 명시적으로 선언- 메서드 레벨의 중복
@Transactional제거 (단,readOnly = true는 유지)
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (12)
backend/src/main/java/org/sejongisc/backend/board/dto/CommentResponse.java(1 hunks)backend/src/main/java/org/sejongisc/backend/board/entity/Comment.java(1 hunks)backend/src/main/java/org/sejongisc/backend/board/entity/Post.java(1 hunks)backend/src/main/java/org/sejongisc/backend/board/entity/PostAttachment.java(1 hunks)backend/src/main/java/org/sejongisc/backend/board/entity/PostBookmark.java(1 hunks)backend/src/main/java/org/sejongisc/backend/board/entity/PostLike.java(1 hunks)backend/src/main/java/org/sejongisc/backend/board/repository/CommentRepository.java(1 hunks)backend/src/main/java/org/sejongisc/backend/board/repository/PostAttachmentRepository.java(1 hunks)backend/src/main/java/org/sejongisc/backend/board/repository/PostBookmarkRepository.java(1 hunks)backend/src/main/java/org/sejongisc/backend/board/repository/PostLikeRepository.java(1 hunks)backend/src/main/java/org/sejongisc/backend/board/service/PostServiceImpl.java(1 hunks)backend/src/test/java/org/sejongisc/backend/board/service/PostServiceImplTest.java(1 hunks)
🚧 Files skipped from review as they are similar to previous changes (3)
- backend/src/main/java/org/sejongisc/backend/board/entity/Post.java
- backend/src/main/java/org/sejongisc/backend/board/repository/PostAttachmentRepository.java
- backend/src/test/java/org/sejongisc/backend/board/service/PostServiceImplTest.java
🧰 Additional context used
🧬 Code graph analysis (6)
backend/src/main/java/org/sejongisc/backend/board/entity/PostBookmark.java (4)
backend/src/main/java/org/sejongisc/backend/board/entity/Comment.java (1)
Entity(11-31)backend/src/main/java/org/sejongisc/backend/board/entity/Post.java (1)
Entity(12-60)backend/src/main/java/org/sejongisc/backend/board/entity/PostAttachment.java (1)
Entity(11-34)backend/src/main/java/org/sejongisc/backend/board/entity/PostLike.java (1)
Entity(11-28)
backend/src/main/java/org/sejongisc/backend/board/entity/PostAttachment.java (2)
backend/src/main/java/org/sejongisc/backend/board/entity/Post.java (1)
Entity(12-60)backend/src/main/java/org/sejongisc/backend/board/dto/PostAttachmentResponse.java (1)
Getter(10-26)
backend/src/main/java/org/sejongisc/backend/board/dto/CommentResponse.java (2)
backend/src/main/java/org/sejongisc/backend/board/dto/CommentRequest.java (1)
ToString(13-26)backend/src/main/java/org/sejongisc/backend/board/dto/PostResponse.java (1)
ToString(19-40)
backend/src/main/java/org/sejongisc/backend/board/service/PostServiceImpl.java (1)
backend/src/main/java/org/sejongisc/backend/board/service/FileUploadService.java (1)
Service(16-92)
backend/src/main/java/org/sejongisc/backend/board/entity/PostLike.java (3)
backend/src/main/java/org/sejongisc/backend/board/entity/Comment.java (1)
Entity(11-31)backend/src/main/java/org/sejongisc/backend/board/entity/Post.java (1)
Entity(12-60)backend/src/main/java/org/sejongisc/backend/board/entity/PostBookmark.java (1)
Entity(11-28)
backend/src/main/java/org/sejongisc/backend/board/entity/Comment.java (2)
backend/src/main/java/org/sejongisc/backend/board/entity/Post.java (1)
Entity(12-60)backend/src/main/java/org/sejongisc/backend/user/entity/User.java (1)
Entity(13-67)
🔇 Additional comments (9)
backend/src/main/java/org/sejongisc/backend/board/dto/CommentResponse.java (1)
27-35: Null 참조 가능성 검토 필요Line 30에서
comment.getPost().getPostId()를 호출할 때, post가 null이면 NullPointerException이 발생할 수 있습니다. Comment 엔티티의 post 필드가 항상 non-null임을 보장하려면@ManyToOne(optional = false)를 사용하거나, 여기서 null 체크를 추가하는 것을 고려하세요.Comment 엔티티의 post 관계가 필수인지 확인하세요.
backend/src/main/java/org/sejongisc/backend/board/entity/Comment.java (1)
23-27: JPA 관계 매핑이 올바르게 구현됨Post와 User 모두에 대해
@ManyToOne관계를 사용하여 데이터베이스 참조 무결성이 보장되며, 다른 엔티티들과 일관된 패턴을 유지하고 있습니다.backend/src/main/java/org/sejongisc/backend/board/service/PostServiceImpl.java (7)
1-26: 임포트가 적절합니다.필요한 Spring Data JPA, 트랜잭션, 재시도 관련 의존성이 올바르게 임포트되어 있습니다.
40-73: 게시물 작성 로직이 올바릅니다.Post 엔티티 생성, 파일 업로드, PostAttachment 저장 흐름이 적절하게 구현되어 있습니다. 파일 null 체크도 잘 되어 있습니다.
151-165: 게시물 목록 조회 로직이 올바릅니다.페이징, 정렬, 게시판 타입별 필터링이 적절하게 구현되어 있습니다.
167-182: 게시물 검색 로직이 올바릅니다.제목과 내용을 대소문자 구분 없이 검색하는 로직이 적절하게 구현되어 있습니다.
184-227: 게시물 상세 조회 로직이 올바릅니다.게시물, 댓글 페이징, 첨부파일 조회가 적절하게 구현되어 있으며, DTO 변환도 올바르게 처리되고 있습니다.
259-274: 댓글 수정 로직이 올바릅니다.작성자 확인 및 내용 업데이트가 적절하게 구현되어 있습니다.
379-393: 매핑 로직이 올바릅니다.이전 리뷰에서 언급된 N+1 문제와 관련하여: 현재
mapToPostResponse메서드는 첨부파일을 조회하지 않고 Post 엔티티의 필드만 사용하여 PostResponse를 생성합니다. 첨부파일은getPostDetail메서드에서만 별도로 조회되며, 이는 단일 게시물 조회이므로 N+1 문제가 발생하지 않습니다.현재 구현은 올바릅니다.
backend/src/main/java/org/sejongisc/backend/board/entity/PostAttachment.java
Show resolved
Hide resolved
backend/src/main/java/org/sejongisc/backend/board/service/PostServiceImpl.java
Show resolved
Hide resolved
backend/src/main/java/org/sejongisc/backend/board/service/PostServiceImpl.java
Show resolved
Hide resolved
backend/src/main/java/org/sejongisc/backend/board/service/PostServiceImpl.java
Show resolved
Hide resolved
backend/src/main/java/org/sejongisc/backend/board/service/PostServiceImpl.java
Show resolved
Hide resolved
backend/src/main/java/org/sejongisc/backend/board/service/PostServiceImpl.java
Show resolved
Hide resolved
There was a problem hiding this comment.
Actionable comments posted: 1
♻️ Duplicate comments (7)
backend/src/main/java/org/sejongisc/backend/board/service/PostServiceImpl.java (7)
77-77: 클래스 레벨의 @transactional과 중복됩니다.29번 라인의 클래스 레벨 어노테이션으로 이미 트랜잭션이 적용되므로 중복입니다.
123-123: 클래스 레벨의 @transactional과 중복됩니다.29번 라인의 클래스 레벨 어노테이션으로 이미 트랜잭션이 적용되므로 중복입니다.
234-234: 클래스 레벨의 @transactional과 중복됩니다.29번 라인의 클래스 레벨 어노테이션으로 이미 트랜잭션이 적용되므로 중복입니다.
264-264: 클래스 레벨의 @transactional과 중복됩니다.29번 라인의 클래스 레벨 어노테이션으로 이미 트랜잭션이 적용되므로 중복입니다.
281-281: 클래스 레벨의 @transactional과 중복됩니다.29번 라인의 클래스 레벨 어노테이션으로 이미 트랜잭션이 적용되므로 중복입니다.
314-314: 클래스 레벨의 @transactional과 중복됩니다.29번 라인의 클래스 레벨 어노테이션으로 이미 트랜잭션이 적용되므로 중복입니다.
349-349: 클래스 레벨의 @transactional과 중복됩니다.29번 라인의 클래스 레벨 어노테이션으로 이미 트랜잭션이 적용되므로 중복입니다.
🧹 Nitpick comments (3)
backend/src/main/java/org/sejongisc/backend/board/service/PostServiceImpl.java (3)
42-42: 클래스 레벨의 @transactional과 중복됩니다.29번 라인에서 이미 클래스 전체에
@Transactional이 선언되어 있으므로, 이 메서드의@Transactional어노테이션은 불필요합니다. 다른 설정(예: readOnly, propagation)이 필요하지 않다면 제거하는 것이 좋습니다.
382-396: 목록 조회 시 첨부파일이 포함되지 않습니다.이 메서드는
PostResponse를 반환하지만attachments필드를 설정하지 않습니다.getPostDetail메서드에서는 첨부파일을 포함하지만, 목록 조회 시에는 포함되지 않습니다.이것이 의도된 동작이라면 (목록 조회 시 성능을 위해 첨부파일 제외) 문제없지만, 다음을 권장합니다:
- 메서드에 주석을 추가하여 의도를 명확히 하거나
- 별도의 DTO(예:
PostSummaryResponse)를 사용하여 목록용과 상세용을 구분참고: 이전 리뷰에서 언급된 N+1 쿼리 문제는 현재 코드에는 해당되지 않습니다. 이 메서드는 첨부파일을 조회하지 않습니다.
56-72: 파일 업로드 검증이 부족합니다.파일 크기 제한이나 허용된 파일 타입 검증이 없습니다. 악의적인 사용자가 대용량 파일이나 실행 파일을 업로드할 수 있습니다.
다음 검증을 추가하는 것을 권장합니다:
- 파일 크기 제한 (개별 파일 및 전체 업로드)
- 파일 확장자/MIME 타입 화이트리스트
- 파일명 sanitization (이미 FileUploadService에서 일부 처리됨)
예시:
private static final long MAX_FILE_SIZE = 10 * 1024 * 1024; // 10MB private static final Set<String> ALLOWED_EXTENSIONS = Set.of("jpg", "jpeg", "png", "pdf", "doc", "docx"); private void validateFile(MultipartFile file) { if (file.getSize() > MAX_FILE_SIZE) { throw new CustomException(ErrorCode.FILE_SIZE_EXCEEDED); } String filename = file.getOriginalFilename(); String extension = filename.substring(filename.lastIndexOf(".") + 1).toLowerCase(); if (!ALLOWED_EXTENSIONS.contains(extension)) { throw new CustomException(ErrorCode.INVALID_FILE_TYPE); } }
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (1)
backend/src/main/java/org/sejongisc/backend/board/service/PostServiceImpl.java(1 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
backend/src/main/java/org/sejongisc/backend/board/service/PostServiceImpl.java (1)
backend/src/main/java/org/sejongisc/backend/board/service/FileUploadService.java (1)
Service(16-92)
Summary by CodeRabbit
릴리스 노트
새로운 기능
테스트