Skip to content

[feat] 투표 생성 api 개발#46

Merged
seongjunnoh merged 15 commits intodevelopfrom
feat/#37-create-vote
Jul 5, 2025
Merged

[feat] 투표 생성 api 개발#46
seongjunnoh merged 15 commits intodevelopfrom
feat/#37-create-vote

Conversation

@seongjunnoh
Copy link
Collaborator

@seongjunnoh seongjunnoh commented Jul 3, 2025

#️⃣ 연관된 이슈

closes #37

📝 작업 내용

투표 생성 api 를 개발하였습니다.

  • controller

    • request body 유효성 검증 (피그마 화면설계서의 유효성 검증 조건 반영)
    • request body -> command 로 매핑 후, use case 호출
  • application

    • Vote domain 을 생성한 후, Vote 도메인에 대한 유효성 검증을 수행
    • 이때 유효성 검사에 실패할 경우, BusinessException을 extends 하는 InvalidStateException 을 throw
    • 예외 상황의 상세 error message를 클라이언트에게 전달하기 위해, 상세 error message를 포함하는 InvalidStateException 객체를 생성
    • BusinessExceptionHandler 가 이를 catch 하여 클라이언트에게 [error code의 메시지 + 상세 error message]를 응답할 수 있도록 BusinessExceptionHandler 코드 수정
  • 영속성 adapter

    • VoteJpaEntity 저장 후, 이와 연관관계를 가지는 VoteItemJpaEntity 들을 모두 저장

📸 스크린샷

💬 리뷰 요구사항

투표 생성 시, controller단에서의 bean validation을 통해서 검증하기 어려운

  1. 투표를 기록할 페이지가 유효한지
  2. 해당 투표를 총평으로 등록할 수 있는지

에 대한 유효성 검사를 Vote 도메인의 책임으로 위임하여 코드를 구현하였습니다.

이때 유효성 검사 실패의 정확한 원인을 클라이언트가 알면 좋지 않나 싶어서, BusinessException 발생 시 단순히 ErrorCode 의 메시지를 응답하는 기존의 코드를 상세 error message 까지 덧붙여서 응답할 수 있도록 exception handler 의 코드 또한 수정하였습니다.

이 부분 참고해서 리뷰해주시면 감사하겠습니다!


image image

-> 현재 error message 는 위 캡쳐 화면처럼 나옵니다!

📌 PR 진행 시 이러한 점들을 참고해 주세요

* P1 : 꼭 반영해 주세요 (Request Changes) - 이슈가 발생하거나 취약점이 발견되는 케이스 등
* P2 : 반영을 적극적으로 고려해 주시면 좋을 것 같아요 (Comment)
* P3 : 이런 방법도 있을 것 같아요~ 등의 사소한 의견입니다 (Chore)

Summary by CodeRabbit

  • 신규 기능

    • 투표 생성 API 엔드포인트가 추가되어 사용자는 방 내에서 투표를 생성할 수 있습니다.
    • 투표 생성 요청 및 응답을 위한 데이터 구조와 유효성 검사 로직이 도입되었습니다.
    • 투표 항목에 대한 입력값(최대 5개, 항목명 20자 이내 등)에 대한 검증이 적용됩니다.
    • 도서 및 방 조회 기능이 추가되어 관련 엔티티 조회 시 명확한 예외 처리가 이루어집니다.
  • 버그 수정

    • 사용자 회원가입 및 닉네임 검증 요청 시 유효성 검사가 개선되었습니다.
  • 에러 코드 및 예외 처리

    • 방, 투표 등 다양한 상황에 대한 세분화된 에러 코드가 추가되었습니다.
    • 비즈니스 예외 발생 시 상세 메시지가 응답에 포함되어 문제 원인을 더 명확히 확인할 수 있습니다.
  • 테스트

    • 투표 생성 API의 정상 및 실패 케이스에 대한 통합 테스트와, 투표 도메인 유효성 검증에 대한 단위 테스트가 추가되었습니다.
  • 기타

    • 불필요한 더미 클래스 및 인터페이스가 삭제되어 코드가 정리되었습니다.

@seongjunnoh seongjunnoh linked an issue Jul 3, 2025 that may be closed by this pull request
2 tasks
@coderabbitai
Copy link

coderabbitai bot commented Jul 3, 2025

