Conversation
개요자식 게시판 조회 및 게시판 삭제 기능을 추가하는 변경으로, 새로운 컨트롤러 엔드포인트 두 개, 저장소 쿼리 메서드 세 개, 서비스 메서드 두 개, 그리고 프로젝션 인터페이스 하나를 도입합니다. 변경 사항
시퀀스 다이어그램sequenceDiagram
participant Client
participant BoardController
participant PostService
participant BoardRepository
participant PostRepository
participant Database
Client->>BoardController: DELETE /api/board/{boardId}
BoardController->>PostService: deleteBoard(boardId, userId)
PostService->>PostService: validateBoardOwner(boardId, userId)
PostService->>BoardRepository: findAllByParentBoard_BoardId(boardId)
BoardRepository->>Database: Query child boards
Database-->>BoardRepository: List<Board>
PostService->>PostRepository: findPostIdAndUserIdByBoardId(boardId)
PostRepository->>Database: Query posts with IDs
Database-->>PostRepository: List<PostIdUserIdProjection>
PostService->>Database: Delete posts
PostService->>Database: Delete boards (cascade)
Database-->>PostService: Success
PostService-->>BoardController: Success
BoardController-->>Client: 200 OK
예상 코드 리뷰 난이도🎯 3 (중간) | ⏱️ ~20분
관련 가능성 있는 PR
제안하는 리뷰어
시
Pre-merge checks and finishing touches❌ Failed checks (1 warning)
✅ Passed checks (2 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: 4
🧹 Nitpick comments (7)
backend/src/main/java/org/sejongisc/backend/common/exception/ErrorCode.java (1)
106-108: 불필요한 빈 줄 정리마지막 enum 상수와 필드 선언 사이에 빈 줄이 3개 있습니다. 코드 일관성을 위해 1개로 줄이는 것을 권장합니다.
INVALID_BOARD_TYPE(HttpStatus.BAD_REQUEST, "상위 게시판에는 글을 작성할 수 없습니다."); - - - private final HttpStatus status;backend/src/main/java/org/sejongisc/backend/board/service/PostService.java (1)
36-38: LGTM - 메서드 주석 추가 권장새로운 메서드들이 PR 목표에 맞게 추가되었습니다. 다만, 다른 메서드들과의 일관성을 위해
deleteBoard메서드에도 주석을 추가하는 것을 권장합니다.// 하위 게시판 목록 조회 List<BoardResponse> getChildBoards(); + // 게시판 삭제 void deleteBoard(UUID boardId, UUID boardUserId);backend/src/main/java/org/sejongisc/backend/board/controller/BoardController.java (3)
169-172: 엔드포인트 경로 수정 및 미사용 파라미터 검토 필요.
/childs는 문법적으로 올바르지 않습니다./children으로 변경하는 것이 좋습니다.customUserDetails파라미터가 메서드 내에서 사용되지 않습니다. 인증 강제를 위한 것이라면 문제없지만, 그렇지 않다면 제거를 고려해 주세요.- @GetMapping("/childs") + @GetMapping("/children") public ResponseEntity<List<BoardResponse>> getChildBoards( @AuthenticationPrincipal CustomUserDetails customUserDetails) {
183-188: 응답 타입 일관성 및 반환 메시지 검토.
ResponseEntity<?>는 다른 DELETE 엔드포인트(deletePost,deleteComment)가void를 반환하는 것과 일관성이 없습니다.- 성공 메시지를 문자열로 반환하는 방식은 API 응답 형식의 일관성을 해칠 수 있습니다.
다른 엔드포인트와 일관성을 맞추려면:
- public ResponseEntity<?> deleteBoard( + public ResponseEntity<Void> deleteBoard( @PathVariable UUID boardId, @AuthenticationPrincipal CustomUserDetails customUserDetails) { UUID userId = customUserDetails.getUserId(); postService.deleteBoard(boardId, userId); - return ResponseEntity.ok("게시판 삭제가 완료되었습니다."); + return ResponseEntity.ok().build(); }
267-269: 불필요한 빈 줄 제거.파일 끝에 불필요한 빈 줄이 추가되었습니다.
backend/src/main/java/org/sejongisc/backend/board/service/PostServiceImpl.java (2)
4-4: 사용되지 않는 import.
Map이 import되었지만 이 파일에서 사용되지 않습니다.-import java.util.Map;
193-195:deletePost호출 시 userId 우회 방식에 대한 검토.
deletePost(row.getPostId(), row.getUserId())는 게시물 작성자의 userId를 전달하여 소유권 검사를 우회합니다. 이는 동작하지만,deletePost메서드의 의도와 다르게 사용되어 암묵적인 결합을 만듭니다.장기적으로는 관리자 권한으로 삭제하는 별도의 내부 메서드를 고려해 볼 수 있습니다:
private void deletePostByAdmin(UUID postId) { // 소유권 검사 없이 삭제 로직만 수행 }
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (7)
backend/src/main/java/org/sejongisc/backend/board/controller/BoardController.java(2 hunks)backend/src/main/java/org/sejongisc/backend/board/repository/BoardRepository.java(1 hunks)backend/src/main/java/org/sejongisc/backend/board/repository/PostRepository.java(2 hunks)backend/src/main/java/org/sejongisc/backend/board/repository/projection/PostIdUserIdProjection.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(4 hunks)backend/src/main/java/org/sejongisc/backend/common/exception/ErrorCode.java(2 hunks)
🔇 Additional comments (5)
backend/src/main/java/org/sejongisc/backend/common/exception/ErrorCode.java (1)
90-91: LGTM!새로운 에러 코드가 기존 패턴(
INVALID_POST_OWNER,INVALID_COMMENT_OWNER)과 일관성 있게 추가되었습니다. 게시판 수정/삭제 권한 검증에 적절한HttpStatus.FORBIDDEN상태를 사용하고 있습니다.backend/src/main/java/org/sejongisc/backend/board/repository/PostRepository.java (1)
29-34: LGTM!프로젝션 기반 쿼리가 적절하게 구현되었습니다. 전체
Post엔티티 대신 필요한 필드(postId,userId)만 조회하여 성능상 이점이 있습니다. JPQL 문법과@Param어노테이션 사용이 올바릅니다.backend/src/main/java/org/sejongisc/backend/board/repository/projection/PostIdUserIdProjection.java (1)
1-8: LGTM!Spring Data JPA 인터페이스 기반 프로젝션 패턴을 올바르게 따르고 있습니다. 메서드명이 JPQL 쿼리의 alias(
postId,userId)와 정확히 일치하여 프로젝션 매핑이 정상 동작합니다.backend/src/main/java/org/sejongisc/backend/board/repository/BoardRepository.java (1)
11-12: LGTM!Spring Data JPA 파생 쿼리 네이밍 규칙을 올바르게 따르고 있습니다.
findAllByParentBoardIsNotNull()은 하위 게시판 목록 조회에,findAllByParentBoard_BoardId()는 특정 부모의 하위 게시판 조회(cascade 삭제 시)에 적절하게 사용될 것입니다.backend/src/main/java/org/sejongisc/backend/board/service/PostServiceImpl.java (1)
186-190: I attempted to verify the review comment by examining the Board entity, deletion logic, and hierarchy constraints, but encountered repository access issues that prevented code inspection.Cannot verify multi-level board hierarchy concern due to inaccessible codebase.
The original review raises a valid architectural question: whether the current deletion logic (which only retrieves direct child boards) properly handles board hierarchies deeper than 2 levels. Without access to the codebase, I cannot definitively determine:
- Whether the Board entity enforces depth constraints
- If cascade deletion is properly configured in the JPA/Hibernate mapping
- Whether recursive deletion logic exists elsewhere
- The intended design (2-level vs. multi-level support)
The concern remains valid and requires manual verification of the Board entity's
parentBoardrelationship configuration and the complete deletion flow logic.
| } | ||
|
|
||
|
|
||
| // 게시판 생성 |
There was a problem hiding this comment.
주석이 잘못되었습니다.
주석이 "게시판 생성"으로 되어 있으나, 실제로는 "하위 게시판 목록 조회" 기능입니다. 복사-붙여넣기 실수로 보입니다.
- // 게시판 생성
+ // 하위 게시판 목록 조회📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| // 게시판 생성 | |
| // 하위 게시판 목록 조회 |
🤖 Prompt for AI Agents
In
backend/src/main/java/org/sejongisc/backend/board/controller/BoardController.java
around line 164, the inline comment currently reads "게시판 생성" but the method
actually implements "하위 게시판 목록 조회"; update the comment to accurately describe
the method (e.g., "하위 게시판 목록 조회" or the appropriate Javadoc/mapping
description), and scan nearby comments for any other copy-paste mismatches to
correct them as well.
| return ResponseEntity.ok(postService.getChildBoards()); | ||
| } | ||
|
|
||
| // 게시글 삭제 |
There was a problem hiding this comment.
주석이 잘못되었습니다.
주석이 "게시글 삭제"로 되어 있으나, 실제로는 "게시판 삭제" 기능입니다.
- // 게시글 삭제
+ // 게시판 삭제📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| // 게시글 삭제 | |
| // 게시판 삭제 |
🤖 Prompt for AI Agents
In
backend/src/main/java/org/sejongisc/backend/board/controller/BoardController.java
around line 175, the inline comment incorrectly reads "게시글 삭제" but the method
implements deletion of a board; update the comment to "게시판 삭제" (or a more
descriptive "게시판 삭제 처리" / "Delete board") to accurately reflect the
functionality and keep any adjacent JavaDoc or method-level comments consistent.
| @Transactional | ||
| public void deleteBoard(UUID boardId, UUID boardUserId) { | ||
| User user = userRepository.findById(boardUserId).orElseThrow(() -> new CustomException(ErrorCode.USER_NOT_FOUND)); | ||
| if(!user.getRole().equals(Role.PRESIDENT)){ | ||
| throw new CustomException(ErrorCode.INVALID_BOARD_OWNER); | ||
| } | ||
| //상위 게시판이면 하위 게시판 목록을 조회 | ||
| // 1. 부모 + 자식 boardId 목록 만들기 | ||
| List<UUID> targetBoardIds = Stream.concat( | ||
| Stream.of(boardId), // 자신 포함 | ||
| boardRepository.findAllByParentBoard_BoardId(boardId).stream() | ||
| .map(Board::getBoardId) | ||
| ).toList(); | ||
|
|
||
| // 2. 각 boardId마다 postId/userId 조회해서 삭제 | ||
| targetBoardIds.stream() | ||
| .flatMap(id -> postRepository.findPostIdAndUserIdByBoardId(id).stream()) | ||
| .forEach(row -> deletePost(row.getPostId(), row.getUserId())); | ||
| targetBoardIds.forEach(boardRepository::deleteById); | ||
| return; | ||
| } |
There was a problem hiding this comment.
🛠️ Refactor suggestion | 🟠 Major
@Override 어노테이션 누락 및 불필요한 return 문.
deleteBoard 메서드에 @Override 어노테이션이 누락되었고, line 197의 return; 문은 불필요합니다.
+ @Override
@Transactional
public void deleteBoard(UUID boardId, UUID boardUserId) {
// ... existing code ...
targetBoardIds.forEach(boardRepository::deleteById);
- return;
}📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| @Transactional | |
| public void deleteBoard(UUID boardId, UUID boardUserId) { | |
| User user = userRepository.findById(boardUserId).orElseThrow(() -> new CustomException(ErrorCode.USER_NOT_FOUND)); | |
| if(!user.getRole().equals(Role.PRESIDENT)){ | |
| throw new CustomException(ErrorCode.INVALID_BOARD_OWNER); | |
| } | |
| //상위 게시판이면 하위 게시판 목록을 조회 | |
| // 1. 부모 + 자식 boardId 목록 만들기 | |
| List<UUID> targetBoardIds = Stream.concat( | |
| Stream.of(boardId), // 자신 포함 | |
| boardRepository.findAllByParentBoard_BoardId(boardId).stream() | |
| .map(Board::getBoardId) | |
| ).toList(); | |
| // 2. 각 boardId마다 postId/userId 조회해서 삭제 | |
| targetBoardIds.stream() | |
| .flatMap(id -> postRepository.findPostIdAndUserIdByBoardId(id).stream()) | |
| .forEach(row -> deletePost(row.getPostId(), row.getUserId())); | |
| targetBoardIds.forEach(boardRepository::deleteById); | |
| return; | |
| } | |
| @Override | |
| @Transactional | |
| public void deleteBoard(UUID boardId, UUID boardUserId) { | |
| User user = userRepository.findById(boardUserId).orElseThrow(() -> new CustomException(ErrorCode.USER_NOT_FOUND)); | |
| if(!user.getRole().equals(Role.PRESIDENT)){ | |
| throw new CustomException(ErrorCode.INVALID_BOARD_OWNER); | |
| } | |
| //상위 게시판이면 하위 게시판 목록을 조회 | |
| // 1. 부모 + 자식 boardId 목록 만들기 | |
| List<UUID> targetBoardIds = Stream.concat( | |
| Stream.of(boardId), // 자신 포함 | |
| boardRepository.findAllByParentBoard_BoardId(boardId).stream() | |
| .map(Board::getBoardId) | |
| ).toList(); | |
| // 2. 각 boardId마다 postId/userId 조회해서 삭제 | |
| targetBoardIds.stream() | |
| .flatMap(id -> postRepository.findPostIdAndUserIdByBoardId(id).stream()) | |
| .forEach(row -> deletePost(row.getPostId(), row.getUserId())); | |
| targetBoardIds.forEach(boardRepository::deleteById); | |
| } |
🤖 Prompt for AI Agents
In
backend/src/main/java/org/sejongisc/backend/board/service/PostServiceImpl.java
around lines 178 to 198, the deleteBoard method is missing the @Override
annotation and contains an unnecessary trailing "return;" at line 197; add the
@Override annotation immediately above the method signature and remove the
redundant "return;" statement so the method ends naturally after deleting boards
and posts.
| // 하위 게시판 조회 | ||
| @Transactional(readOnly = true) | ||
| public List<BoardResponse> getChildBoards() { | ||
| List<Board> childBoards = boardRepository.findAllByParentBoardIsNotNull(); | ||
|
|
||
| return childBoards.stream() | ||
| .map(BoardResponse::from) | ||
| .toList(); | ||
| } |
There was a problem hiding this comment.
🛠️ Refactor suggestion | 🟠 Major
@Override 어노테이션 누락.
getChildBoards 메서드가 PostService 인터페이스를 구현하고 있다면 @Override 어노테이션이 필요합니다.
// 하위 게시판 조회
+ @Override
@Transactional(readOnly = true)
public List<BoardResponse> getChildBoards() {🤖 Prompt for AI Agents
In
backend/src/main/java/org/sejongisc/backend/board/service/PostServiceImpl.java
around lines 338 to 346, the getChildBoards() method is missing the @Override
annotation; if this method implements PostService, add the @Override annotation
immediately above the method signature to reflect interface implementation and
help the compiler catch signature mismatches.
Summary by CodeRabbit
릴리스 노트
✏️ Tip: You can customize this high-level summary in your review settings.