"""

Walkthrough

투표 생성 API를 도입하기 위해 도메인, 어댑터, 서비스, 컨트롤러, DTO, 예외처리 등 전반에 걸쳐 대규모 변경이 이루어졌습니다. 투표 생성 요청 및 검증, 투표/투표항목 저장, 관련 엔터티 조회, 예외 코드 추가, 테스트 코드 작성 등이 포함됩니다.

Changes

파일/경로 그룹 변경 요약
src/main/java/konkuk/thip/vote/adapter/in/web/VoteCommandController.java
src/main/java/konkuk/thip/vote/adapter/in/web/request/VoteCreateRequest.java
src/main/java/konkuk/thip/vote/adapter/in/web/response/VoteCreateResponse.java
투표 생성 API 컨트롤러, 요청/응답 DTO 신설 및 유효성 검증 추가
src/main/java/konkuk/thip/vote/application/port/in/VoteCreateUseCase.java
src/main/java/konkuk/thip/vote/application/port/in/dto/VoteCreateCommand.java
투표 생성 유스케이스 인터페이스 및 커맨드 DTO 도입
src/main/java/konkuk/thip/vote/application/service/VoteService.java 투표 생성 서비스 구현, 투표/투표항목 생성 및 검증 로직 추가
src/main/java/konkuk/thip/vote/domain/Vote.java
src/main/java/konkuk/thip/vote/domain/VoteItem.java
투표 및 투표항목 도메인에 팩토리 메서드 및 검증 로직 추가
src/main/java/konkuk/thip/vote/adapter/out/persistence/VoteCommandPersistenceAdapter.java
src/main/java/konkuk/thip/vote/adapter/out/persistence/VoteItemJpaRepository.java
투표/투표항목 저장 어댑터 및 JPA 레포지토리 추가, 연관 엔터티 조회 및 예외처리
src/main/java/konkuk/thip/vote/application/port/out/VoteCommandPort.java 투표/투표항목 저장 포트 메서드 추가
src/main/java/konkuk/thip/book/adapter/out/persistence/BookCommandPersistenceAdapter.java
src/main/java/konkuk/thip/book/application/port/out/BookCommandPort.java
Book 엔터티 ID 기반 조회 메서드 추가 및 예외처리
src/main/java/konkuk/thip/room/adapter/out/persistence/RoomCommandPersistenceAdapter.java
src/main/java/konkuk/thip/room/application/port/out/RoomCommandPort.java
Room 엔터티 ID 기반 조회 메서드 추가 및 예외처리
src/main/java/konkuk/thip/common/exception/code/ErrorCode.java 투표, 룸, 검색 관련 신규 에러코드 다수 추가
src/main/java/konkuk/thip/common/exception/handler/GlobalExceptionHandler.java 비즈니스 예외 메시지 상세정보 반환 개선
src/main/java/konkuk/thip/user/adapter/in/web/UserCommandController.java @validated@Valid로 검증 어노테이션 변경
src/test/java/konkuk/thip/vote/adapter/in/web/VoteCreateControllerTest.java
src/test/java/konkuk/thip/vote/domain/VoteTest.java
투표 생성 API 통합/유닛 테스트 신설, 다양한 성공/실패 케이스 검증
src/main/java/konkuk/thip/vote/adapter/in/web/request/DummyRequest.java
src/main/java/konkuk/thip/vote/application/port/in/DummyUseCase.java
src/main/java/konkuk/thip/vote/application/port/in/dto/DummyCommand.java
불필요한 더미 클래스/인터페이스 삭제

Sequence Diagram(s)

sequenceDiagram
actor 사용자
participant VoteCommandController
participant VoteService
participant RoomCommandPort
participant BookCommandPort
participant VoteCommandPort

사용자->>VoteCommandController: POST /rooms/{roomId}/vote (VoteCreateRequest)
VoteCommandController->>VoteService: createVote(VoteCreateCommand)
VoteService->>RoomCommandPort: findById(roomId)
VoteService->>BookCommandPort: findById(bookId)
VoteService->>VoteCommandPort: saveVote(Vote)
VoteService->>VoteCommandPort: saveAllVoteItems(List<VoteItem>)
VoteService-->>VoteCommandController: voteId 반환
VoteCommandController-->>사용자: BaseResponse<VoteCreateResponse>
Loading

Assessment against linked issues

Objective Addressed Explanation
투표 생성 API 개발 (#37)

Assessment against linked issues: Out-of-scope changes

Code Change Explanation
UserCommandController의 @validated@Valid 변경 (src/main/java/konkuk/thip/user/adapter/in/web/UserCommandController.java) 투표 생성 API와 무관한 사용자 컨트롤러의 검증 어노테이션 변경입니다.
ErrorCode에 INVALID_SEARCH_TYPE 등 검색/룸 관련 에러코드 추가 (src/main/java/konkuk/thip/common/exception/code/ErrorCode.java) 투표 생성과 직접 관련 없는 에러코드가 포함되어 있습니다.
businessExceptionHandler 상세 메시지 반환 개선 (src/main/java/konkuk/thip/common/exception/handler/GlobalExceptionHandler.java) 투표 생성 API와 직접적인 연관이 없는 전역 예외처리 개선입니다.

Suggested reviewers

  • buzz0331

Poem

투표를 만들었어요, 토끼가 깡총깡총
검증도 꼼꼼히, 예외도 척척!
도메인부터 컨트롤러까지
테스트도 빵빵, 실패도 걱정 마
이제 투표는 문제없지,
🐰 코드 속에 행복이!

(깡총! 투표 생성 API 완성!)
"""


📜 Recent review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 1a01f13 and 69ff3ff.

📒 Files selected for processing (3)
  • src/main/java/konkuk/thip/book/adapter/out/persistence/BookCommandPersistenceAdapter.java (1 hunks)
  • src/main/java/konkuk/thip/book/application/port/out/BookCommandPort.java (1 hunks)
  • src/main/java/konkuk/thip/common/exception/code/ErrorCode.java (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (3)
  • src/main/java/konkuk/thip/book/application/port/out/BookCommandPort.java
  • src/main/java/konkuk/thip/book/adapter/out/persistence/BookCommandPersistenceAdapter.java
  • src/main/java/konkuk/thip/common/exception/code/ErrorCode.java
⏰ Context from checks skipped due to timeout of 90000ms (1)
  • GitHub Check: build
✨ Finishing Touches
  • 📝 Generate Docstrings

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.

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Explain this complex logic.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai explain this code block.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and explain its main purpose.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR.
  • @coderabbitai generate sequence diagram to generate a sequence diagram of the changes in this PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link

@coderabbitai coderabbitai bot left a 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

🧹 Nitpick comments (4)
src/main/java/konkuk/thip/vote/adapter/in/web/VoteCommandController.java (1)

21-21: API 문서화 추가 제안

Swagger/OpenAPI 어노테이션을 추가하면 API 문서화가 개선될 것입니다.

+@Operation(summary = "투표 생성", description = "특정 방에 새로운 투표를 생성합니다")
+@ApiResponses({
+    @ApiResponse(responseCode = "200", description = "투표 생성 성공"),
+    @ApiResponse(responseCode = "400", description = "잘못된 요청"),
+    @ApiResponse(responseCode = "404", description = "방을 찾을 수 없음")
+})
 @PostMapping("/rooms/{roomId}/vote")
src/main/java/konkuk/thip/vote/application/service/VoteService.java (2)

46-53: 빈 투표 항목 리스트 처리 고려

투표 항목이 없는 경우에 대한 처리가 필요할 수 있습니다.

 // 3. vote item 저장
+if (command.voteItemCreateCommands() != null && !command.voteItemCreateCommands().isEmpty()) {
     List<VoteItem> voteItems = command.voteItemCreateCommands().stream()
             .map(itemCmd -> VoteItem.withoutId(
                     itemCmd.itemName(),
                     0,
                     savedVoteId
             ))
             .toList();
     voteCommandPort.saveAllVoteItems(voteItems);
+}

30-56: 로깅 추가로 디버깅 및 모니터링 개선

중요한 비즈니스 로직이므로 로깅을 추가하면 운영 시 도움이 될 것입니다.

 public Long createVote(VoteCreateCommand command) {
+    log.info("투표 생성 시작 - userId: {}, roomId: {}", command.userId(), command.roomId());
     // 1. validate
     Vote vote = Vote.withoutId(
             command.content(),
             command.userId(),
             command.page(),
             command.isOverview(),
             command.roomId()
     );

     validateVote(vote);

     // 2. vote 저장
     Long savedVoteId = voteCommandPort.saveVote(vote);
+    log.info("투표 저장 완료 - voteId: {}", savedVoteId);

     // 3. vote item 저장
     List<VoteItem> voteItems = command.voteItemCreateCommands().stream()
             .map(itemCmd -> VoteItem.withoutId(
                     itemCmd.itemName(),
                     0,
                     savedVoteId
             ))
             .toList();
     voteCommandPort.saveAllVoteItems(voteItems);
+    log.info("투표 항목 저장 완료 - voteId: {}, itemCount: {}", savedVoteId, voteItems.size());

     return savedVoteId;
 }
src/main/java/konkuk/thip/vote/domain/Vote.java (1)

37-46: 총평 검증 로직이 명확합니다

80% 진행률 체크와 구체적인 에러 메시지 제공이 좋습니다. 매직 넘버를 상수로 추출하면 더 좋을 것 같습니다.

+private static final double OVERVIEW_MINIMUM_PROGRESS_RATIO = 0.8;
+
 public void validateOverview(int totalPageCount) {
     double ratio = (double) page / totalPageCount;
-    if (isOverview && ratio < 0.8) {
+    if (isOverview && ratio < OVERVIEW_MINIMUM_PROGRESS_RATIO) {
         String message = String.format(
                 "총평(isOverview)은 진행률이 80%% 이상일 때만 가능합니다. 현재 진행률 = %.2f%% (%d/%d)",
                 ratio * 100, page, totalPageCount
         );
         throw new InvalidStateException(VOTE_CANNOT_BE_OVERVIEW, new IllegalStateException(message));
     }
 }
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 652884f and 1f807d6.

📒 Files selected for processing (23)
  • src/main/java/konkuk/thip/book/adapter/out/persistence/BookCommandPersistenceAdapter.java (2 hunks)
  • src/main/java/konkuk/thip/book/application/port/out/BookCommandPort.java (1 hunks)
  • src/main/java/konkuk/thip/common/exception/code/ErrorCode.java (1 hunks)
  • src/main/java/konkuk/thip/common/exception/handler/GlobalExceptionHandler.java (1 hunks)
  • src/main/java/konkuk/thip/room/adapter/out/persistence/RoomCommandPersistenceAdapter.java (1 hunks)
  • src/main/java/konkuk/thip/room/application/port/out/RoomCommandPort.java (1 hunks)
  • src/main/java/konkuk/thip/user/adapter/in/web/UserCommandController.java (3 hunks)
  • src/main/java/konkuk/thip/vote/adapter/in/web/VoteCommandController.java (1 hunks)
  • src/main/java/konkuk/thip/vote/adapter/in/web/request/DummyRequest.java (0 hunks)
  • src/main/java/konkuk/thip/vote/adapter/in/web/request/VoteCreateRequest.java (1 hunks)
  • src/main/java/konkuk/thip/vote/adapter/in/web/response/VoteCreateResponse.java (1 hunks)
  • src/main/java/konkuk/thip/vote/adapter/out/persistence/VoteCommandPersistenceAdapter.java (1 hunks)
  • src/main/java/konkuk/thip/vote/adapter/out/persistence/VoteItemJpaRepository.java (1 hunks)
  • src/main/java/konkuk/thip/vote/application/port/in/DummyUseCase.java (0 hunks)
  • src/main/java/konkuk/thip/vote/application/port/in/VoteCreateUseCase.java (1 hunks)
  • src/main/java/konkuk/thip/vote/application/port/in/dto/DummyCommand.java (0 hunks)
  • src/main/java/konkuk/thip/vote/application/port/in/dto/VoteCreateCommand.java (1 hunks)
  • src/main/java/konkuk/thip/vote/application/port/out/VoteCommandPort.java (1 hunks)
  • src/main/java/konkuk/thip/vote/application/service/VoteService.java (1 hunks)
  • src/main/java/konkuk/thip/vote/domain/Vote.java (2 hunks)
  • src/main/java/konkuk/thip/vote/domain/VoteItem.java (1 hunks)
  • src/test/java/konkuk/thip/vote/adapter/in/web/VoteCreateControllerTest.java (1 hunks)
  • src/test/java/konkuk/thip/vote/domain/VoteTest.java (1 hunks)
💤 Files with no reviewable changes (3)
  • src/main/java/konkuk/thip/vote/application/port/in/DummyUseCase.java
  • src/main/java/konkuk/thip/vote/adapter/in/web/request/DummyRequest.java
  • src/main/java/konkuk/thip/vote/application/port/in/dto/DummyCommand.java
🧰 Additional context used
🧠 Learnings (5)
src/main/java/konkuk/thip/book/application/port/out/BookCommandPort.java (1)
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로부터 조회하는 메서드를 추가함.
src/main/java/konkuk/thip/vote/application/port/out/VoteCommandPort.java (1)
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로부터 조회하는 메서드를 추가함.
src/main/java/konkuk/thip/book/adapter/out/persistence/BookCommandPersistenceAdapter.java (2)
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로부터 조회하는 메서드를 추가함.
Learnt from: seongjunnoh
PR: THIP-TextHip/THIP-Server#36
File: src/main/java/konkuk/thip/user/adapter/out/persistence/UserJpaRepository.java:7-7
Timestamp: 2025-06-29T09:47:31.299Z
Learning: Spring Data JPA에서 findBy{FieldName} 패턴의 메서드는 명시적 선언 없이 자동으로 생성되며, Optional<Entity> 반환 타입을 사용하는 것이 null 안전성을 위해 권장됩니다.
src/main/java/konkuk/thip/room/adapter/out/persistence/RoomCommandPersistenceAdapter.java (1)
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로부터 조회하는 메서드를 추가함.
src/main/java/konkuk/thip/room/application/port/out/RoomCommandPort.java (1)
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로부터 조회하는 메서드를 추가함.
🧬 Code Graph Analysis (1)
src/main/java/konkuk/thip/room/adapter/out/persistence/RoomCommandPersistenceAdapter.java (3)
src/main/java/konkuk/thip/common/exception/EntityNotFoundException.java (1)
  • EntityNotFoundException (5-10)
src/main/java/konkuk/thip/book/adapter/out/persistence/BookCommandPersistenceAdapter.java (1)
  • Repository (16-37)
src/main/java/konkuk/thip/room/adapter/out/persistence/RoomQueryPersistenceAdapter.java (1)
  • Repository (10-21)
🔇 Additional comments (32)
src/main/java/konkuk/thip/room/application/port/out/RoomCommandPort.java (1)

3-7: CQRS 컨벤션을 잘 준수한 설계입니다.

학습된 컨벤션에 따라 CommandPort에 findById 메서드를 적절히 추가하였습니다. 도메인 엔티티를 반환하는 메서드 시그니처도 명확합니다.

src/main/java/konkuk/thip/book/application/port/out/BookCommandPort.java (1)

12-12: 일관성 있는 인터페이스 설계입니다.

기존 findByIsbn 메서드와 동일한 패턴으로 findById 메서드를 추가하여 인터페이스의 일관성을 유지했습니다.

src/main/java/konkuk/thip/vote/domain/VoteItem.java (1)

19-26: 명확한 의도를 가진 정적 팩토리 메서드입니다.

withoutId 메서드명으로 새로운 엔티티 생성 시 ID가 없다는 의도를 명확히 표현했습니다. 빌더 패턴을 활용한 구현도 깔끔합니다.

src/main/java/konkuk/thip/vote/application/port/in/VoteCreateUseCase.java (1)

1-8: 단일 책임 원칙을 잘 따른 유스케이스 인터페이스입니다.

투표 생성이라는 하나의 책임만을 가지는 깔끔한 인터페이스 설계입니다. 메서드 시그니처도 명확하고 헥사고날 아키텍처의 포트 역할을 잘 수행합니다.

src/main/java/konkuk/thip/vote/adapter/in/web/response/VoteCreateResponse.java (1)

1-9: 간결하고 적절한 응답 DTO 설계입니다.

레코드 클래스를 활용하여 불변성을 보장하면서도 간결한 응답 DTO를 구현했습니다. 정적 팩토리 메서드 of()로 편의성도 제공합니다.

src/main/java/konkuk/thip/vote/application/port/out/VoteCommandPort.java (1)

10-12: 인터페이스 설계가 잘 되어있습니다.

CQRS 패턴을 따르고 있으며, 투표 저장과 투표 항목 일괄 저장 기능을 명확하게 분리했습니다. 메서드명도 직관적이고 반환 타입도 적절합니다.

src/main/java/konkuk/thip/room/adapter/out/persistence/RoomCommandPersistenceAdapter.java (2)

17-18: 필드명 변경이 적절합니다.

jpaRepositoryroomJpaRepository, userMapperroomMapper로 변경하여 명확성을 높였습니다.


20-27: findById 메서드 구현이 우수합니다.

EntityNotFoundException을 사용한 예외 처리가 적절하고, BookCommandPersistenceAdapter와 일관된 패턴을 따르고 있습니다. 도메인 특화 에러 코드 사용도 좋습니다.

src/main/java/konkuk/thip/book/adapter/out/persistence/BookCommandPersistenceAdapter.java (1)

29-36: findById 메서드 구현이 일관성 있고 우수합니다.

RoomCommandPersistenceAdapter와 동일한 패턴을 따르고 있으며, 도메인 특화 예외 처리가 적절합니다. 매퍼를 통한 도메인 엔티티 변환도 깔끔하게 구현되었습니다.

src/main/java/konkuk/thip/vote/adapter/out/persistence/VoteItemJpaRepository.java (1)

8-11: JPA Repository 인터페이스가 잘 설계되었습니다.

Spring Data JPA의 property expression을 사용한 커스텀 쿼리 메서드가 적절하고, 메서드명도 직관적입니다. 투표 ID로 투표 항목들을 조회하는 기능이 명확하게 구현되었습니다.

src/main/java/konkuk/thip/common/exception/handler/GlobalExceptionHandler.java (1)

95-102: 예외 처리 향상이 우수합니다.

cause에서 상세 메시지를 추출하여 클라이언트에게 더 구체적인 오류 정보를 제공하는 개선사항이 좋습니다. Optional.ofNullable을 사용한 안전한 null 처리도 적절합니다. PR 목표에 부합하는 개선사항입니다.

src/main/java/konkuk/thip/vote/application/port/in/dto/VoteCreateCommand.java (1)

5-19: Record 사용이 적절합니다

DTO로 record를 사용하여 불변성을 보장하는 것은 좋은 선택입니다. 명확한 필드명과 구조로 가독성도 우수합니다.

src/main/java/konkuk/thip/vote/adapter/in/web/VoteCommandController.java (1)

21-30: 투표 생성 API 구현이 깔끔합니다

컨트롤러가 웹 요청 처리에만 집중하고, 비즈니스 로직은 UseCase에 위임하는 구조가 좋습니다. @Valid를 통한 요청 검증도 적절합니다.

src/main/java/konkuk/thip/user/adapter/in/web/UserCommandController.java (1)

4-4: 검증 어노테이션 일관성 개선

@validated에서 @Valid로의 변경은 프로젝트 전체의 일관성을 높이는 좋은 결정입니다. 그룹 검증이 필요하지 않은 경우 표준 @Valid 사용이 더 적절합니다.

Also applies to: 31-31, 41-41

src/main/java/konkuk/thip/vote/application/service/VoteService.java (1)

28-56: 투표 생성 로직이 잘 구현되었습니다

트랜잭션 처리, 도메인 검증, 저장 로직의 흐름이 명확하고 적절합니다. 도메인 레벨에서의 검증을 통해 비즈니스 규칙을 잘 캡슐화했습니다.

src/main/java/konkuk/thip/vote/domain/Vote.java (2)

26-35: 팩토리 메서드 구현이 깔끔합니다

ID 없이 Vote 엔티티를 생성하는 팩토리 메서드가 명확하고 사용하기 편리합니다.


48-58: 페이지 범위 검증이 적절합니다

페이지 범위 검증 로직과 에러 메시지가 명확하고 사용자에게 도움이 되는 정보를 제공합니다.

src/main/java/konkuk/thip/vote/adapter/out/persistence/VoteCommandPersistenceAdapter.java (2)

3-10: 의존성 주입이 적절히 구성되어 있습니다.

필요한 repository들과 mapper들이 모두 주입되어 있어 투표 생성 기능을 위한 데이터 접근이 가능합니다.


34-45: 투표 저장 로직이 올바르게 구현되어 있습니다.

사용자와 룸 엔터티를 조회한 후 해당 엔터티들과 함께 투표를 저장하는 로직이 적절합니다. 또한 엔터티가 존재하지 않을 경우 적절한 예외를 던지고 있습니다.

src/test/java/konkuk/thip/vote/adapter/in/web/VoteCreateControllerTest.java (4)

76-85: 데이터베이스 정리가 적절하게 구현되어 있습니다.

각 테스트 후 관련 엔터티들을 올바른 순서로 정리하여 테스트 간 격리를 보장합니다.


87-130: 테스트 데이터 설정이 체계적으로 구성되어 있습니다.

테스트에 필요한 모든 엔터티(사용자, 책, 카테고리, 룸)를 생성하는 헬퍼 메소드가 잘 구성되어 있습니다.


132-179: 투표 생성 성공 테스트가 포괄적으로 구현되어 있습니다.

API 호출부터 데이터베이스 검증까지 전체 플로우를 테스트하고 있으며, 투표와 투표 항목 모두 올바르게 저장되는지 확인합니다.


181-352: 다양한 검증 실패 시나리오를 철저히 테스트하고 있습니다.

필수 필드 누락, 길이 제한 초과, 투표 항목 개수 제한 등 모든 검증 규칙을 테스트하여 API의 견고성을 확보합니다.

src/test/java/konkuk/thip/vote/domain/VoteTest.java (3)

11-17: 페이지 범위 검증 성공 케이스가 적절히 테스트되어 있습니다.

유효한 페이지 범위에 대해 예외가 발생하지 않는지 확인하고, 경계값 테스트도 포함되어 있습니다.


19-37: 페이지 범위 검증 실패 케이스가 철저히 테스트되어 있습니다.

페이지가 범위를 벗어날 때 적절한 예외가 발생하는지 확인하고, 예외 메시지도 검증합니다.


39-61: 총평 검증 로직이 올바르게 테스트되어 있습니다.

진행률 80% 기준으로 총평 가능 여부를 판단하는 로직과 예외 상황을 모두 테스트합니다.

src/main/java/konkuk/thip/common/exception/code/ErrorCode.java (3)

53-57: 최근 검색 관련 에러 코드가 적절히 추가되었습니다.

검색 타입 검증을 위한 에러 코드가 명확한 메시지와 함께 추가되었습니다.


59-63: 룸 관련 에러 코드가 적절히 추가되었습니다.

룸 존재 여부 확인을 위한 에러 코드가 404 상태와 함께 적절히 정의되었습니다.


64-70: 투표 관련 에러 코드가 체계적으로 추가되었습니다.

투표 존재 여부, 총평 가능 여부, 페이지 범위 검증에 대한 에러 코드가 적절한 HTTP 상태 코드와 함께 정의되었습니다.

src/main/java/konkuk/thip/vote/adapter/in/web/request/VoteCreateRequest.java (3)

11-26: 투표 생성 요청 DTO의 검증 규칙이 적절히 정의되어 있습니다.

모든 필수 필드에 대한 검증 어노테이션과 길이 제한이 명확하게 설정되어 있어 API의 견고성을 보장합니다.


27-32: 투표 항목 DTO가 깔끔하게 구성되어 있습니다.

중첩된 record 구조로 투표 항목을 표현하고 적절한 검증 규칙을 적용했습니다.


33-47: 요청 DTO를 커맨드 객체로 변환하는 로직이 올바르게 구현되어 있습니다.

웹 계층의 DTO를 애플리케이션 계층의 커맨드 객체로 적절히 변환하여 계층 간 분리를 유지합니다.


String content,

List<VoteItemCreateCommand> voteItemCreateCommands
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Verification agent

🧩 Analysis chain

null safety 고려 필요

voteItemCreateCommands 리스트가 null일 경우를 대비한 방어 로직이 필요할 수 있습니다. 서비스 레이어에서 이를 처리하고 있는지 확인이 필요합니다.


🏁 Script executed:

#!/bin/bash
# voteItemCreateCommands null 체크가 서비스 레이어에서 이루어지는지 확인
ast-grep --pattern 'command.voteItemCreateCommands()' -A 5

Length of output: 741


#!/bin/bash
# VoteCreateCommand 클래스 내용 확인
cat src/main/java/konkuk/thip/vote/application/port/in/dto/VoteCreateCommand.java

다음으로 VoteCreateCommand DTO에서 voteItemCreateCommands 필드 정의와 null 처리 여부를 확인합니다:


🏁 Script executed:

#!/bin/bash
# VoteCreateCommand 클래스 내용 확인
cat src/main/java/konkuk/thip/vote/application/port/in/dto/VoteCreateCommand.java

Length of output: 433


null 안전성 강화 필요

VoteCreateCommandvoteItemCreateCommands가 null일 경우 서비스 레이어에서 바로 .stream()을 호출해 NPE가 발생할 수 있으므로, 다음 파일에 방어 로직을 추가해야 합니다.

  • src/main/java/konkuk/thip/vote/application/port/in/dto/VoteCreateCommand.java
    레코드 정의에 Objects.requireNonNull 검증 추가

     public record VoteCreateCommand(
             Long userId,
             Long roomId,
             int page,
             boolean isOverview,
             String content,
             List<VoteItemCreateCommand> voteItemCreateCommands
     ) {
    +    public VoteCreateCommand {
    +        Objects.requireNonNull(voteItemCreateCommands, "voteItemCreateCommands must not be null");
    +    }
    
         public record VoteItemCreateCommand(String itemName) {}
     }
  • src/main/java/konkuk/thip/vote/application/service/VoteService.java
    서비스 호출부에서 null을 빈 리스트로 대체

     List<VoteItem> voteItems = 
    -    command.voteItemCreateCommands().stream()
    +    Optional.ofNullable(command.voteItemCreateCommands())
    +            .orElseGet(List::of)
    +            .stream()
             .map(itemCmd -> VoteItem.withoutId(
                 itemCmd.itemName(),
                 0,
                 savedVoteId
             ))

위 두 군데에 null 체크를 추가해 NPE를 방지하세요.

📝 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.

Suggested change
List<VoteItemCreateCommand> voteItemCreateCommands
// File: src/main/java/konkuk/thip/vote/application/port/in/dto/VoteCreateCommand.java
public record VoteCreateCommand(
Long userId,
Long roomId,
int page,
boolean isOverview,
String content,
List<VoteItemCreateCommand> voteItemCreateCommands
) {
public VoteCreateCommand {
Objects.requireNonNull(voteItemCreateCommands, "voteItemCreateCommands must not be null");
}
public record VoteItemCreateCommand(String itemName) {}
}
Suggested change
List<VoteItemCreateCommand> voteItemCreateCommands
// File: src/main/java/konkuk/thip/vote/application/service/VoteService.java
List<VoteItem> voteItems =
Optional.ofNullable(command.voteItemCreateCommands())
.orElseGet(List::of)
.stream()
.map(itemCmd -> VoteItem.withoutId(
itemCmd.itemName(),
0,
savedVoteId
))
🤖 Prompt for AI Agents
In src/main/java/konkuk/thip/vote/application/port/in/dto/VoteCreateCommand.java
at line 16, add a null check using Objects.requireNonNull for the
voteItemCreateCommands field in the record constructor to prevent null values.
Additionally, in
src/main/java/konkuk/thip/vote/application/service/VoteService.java, update the
service method that calls VoteCreateCommand to replace any null
voteItemCreateCommands with an empty list before processing, ensuring no
NullPointerException occurs when calling stream().

Copy link
Contributor

@buzz0331 buzz0331 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

수고많으셨습니다~!!
리뷰 간단하게 남겼으니 확인 부탁드릴게요~

기록 생성 api 개발 중인데 많이 참고하겠습니다 🤩


@PostMapping("/users/signup")
public BaseResponse<UserSignupResponse> signup(@Validated @RequestBody UserSignupRequest request,
public BaseResponse<UserSignupResponse> signup(@Valid @RequestBody UserSignupRequest request,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍🏻 👍🏻

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

붐붐따리붐붐따 붐업

@RequiredArgsConstructor
public class VoteService implements DummyUseCase {
@Slf4j
public class VoteService implements VoteCreateUseCase {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

p2: VoteCreateService로 수정해야할 것 같습니다!

Comment on lines 62 to 66
// 페이지 유효성 검증
vote.validatePage(book.getPageCount());

// 총평 유효성 검증
vote.validateOverview(book.getPageCount());
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

도메인 계층에서 유효성 검증 너무 좋습니다잉

"총평(isOverview)은 진행률이 80%% 이상일 때만 가능합니다. 현재 진행률 = %.2f%% (%d/%d)",
ratio * 100, page, totalPageCount
);
throw new InvalidStateException(VOTE_CANNOT_BE_OVERVIEW, new IllegalStateException(message));
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

오호 동적으로 예외 메시지를 발생시키려고 2번째 파라미터로 예외를 던지는거 맞나요?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

넵 맞습니다!
ErrorCode 로 정의한 정적인 에러 메시지에 덧붙여 현재 상황에 대한 동적인 예외 메시지 또한 클라이언트에게 응답하는게 더 좋지 않을까 싶어서, InvalidStateExcetption에 대한 ExceptionHandler 의 코드를 수정하여 2번째 파라미터로 전달받은 exception 객체의 message 도 클라이언트가 응답받을 수 있도록 하였습니다!

Comment on lines +150 to +155
Long userId = userJpaRepository.findAll().get(0).getUserId();
Long roomId = roomJpaRepository.findAll().get(0).getRoomId();

//when : 투표 생성 api 호출 (filter 통과 없이)
ResultActions result = mockMvc.perform(post("/rooms/{roomId}/vote", roomId)
.requestAttr("userId", userId)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

필터 안쓰고 이런 방법이 있군요! ㅋㅋ하나 배워갑니다

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

공감합니다

@NotNull(message = "투표 항목은 필수입니다.")
@Size(min = 1, max = 5, message = "투표 항목은 1개 이상, 최대 5개까지입니다.")
@Valid
List<VoteItemCreateRequest> voteItemCreateRequests
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

p3: api에 명세되는 이름이라 인스턴스 이름만 voteItemList로 바꾸는거 어떨까욥

String content,

@NotNull(message = "투표 항목은 필수입니다.")
@Size(min = 1, max = 5, message = "투표 항목은 1개 이상, 최대 5개까지입니다.")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

현재 "투표 항목은 1개 이상, 최대 5개까지이다"라는 요구사항은 도메인 규칙에 해당하는 것 같긴한데, 어떻게 생각하시나요..? 변동될 수 있는 규칙이라 도메인에서 검증하는 것이 더 유지보수하는게 좋을 것 같긴한데 따지고보면 다른 파라미터들도 다 비슷한 거 같아서.. 애매하네요 😅

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

저도 현준님이 리뷰 남겨주신 것처럼 "투표 항목은 1개 이상, 5개 이하만 가능하다" 라는 요구사항을 투표 생성 api 구현시에 지켜야 하는 도메인 규칙이라고 생각합니다!
하지만 투표 생성 api 의 도메인 계층까지 가지않고, request body 에서도 충분히 검증할 수 있는 validation 이라 생각해서 request body 내부에서 validation 을 수행하도록 구현하였습니다!

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

넵 확인했습니다! 뭐 투표를 생성하는 로직이 다른 api에 사용하는 것은 아니니 상관없을 것 같네요! 만약 추후에 투표를 생성하는 로직이 이 api가 아닌 다른 api에서도 필요해진다면 중복 코드를 방지하기 위해 도메인 계층으로 옮기는 것도 좋을 것 같아요~

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

넵 동의합니다!

Copy link
Member

@hd0rable hd0rable left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

수고하셨습니닷!! 리뷰란 확인부탁드립니당

public ResponseEntity<ErrorResponse> businessExceptionHandler(BusinessException e) {
log.error("[BusinessExceptionHandler] {}", e.getMessage());

// 1) cause 에 포함된 상세 메시지를 파싱, 없다면 빈 문자열로 설정
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🤩


@PostMapping("/users/signup")
public BaseResponse<UserSignupResponse> signup(@Validated @RequestBody UserSignupRequest request,
public BaseResponse<UserSignupResponse> signup(@Valid @RequestBody UserSignupRequest request,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

붐붐따리붐붐따 붐업

Comment on lines 49 to 57
List<VoteItemJpaEntity> voteItemJpaEntities = voteItems.stream()
.map(voteItem -> {
VoteJpaEntity voteJpaEntity = voteJpaRepository.findById(voteItem.getVoteId()).orElseThrow(
() -> new EntityNotFoundException(VOTE_NOT_FOUND)
);

return voteItemMapper.toJpaEntity(voteItem, voteJpaEntity);
})
.toList();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

p2: 토래말처럼 n+1 문제가 발생할것같은데
서비스로직에서 해당 vote를 생성할때 이미 검증된 동일한 voteID로 voteItem을 생성하기 때문에 한 번만 findById로 검증하면 충분할 것 같습니다!

Suggested change
List<VoteItemJpaEntity> voteItemJpaEntities = voteItems.stream()
.map(voteItem -> {
VoteJpaEntity voteJpaEntity = voteJpaRepository.findById(voteItem.getVoteId()).orElseThrow(
() -> new EntityNotFoundException(VOTE_NOT_FOUND)
);
return voteItemMapper.toJpaEntity(voteItem, voteJpaEntity);
})
.toList();
Long voteId = voteItems.get(0).getVoteId();
VoteJpaEntity voteJpaEntity = voteJpaRepository.findById(voteId)
.orElseThrow(() -> new EntityNotFoundException(VOTE_NOT_FOUND));
List<VoteItemJpaEntity> voteItemJpaEntities = voteItems.stream()
.map(voteItem -> voteItemMapper.toJpaEntity(voteItem, voteJpaEntity))
.toList();

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

반영해서 수정하겠습니다!

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

토래는 누구죠? ㅋㄷㅋㄷㅋ


private void saveUserAndRoom() {
AliasJpaEntity alias = aliasJpaRepository.save(AliasJpaEntity.builder()
.value("책벌레")
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ㅋㅋㅋ 📚🐜

Comment on lines +150 to +155
Long userId = userJpaRepository.findAll().get(0).getUserId();
Long roomId = roomJpaRepository.findAll().get(0).getRoomId();

//when : 투표 생성 api 호출 (filter 통과 없이)
ResultActions result = mockMvc.perform(post("/rooms/{roomId}/vote", roomId)
.requestAttr("userId", userId)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

공감합니다

Copy link

@coderabbitai coderabbitai bot left a 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

♻️ Duplicate comments (1)
src/main/java/konkuk/thip/vote/adapter/out/persistence/VoteCommandPersistenceAdapter.java (1)

47-61: N+1 문제가 효과적으로 해결되었습니다.

이전 리뷰 코멘트에서 지적된 N+1 문제가 잘 해결되었습니다. 투표 엔터티를 한 번만 조회하여 재사용하는 방식으로 성능이 개선되었습니다.

빈 리스트 처리와 벌크 저장 로직도 적절하게 구현되어 있습니다.

🧹 Nitpick comments (1)
src/main/java/konkuk/thip/vote/application/service/VoteCreateService.java (1)

46-52: VoteItem 생성 로직에서 개선을 고려해보세요.

현재 모든 투표 항목의 초기 투표 수가 0으로 하드코딩되어 있습니다. 도메인 객체에서 기본값을 관리하거나 상수로 정의하는 것이 좋겠습니다.

다음과 같이 개선할 수 있습니다:

                .map(itemCmd -> VoteItem.withoutId(
                        itemCmd.itemName(),
-                       0,
+                       VoteItem.DEFAULT_VOTE_COUNT,
                        savedVoteId
                ))
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 1f807d6 and 1a01f13.

📒 Files selected for processing (4)
  • src/main/java/konkuk/thip/vote/adapter/in/web/request/VoteCreateRequest.java (1 hunks)
  • src/main/java/konkuk/thip/vote/adapter/out/persistence/VoteCommandPersistenceAdapter.java (1 hunks)
  • src/main/java/konkuk/thip/vote/application/service/VoteCreateService.java (1 hunks)
  • src/test/java/konkuk/thip/vote/adapter/in/web/VoteCreateControllerTest.java (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • src/test/java/konkuk/thip/vote/adapter/in/web/VoteCreateControllerTest.java
  • src/main/java/konkuk/thip/vote/adapter/in/web/request/VoteCreateRequest.java
🧰 Additional context used
🧬 Code Graph Analysis (1)
src/main/java/konkuk/thip/vote/adapter/out/persistence/VoteCommandPersistenceAdapter.java (1)
src/main/java/konkuk/thip/common/exception/EntityNotFoundException.java (1)
  • EntityNotFoundException (5-10)
⏰ Context from checks skipped due to timeout of 90000ms (1)
  • GitHub Check: build
🔇 Additional comments (7)
src/main/java/konkuk/thip/vote/application/service/VoteCreateService.java (4)

19-22: 클래스 구조가 깔끔하고 적절합니다.

필요한 어노테이션들이 적절히 적용되어 있고, 의존성 주입도 올바르게 구성되어 있습니다.


28-30: 트랜잭션 경계가 적절하게 설정되었습니다.

투표 생성과 투표 항목 저장이 하나의 원자적 작업으로 처리되어 데이터 일관성을 보장합니다.


32-38: 도메인 객체 생성 로직이 명확합니다.

커맨드 객체의 데이터를 도메인 객체로 변환하는 과정이 명확하게 구현되어 있습니다.


58-67: 도메인 검증 로직이 적절하게 분리되어 있습니다.

검증 로직이 도메인 레이어에 위임되어 있고, 필요한 데이터를 조회하여 검증하는 구조가 올바릅니다. 비즈니스 규칙이 도메인 객체에 캡슐화되어 있어 좋은 설계입니다.

src/main/java/konkuk/thip/vote/adapter/out/persistence/VoteCommandPersistenceAdapter.java (3)

3-10: 필요한 의존성들이 적절히 추가되었습니다.

투표 생성 기능을 위한 필수 의존성들이 명확하게 추가되어 있습니다.


27-32: 의존성 주입이 올바르게 구성되었습니다.

새로 추가된 리포지토리와 매퍼들이 적절히 주입되어 있습니다.


34-45: 엔터티 존재 검증과 예외 처리가 적절합니다.

사용자와 룸 엔터티의 존재를 검증하고, 적절한 에러 코드로 예외를 발생시키는 로직이 잘 구현되어 있습니다.

buzz0331
buzz0331 previously approved these changes Jul 5, 2025
Copy link
Contributor

@buzz0331 buzz0331 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

확인했습니다~ 고생하셨어유

conflict 나고 있네염,,

String content,

@NotNull(message = "투표 항목은 필수입니다.")
@Size(min = 1, max = 5, message = "투표 항목은 1개 이상, 최대 5개까지입니다.")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

넵 확인했습니다! 뭐 투표를 생성하는 로직이 다른 api에 사용하는 것은 아니니 상관없을 것 같네요! 만약 추후에 투표를 생성하는 로직이 이 api가 아닌 다른 api에서도 필요해진다면 중복 코드를 방지하기 위해 도메인 계층으로 옮기는 것도 좋을 것 같아요~

@sonarqubecloud
Copy link

sonarqubecloud bot commented Jul 5, 2025

@seongjunnoh seongjunnoh merged commit a6d9ea5 into develop Jul 5, 2025
3 checks passed
@seongjunnoh seongjunnoh deleted the feat/#37-create-vote branch July 5, 2025 11:26
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[THIP2025-71] [feat] 투표 생성 api

3 participants