Skip to content

[OT-136][FEAT]: 댓글 CRUD API 구현#83

Merged
marulog merged 14 commits intodevelopfrom
OT-136-feature/comment-api
Feb 28, 2026
Merged

[OT-136][FEAT]: 댓글 CRUD API 구현#83
marulog merged 14 commits intodevelopfrom
OT-136-feature/comment-api

Conversation

@marulog
Copy link
Copy Markdown
Collaborator

@marulog marulog commented Feb 27, 2026

📝 작업 내용

이번 PR에서 작업한 내용을 적어주세요

  • 댓글 등록
  • 댓글 수정
  • 댓글 삭제
  • 댓글 조회

📷 스크린샷

  • 댓글 등록
    유저는 하나의 영상에 중복해서 댓글 작성이 가능합니다.
image
  • 댓글 수정
    본인이 등록한 댓글에 대해서만 수정이 가능합니다.
image image
  • 댓글 조회
image
  • 댓글 삭제
image

☑️ 체크 리스트

체크 리스트를 확인해주세요

  • 테스트는 잘 통과했나요?
  • 충돌을 해결했나요?
  • 이슈는 등록했나요?
  • 라벨은 등록했나요?

#️⃣ 연관된 이슈

close #77

💬 리뷰 요구사항

컨벤션 규칙 자세히 봐주시면 감사하겠습니다.
댓글 CRUD는 간단한거라서 큰 문제는 없어 보입니다.

Summary by CodeRabbit

  • 새로운 기능
    • 댓글 작성·수정·삭제 기능 추가
    • 내 댓글 목록 조회 및 페이지네이션 제공
    • 스포일러 표시 옵션 추가
  • 버그 수정 / 권한
    • 본인 작성 댓글만 수정/삭제 가능하도록 권한 검사 적용
  • 문서
    • API 문서(스웨거) 표기 통일: Likes API 명칭 정비
  • 기타
    • API 응답 및 에러 코드 정비 (댓글 관련 코드 추가/정리)

@marulog marulog self-assigned this Feb 27, 2026
@marulog marulog added the feat 새로운 기능 구현 label Feb 27, 2026
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Feb 27, 2026

Walkthrough

댓글 CRUD API가 추가되었습니다. CommentApi 인터페이스와 CommentController, CommentService, 관련 DTO들, Comment 도메인/리포지토리(인터페이스·구현) 및 ErrorCode/ContentsRepository 변경이 포함됩니다. (50단어 이내)

Changes

Cohort / File(s) Summary
API 레이어
apps/api-user/src/main/java/com/ott/api_user/comment/controller/CommentApi.java, apps/api-user/src/main/java/com/ott/api_user/comment/controller/CommentController.java
댓글 관련 공개 API 인터페이스와 REST 컨트롤러 추가: POST/PATCH/DELETE /comments, GET /comments/me. 인증 주체(memberId) 주입, 요청 바인딩 및 SuccessResponse로 래핑.
DTOs (요청/응답)
apps/api-user/src/main/java/com/ott/api_user/comment/dto/request/CreateCommentRequest.java, .../UpdateCommentRequest.java, .../response/CommentResponse.java, .../response/MyCommentResponse.java
댓글 생성·수정 요청 DTO와 응답 DTO 추가. 유효성 검사 및 Swagger 어노테이션 포함, Comment→DTO 매핑 팩토리 메서드 제공.
서비스 계층
apps/api-user/src/main/java/com/ott/api_user/comment/service/CommentService.java
비즈니스 로직 구현: 생성·수정(소유권 검사)·삭제(소프트 삭제)·내 댓글 페이징 조회. 트랜잭션 적용 및 BusinessException/ErrorCode 사용.
도메인 리포지토리
modules/domain/src/main/java/com/ott/domain/comment/repository/CommentRepository.java, .../CommentRepositoryCustom.java, .../CommentRepositoryImpl.java
CommentRepository 및 커스텀 리포지토리/구현 추가. QueryDSL 기반 페이징 조회(findMyComments) 및 연관 엔티티 페치용 findByIdAndStatus 선언/구현.
도메인 엔티티 & 컨텐츠 리포지토리
modules/domain/src/main/java/com/ott/domain/comment/domain/Comment.java, modules/domain/src/main/java/com/ott/domain/contents/repository/ContentsRepository.java
Comment 엔티티에 update(content, isSpoiler) 메서드 추가. ContentsRepository에 media EntityGraph를 사용하는 findByIdAndStatus 추가.
공통/메타데이터
modules/common-web/src/main/java/com/ott/common/web/exception/ErrorCode.java, apps/api-user/src/main/java/com/ott/api_user/likes/controller/LikesAPI.java
ErrorCode enum에 COMMENT_NOT_FOUND, COMMENT_FORBIDDEN 추가 및 BOOKMARK_NOT_FOUND 코드 재배치; LikesAPI @Tag 문자열 변경(메타데이터만). B009 코드 중복 가능성 유의.

Sequence Diagram(s)

sequenceDiagram
    participant Client as Client
    participant Controller as CommentController
    participant Service as CommentService
    participant ContentsRepo as ContentsRepository
    participant CommentRepo as CommentRepository
    participant DB as Database

    Client->>Controller: POST /comments (CreateCommentRequest)
    Controller->>Service: createComment(memberId, request)
    Service->>ContentsRepo: findByIdAndStatus(contentId, ACTIVE)
    ContentsRepo->>DB: query content with media
    DB-->>ContentsRepo: content
    Service->>CommentRepo: save(new Comment(...))
    CommentRepo->>DB: insert comment
    DB-->>CommentRepo: persisted comment
    Service-->>Controller: CommentResponse
    Controller-->>Client: 201 Created + SuccessResponse<CommentResponse>
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Suggested reviewers

  • phonil
  • arlen02-01
🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (2 warnings)

Check name Status Explanation Resolution
Out of Scope Changes check ⚠️ Warning 대부분의 변경사항은 댓글 CRUD API 구현과 관련이 있으나, LikesAPI의 Swagger 태그 수정은 댓글 API와 무관합니다. LikesAPI 태그 변경(좋아요 API → Likes API)은 별도의 변경으로 분리하여 다른 PR에서 처리하기를 권장합니다.
Docstring Coverage ⚠️ Warning Docstring coverage is 54.55% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed PR 제목은 '[OT-136][FEAT]: 댓글 CRUD API 구현'으로 변경 사항의 핵심인 댓글 CRUD API 구현을 명확하게 요약하고 있습니다.
Linked Issues check ✅ Passed PR은 연결된 이슈 #77의 모든 요구사항을 충족합니다. 댓글 생성, 수정, 조회, 삭제 4가지 API가 모두 구현되었습니다.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
  • 📝 Generate docstrings (stacked PR)
  • 📝 Generate docstrings (commit on current branch)
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch OT-136-feature/comment-api

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

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@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: 9

🧹 Nitpick comments (7)
apps/api-user/src/main/java/com/ott/api_user/comment/controller/CommentApi.java (1)

101-101: size 파라미터 유효성 검증 수정 권장

@PositiveOrZerosize=0을 허용하여 빈 결과를 반환하게 됩니다. @Positive를 사용하여 최소 1 이상의 값을 요구하는 것이 적절합니다.

♻️ 수정 제안
-            `@PositiveOrZero` `@Parameter`(description = "페이지 크기", schema = `@Schema`(type = "integer", defaultValue = "20")) `@RequestParam`(defaultValue = "20") Integer size,
+            `@Positive` `@Parameter`(description = "페이지 크기", schema = `@Schema`(type = "integer", defaultValue = "20")) `@RequestParam`(defaultValue = "20") Integer size,
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@apps/api-user/src/main/java/com/ott/api_user/comment/controller/CommentApi.java`
at line 101, The size request parameter in CommentApi (the method parameter
annotated with `@RequestParam` defaultValue = "20") currently uses `@PositiveOrZero`
which allows 0; change it to `@Positive` to require values >=1, update the import
if needed (ensure javax/ jakarta.validation.constraints.Positive is imported),
and adjust any validation messages or API docs if present so the schema/default
remains "20" but validation enforces a minimum of 1.
apps/api-user/src/main/java/com/ott/api_user/comment/controller/CommentController.java (2)

11-11: 사용되지 않는 import 제거

jakarta.validation.constraints.Positive가 import 되었지만 사용되지 않습니다.

♻️ 수정 제안
 import jakarta.validation.Valid;
-import jakarta.validation.constraints.Positive;
 import lombok.RequiredArgsConstructor;
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@apps/api-user/src/main/java/com/ott/api_user/comment/controller/CommentController.java`
at line 11, Remove the unused import Jakarta validation symbol by deleting the
line importing jakarta.validation.constraints.Positive in the CommentController
class (remove the unused Positive import from CommentController.java); ensure no
references to Positive remain and then re-run organize/imports or a build to
confirm there are no unused-import warnings.

27-29: Controller 메서드에 @Valid 어노테이션 추가 권장

CommentApi 인터페이스에는 @Valid가 있지만, 구현체에서도 명시적으로 추가하면 의도가 더 명확해집니다.

♻️ 수정 제안
     public ResponseEntity<SuccessResponse<CommentResponse>> createComment(
-           `@RequestBody` CreateCommentRequest request,
+           `@Valid` `@RequestBody` CreateCommentRequest request,
            `@AuthenticationPrincipal` Long memberId) {
     public ResponseEntity<SuccessResponse<CommentResponse>> updateComment(
             `@PathVariable` Long commentId,
-            `@RequestBody` UpdateCommentRequest request,
+            `@Valid` `@RequestBody` UpdateCommentRequest request,
             `@AuthenticationPrincipal` Long memberId) {
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@apps/api-user/src/main/java/com/ott/api_user/comment/controller/CommentController.java`
around lines 27 - 29, The createComment method in CommentController should
explicitly annotate the request body parameter with `@Valid` to mirror the
CommentApi contract and make validation intent clear: add `@Valid` to the
`@RequestBody` CreateCommentRequest request parameter in the createComment method
and ensure the corresponding javax.validation/ jakarta.validation.Valid import
is present so request DTO validation is applied at controller level.
apps/api-user/src/main/java/com/ott/api_user/comment/service/CommentService.java (2)

61-61: 서비스 레이어에서 @Valid 어노테이션 제거 권장

@Valid 어노테이션은 Controller에서 이미 적용되므로 Service 레이어에서는 중복됩니다. 또한 jakarta.validation.Valid import(Line 18)도 함께 제거해 주세요.

♻️ 수정 제안
-    public CommentResponse updateComment(Long memberId, Long commentId, `@Valid` UpdateCommentRequest request) {
+    public CommentResponse updateComment(Long memberId, Long commentId, UpdateCommentRequest request) {
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@apps/api-user/src/main/java/com/ott/api_user/comment/service/CommentService.java`
at line 61, 서비스 계층의 중복 검증 제거: CommentService 클래스에서 updateComment(Long memberId,
Long commentId, `@Valid` UpdateCommentRequest request) 메서드의 파라미터에 붙은 `@Valid` 어노테이션을
제거하고, 더 이상 사용되지 않는 jakarta.validation.Valid import를 제거하세요; 즉, 메서드 시그니처에서 `@Valid를`
삭제하고 파일 상단의 jakarta.validation.Valid 관련 import 문을 제거하면 됩니다.

45-45: TODO 주석에 대한 비즈니스 규칙 확인 필요

한 유저가 한 콘텐츠에 여러 댓글을 작성할 수 있는지에 대한 비즈니스 규칙이 명확하지 않습니다. 요구사항이 확정되면 필요시 중복 댓글 방지 로직을 추가하거나 TODO 주석을 제거해 주세요.

비즈니스 규칙이 확정되면 중복 댓글 방지 로직 구현을 도와드릴까요?

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@apps/api-user/src/main/java/com/ott/api_user/comment/service/CommentService.java`
at line 45, Clarify the business rule referenced by the TODO in CommentService
(the comment "한 유저가 한 콘텐츠에 여러 댓글 허용?") and then either remove the TODO or
implement duplicate-comment prevention: if the rule disallows multiple comments
per user per content, add a check in the comment creation flow (e.g., in the
CommentService method that creates/saves comments such as createComment or
saveComment) to query for an existing Comment by userId and contentId and
throw/return a conflict error or ignore creation; if multiple comments are
allowed, simply remove the TODO comment. Ensure you reference and update
CommentService and the create/save method and relevant repository calls (e.g.,
CommentRepository.findByUserIdAndContentId) when implementing the check.
apps/api-user/src/main/java/com/ott/api_user/comment/dto/response/CommentResponse.java (2)

20-21: 필드명 일관성 검토: contentsId vs contentId

CreateCommentRequest에서는 contentId를 사용하지만, CommentResponse에서는 contentsId를 사용합니다. API 일관성을 위해 필드명을 통일하는 것을 권장합니다.

♻️ 수정 제안
     `@Schema`(type = "Long", example = "5", description = "콘텐츠 ID")
-    private Long contentsId;
+    private Long contentId;

그리고 from 메서드도 업데이트:

-                .contentsId(comment.getContents().getId())
+                .contentId(comment.getContents().getId())
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@apps/api-user/src/main/java/com/ott/api_user/comment/dto/response/CommentResponse.java`
around lines 20 - 21, The response field name is inconsistent: CommentResponse
uses the field contentsId while CreateCommentRequest uses contentId; rename the
field in CommentResponse from contentsId to contentId and update any usages
accordingly (including the static factory/mapping method from in
CommentResponse) so the class, its `@Schema` annotation, and the from(...) method
reference contentId consistently, and adjust any JSON/serialization expectations
to match the CreateCommentRequest naming.

29-30: Schema example 형식이 실제 LocalDateTime 형식과 불일치

example = "2026.02.15 20:14" 형식은 실제 LocalDateTime JSON 직렬화 형식(ISO-8601)과 다릅니다. PR 스크린샷에서 보이는 실제 응답 형식인 "2026-02-27T04:19:21.526334000"과 일치하도록 수정을 권장합니다.

♻️ 수정 제안
-    `@Schema`(type = "LocalDateTime ", example = "2026.02.15 20:14", description = "작성일시")
+    `@Schema`(type = "LocalDateTime", example = "2026-02-27T04:19:21", description = "작성일시")
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@apps/api-user/src/main/java/com/ott/api_user/comment/dto/response/CommentResponse.java`
around lines 29 - 30, The `@Schema` on CommentResponse.createdDate uses a non-ISO
example and an incorrect type; change the annotation to use an ISO-8601 example
and a string type (remove the trailing space), e.g. update `@Schema`(type =
"string", example = "2026-02-27T04:19:21.526334000", description = "작성일시") so
the example matches the actual LocalDateTime JSON serialization and the type
reflects the serialized form.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In
`@apps/api-user/src/main/java/com/ott/api_user/comment/controller/CommentApi.java`:
- Line 102: getMyComments 메서드의 memberId 파라미터에 `@AuthenticationPrincipal` 어노테이션이
누락되어 있습니다; getMyComments 시그니처에서 Long memberId 매개변수에 다른 메서드들(createComment,
updateComment, deleteComment)과 동일하게 `@AuthenticationPrincipal을` 추가하여 인증된 사용자 ID를
주입받도록 수정하세요.
- Line 32: The API docs declare a 201 but the controller returns 200; make them
consistent. Either update the `@ApiResponse` on CommentApi to responseCode = "200"
to match the current behavior, or (preferred per REST) change
CommentController.createComment() to return a 201 Created response by replacing
the ResponseEntity.ok(...) return with a
ResponseEntity.status(HttpStatus.CREATED).body(...) that wraps
SuccessResponse.of(commentService.createComment(...)); update imports if needed.

In
`@apps/api-user/src/main/java/com/ott/api_user/comment/dto/request/CreateCommentRequest.java`:
- Around line 15-16: CreateCommentRequest 클래스의 contentId 필드(`@Schema` on private
Long contentId)에 잘못된 Swagger 설명("댓글 ID")이 들어가 있습니다; 필드가 콘텐츠 식별자를 나타내므로 `@Schema의`
description 값을 "콘텐츠 ID" 또는 적절한 한국어 설명으로 변경하고 필요하면 example 값을 콘텐츠 식별자 예시(예: "1")로
유지해 주세요.

In
`@apps/api-user/src/main/java/com/ott/api_user/comment/dto/request/UpdateCommentRequest.java`:
- Around line 19-20: The Swagger description says "디폴트 false" but the
UpdateCommentRequest field isSpoiler (class UpdateCommentRequest) can be null;
either update the `@Schema` text to remove the misleading default OR make the code
enforce a false default by initializing isSpoiler (e.g., set a default value or
change to primitive boolean) so runtime and Swagger match; update the field
declaration for isSpoiler and any JSON deserialization/validation as needed to
ensure the default is applied and adjust the `@Schema` example/description to
reflect the chosen behavior.

In
`@apps/api-user/src/main/java/com/ott/api_user/comment/service/CommentService.java`:
- Line 81: Update the incorrect comment above the deleteComment method: change
the Korean comment "본인만 수정 가능" to "본인만 삭제 가능" so it correctly reflects the
permission check in the deleteComment method.
- Line 53: Remove the accidental duplicate semicolon in CommentService.java:
locate the stray ";;" (appearing after a method/statement end like ");;") in the
CommentService class and delete the extra semicolon so the statement ends with a
single semicolon ");". Ensure the file compiles and run tests/build to verify no
syntax errors remain.

In
`@modules/common-web/src/main/java/com/ott/common/web/exception/ErrorCode.java`:
- Around line 51-54: In the ErrorCode enum remove the duplicated/typo constant
MEDIA_NOT_FOUNT and keep the correct MEDIA_NOT_FOUND (both currently use code
"B007"); edit the enum (ErrorCode) so only MEDIA_NOT_FOUND remains and delete
the MEDIA_NOT_FOUNT entry to eliminate the typo and duplicate error code.
- Around line 52-56: There are duplicate error codes; update the enum entries so
each ErrorCode constant has a unique code: change either COMMENT_NOT_FOUND or
BOOKMARK_NOT_FOUND from "B008" to a new unused code and change either
COMMENT_FORBIDDEN or UNSUPPORTED_IMAGE_EXTENSION from "B009" to another new
unused code; locate the constants COMMENT_NOT_FOUND, BOOKMARK_NOT_FOUND,
COMMENT_FORBIDDEN, and UNSUPPORTED_IMAGE_EXTENSION in ErrorCode and assign
distinct string codes (e.g., increment to next available B00x values) ensuring
no other enum value reuses them.

In `@modules/domain/src/main/java/com/ott/domain/comment/domain/Comment.java`:
- Around line 49-52: In Comment.update(String content, Boolean isSpoiler) add
input validation before assigning fields: ensure content is non-null and not
blank (trim and check length) and ensure isSpoiler is non-null; if validation
fails throw a clear unchecked exception (e.g., IllegalArgumentException or
NullPointerException) so the domain invariant is preserved and invalid values
are never written to the entity; perform all checks first and only assign
this.content and this.isSpoiler after they pass.

---

Nitpick comments:
In
`@apps/api-user/src/main/java/com/ott/api_user/comment/controller/CommentApi.java`:
- Line 101: The size request parameter in CommentApi (the method parameter
annotated with `@RequestParam` defaultValue = "20") currently uses `@PositiveOrZero`
which allows 0; change it to `@Positive` to require values >=1, update the import
if needed (ensure javax/ jakarta.validation.constraints.Positive is imported),
and adjust any validation messages or API docs if present so the schema/default
remains "20" but validation enforces a minimum of 1.

In
`@apps/api-user/src/main/java/com/ott/api_user/comment/controller/CommentController.java`:
- Line 11: Remove the unused import Jakarta validation symbol by deleting the
line importing jakarta.validation.constraints.Positive in the CommentController
class (remove the unused Positive import from CommentController.java); ensure no
references to Positive remain and then re-run organize/imports or a build to
confirm there are no unused-import warnings.
- Around line 27-29: The createComment method in CommentController should
explicitly annotate the request body parameter with `@Valid` to mirror the
CommentApi contract and make validation intent clear: add `@Valid` to the
`@RequestBody` CreateCommentRequest request parameter in the createComment method
and ensure the corresponding javax.validation/ jakarta.validation.Valid import
is present so request DTO validation is applied at controller level.

In
`@apps/api-user/src/main/java/com/ott/api_user/comment/dto/response/CommentResponse.java`:
- Around line 20-21: The response field name is inconsistent: CommentResponse
uses the field contentsId while CreateCommentRequest uses contentId; rename the
field in CommentResponse from contentsId to contentId and update any usages
accordingly (including the static factory/mapping method from in
CommentResponse) so the class, its `@Schema` annotation, and the from(...) method
reference contentId consistently, and adjust any JSON/serialization expectations
to match the CreateCommentRequest naming.
- Around line 29-30: The `@Schema` on CommentResponse.createdDate uses a non-ISO
example and an incorrect type; change the annotation to use an ISO-8601 example
and a string type (remove the trailing space), e.g. update `@Schema`(type =
"string", example = "2026-02-27T04:19:21.526334000", description = "작성일시") so
the example matches the actual LocalDateTime JSON serialization and the type
reflects the serialized form.

In
`@apps/api-user/src/main/java/com/ott/api_user/comment/service/CommentService.java`:
- Line 61: 서비스 계층의 중복 검증 제거: CommentService 클래스에서 updateComment(Long memberId,
Long commentId, `@Valid` UpdateCommentRequest request) 메서드의 파라미터에 붙은 `@Valid` 어노테이션을
제거하고, 더 이상 사용되지 않는 jakarta.validation.Valid import를 제거하세요; 즉, 메서드 시그니처에서 `@Valid를`
삭제하고 파일 상단의 jakarta.validation.Valid 관련 import 문을 제거하면 됩니다.
- Line 45: Clarify the business rule referenced by the TODO in CommentService
(the comment "한 유저가 한 콘텐츠에 여러 댓글 허용?") and then either remove the TODO or
implement duplicate-comment prevention: if the rule disallows multiple comments
per user per content, add a check in the comment creation flow (e.g., in the
CommentService method that creates/saves comments such as createComment or
saveComment) to query for an existing Comment by userId and contentId and
throw/return a conflict error or ignore creation; if multiple comments are
allowed, simply remove the TODO comment. Ensure you reference and update
CommentService and the create/save method and relevant repository calls (e.g.,
CommentRepository.findByUserIdAndContentId) when implementing the check.

ℹ️ Review info

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 80bb703 and 5344605.

📒 Files selected for processing (17)
  • apps/api-user/src/main/java/com/ott/api_user/comment/controller/.gitkeep
  • apps/api-user/src/main/java/com/ott/api_user/comment/controller/CommentApi.java
  • apps/api-user/src/main/java/com/ott/api_user/comment/controller/CommentController.java
  • apps/api-user/src/main/java/com/ott/api_user/comment/dto/.gitkeep
  • apps/api-user/src/main/java/com/ott/api_user/comment/dto/request/CreateCommentRequest.java
  • apps/api-user/src/main/java/com/ott/api_user/comment/dto/request/UpdateCommentRequest.java
  • apps/api-user/src/main/java/com/ott/api_user/comment/dto/response/CommentResponse.java
  • apps/api-user/src/main/java/com/ott/api_user/comment/dto/response/MyCommentResponse.java
  • apps/api-user/src/main/java/com/ott/api_user/comment/service/.gitkeep
  • apps/api-user/src/main/java/com/ott/api_user/comment/service/CommentService.java
  • apps/api-user/src/main/java/com/ott/api_user/likes/controller/LikesAPI.java
  • modules/common-web/src/main/java/com/ott/common/web/exception/ErrorCode.java
  • modules/domain/src/main/java/com/ott/domain/comment/domain/Comment.java
  • modules/domain/src/main/java/com/ott/domain/comment/repository/CommentRepository.java
  • modules/domain/src/main/java/com/ott/domain/comment/repository/CommentRepositoryCustom.java
  • modules/domain/src/main/java/com/ott/domain/comment/repository/CommentRepositoryImpl.java
  • modules/domain/src/main/java/com/ott/domain/contents/repository/ContentsRepository.java

Comment thread apps/api-user/src/main/java/com/ott/api_user/comment/controller/CommentApi.java Outdated
Comment thread apps/api-user/src/main/java/com/ott/api_user/comment/service/CommentService.java Outdated
Comment thread apps/api-user/src/main/java/com/ott/api_user/comment/service/CommentService.java Outdated
Comment thread modules/common-web/src/main/java/com/ott/common/web/exception/ErrorCode.java Outdated
@marulog marulog changed the title [OT-136] [FEAT]: 댓글 CRUD API 구현 [OT-136][FEAT]: 댓글 CRUD API 구현 Feb 27, 2026
Copy link
Copy Markdown

@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

♻️ Duplicate comments (2)
modules/domain/src/main/java/com/ott/domain/comment/domain/Comment.java (1)

49-57: ⚠️ Potential issue | 🔴 Critical

isSpoiler null 입력이 도메인 불변식을 깨고 500으로 이어질 수 있습니다.

Line 56에서 isSpoiler를 그대로 대입하면 @Column(nullable = false) 제약과 충돌해 커밋 시점 예외가 발생할 수 있습니다. null 검증을 추가해 주세요.

수정 예시
+import java.util.Objects;
...
     public void update(String content, Boolean isSpoiler) {
         if (content == null || content.isBlank()) {
             throw new IllegalArgumentException("댓글 내용이 비어있습니다.");
         }
 
         this.content = content;
-        this.isSpoiler = isSpoiler;
+        this.isSpoiler = Objects.requireNonNull(isSpoiler, "스포일러 여부는 필수입니다.");
     }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@modules/domain/src/main/java/com/ott/domain/comment/domain/Comment.java`
around lines 49 - 57, Comment.update assigns the Boolean isSpoiler directly
which can violate the non-null DB constraint; add a null-check in the
update(String content, Boolean isSpoiler) method (in class Comment) and throw an
IllegalArgumentException with a clear message when isSpoiler is null, so the
domain invariant is enforced before setting this.isSpoiler and before
persisting.
modules/common-web/src/main/java/com/ott/common/web/exception/ErrorCode.java (1)

53-54: ⚠️ Potential issue | 🟠 Major

에러 코드 B009 중복으로 에러 식별이 충돌합니다.

Line 53과 Line 54가 서로 다른 의미에 동일 코드(B009)를 사용하고 있어 클라이언트 분기/로그 추적이 불안정해집니다. 코드 값을 분리해 주세요.

수정 예시
-    COMMENT_FORBIDDEN(HttpStatus.FORBIDDEN, "B009", "본인이 작성한 댓글만 수정/삭제할 수 있습니다."),
+    COMMENT_FORBIDDEN(HttpStatus.FORBIDDEN, "B019", "본인이 작성한 댓글만 수정/삭제할 수 있습니다."),
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@modules/common-web/src/main/java/com/ott/common/web/exception/ErrorCode.java`
around lines 53 - 54, The duplicate error code "B009" is used for both
COMMENT_FORBIDDEN and UNSUPPORTED_IMAGE_EXTENSION in the ErrorCode enum causing
identification collisions; open the ErrorCode enum and assign a unique code to
one of them (e.g., change UNSUPPORTED_IMAGE_EXTENSION or COMMENT_FORBIDDEN to
"B010" or another unused B0xx) while keeping the HttpStatus and message intact,
and then update any call sites/tests that rely on the old code to use the new
enum value to ensure consistent client branching and logging.
🧹 Nitpick comments (1)
apps/api-user/src/main/java/com/ott/api_user/comment/service/CommentService.java (1)

45-45: 미확정 비즈니스 규칙은 코드 주석으로 남기지 말고 정책으로 확정해 주세요.

Line 45의 주석은 동작 기준이 모호합니다. 허용 정책을 명시적으로 결정(허용/제한)하고 코드 또는 이슈로 고정하는 편이 안전합니다.

원하시면 “콘텐츠당 1댓글 제한” 또는 “다중 댓글 허용” 기준으로 서비스/DB 레벨 구현 초안을 바로 만들어드릴게요.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@apps/api-user/src/main/java/com/ott/api_user/comment/service/CommentService.java`
at line 45, The TODO-like comment in CommentService.java about whether a user
may post multiple comments per content must not remain ambiguous: decide and
codify the policy (either "one comment per user per content" or "allow multiple
comments") then remove the comment and implement the rule in the codebase and/or
policy tracking. If you choose "one comment per content" add enforcement in
CommentService (e.g., in the create/post method) and a DB-level uniqueness
constraint or index to prevent duplicates; if you choose "allow multiple", add a
clarifying comment and ensure no uniqueness constraint exists. Also add a
configuration constant or feature-flag and a short unit/integration test for the
chosen behavior, or open a tracked issue linking the decision if you cannot
implement it now.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In
`@apps/api-user/src/main/java/com/ott/api_user/comment/dto/request/CreateCommentRequest.java`:
- Around line 23-24: The isSpoiler field in CreateCommentRequest (and likewise
in UpdateCommentRequest) can be null but the Comment entity/DB column is
non-nullable; add javax.validation.constraints.NotNull to the isSpoiler field in
both DTOs (annotate the field named isSpoiler) so incoming requests are
validated and nulls are rejected, ensure the `@NotNull` import is added and keep
the existing `@Schema` metadata; alternatively you may switch the field to
primitive boolean to enforce non-null at the type level, but be sure to update
both CreateCommentRequest and UpdateCommentRequest consistently.

In
`@apps/api-user/src/main/java/com/ott/api_user/comment/service/CommentService.java`:
- Around line 90-96: getMyComments in CommentService calls PageRequest.of(page,
size) but does not guard against size < 1; add service-layer validation to
prevent Spring Data IllegalArgumentException by checking the incoming size (and
optionally page) before creating the PageRequest. In getMyComments, validate
that size >= 1 (and page >= 0 if needed), and either normalize to a safe default
or throw a clear IllegalArgumentException/CustomBadRequestException with a
descriptive message; then only call PageRequest.of(page, size) after the check.

---

Duplicate comments:
In
`@modules/common-web/src/main/java/com/ott/common/web/exception/ErrorCode.java`:
- Around line 53-54: The duplicate error code "B009" is used for both
COMMENT_FORBIDDEN and UNSUPPORTED_IMAGE_EXTENSION in the ErrorCode enum causing
identification collisions; open the ErrorCode enum and assign a unique code to
one of them (e.g., change UNSUPPORTED_IMAGE_EXTENSION or COMMENT_FORBIDDEN to
"B010" or another unused B0xx) while keeping the HttpStatus and message intact,
and then update any call sites/tests that rely on the old code to use the new
enum value to ensure consistent client branching and logging.

In `@modules/domain/src/main/java/com/ott/domain/comment/domain/Comment.java`:
- Around line 49-57: Comment.update assigns the Boolean isSpoiler directly which
can violate the non-null DB constraint; add a null-check in the update(String
content, Boolean isSpoiler) method (in class Comment) and throw an
IllegalArgumentException with a clear message when isSpoiler is null, so the
domain invariant is enforced before setting this.isSpoiler and before
persisting.

---

Nitpick comments:
In
`@apps/api-user/src/main/java/com/ott/api_user/comment/service/CommentService.java`:
- Line 45: The TODO-like comment in CommentService.java about whether a user may
post multiple comments per content must not remain ambiguous: decide and codify
the policy (either "one comment per user per content" or "allow multiple
comments") then remove the comment and implement the rule in the codebase and/or
policy tracking. If you choose "one comment per content" add enforcement in
CommentService (e.g., in the create/post method) and a DB-level uniqueness
constraint or index to prevent duplicates; if you choose "allow multiple", add a
clarifying comment and ensure no uniqueness constraint exists. Also add a
configuration constant or feature-flag and a short unit/integration test for the
chosen behavior, or open a tracked issue linking the decision if you cannot
implement it now.

ℹ️ Review info

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 5344605 and ae96a7b.

📒 Files selected for processing (7)
  • apps/api-user/src/main/java/com/ott/api_user/comment/controller/CommentApi.java
  • apps/api-user/src/main/java/com/ott/api_user/comment/controller/CommentController.java
  • apps/api-user/src/main/java/com/ott/api_user/comment/dto/request/CreateCommentRequest.java
  • apps/api-user/src/main/java/com/ott/api_user/comment/dto/request/UpdateCommentRequest.java
  • apps/api-user/src/main/java/com/ott/api_user/comment/service/CommentService.java
  • modules/common-web/src/main/java/com/ott/common/web/exception/ErrorCode.java
  • modules/domain/src/main/java/com/ott/domain/comment/domain/Comment.java
🚧 Files skipped from review as they are similar to previous changes (1)
  • apps/api-user/src/main/java/com/ott/api_user/comment/dto/request/UpdateCommentRequest.java

@marulog marulog requested a review from phonil February 27, 2026 08:35
Copy link
Copy Markdown
Contributor

@phonil phonil left a comment

Choose a reason for hiding this comment

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

고생하셨어요!! 한 번 확인해주세요~

Comment thread apps/api-user/src/main/java/com/ott/api_user/comment/controller/CommentApi.java Outdated
Comment thread apps/api-user/src/main/java/com/ott/api_user/comment/controller/CommentApi.java Outdated
Comment thread apps/api-user/src/main/java/com/ott/api_user/comment/controller/CommentApi.java Outdated
Copy link
Copy Markdown

@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

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In
`@apps/api-user/src/main/java/com/ott/api_user/comment/controller/CommentApi.java`:
- Around line 103-104: Add an upper bound to the page-size parameter to prevent
excessive load by annotating the existing size parameter (the one currently
annotated with `@Positive` in CommentApi) with `@Max` (e.g., `@Max`(100)) and update
the OpenAPI `@Schema` if desired; also import javax.validation.constraints.Max so
the validator runs. Ensure the parameter signature that contains "@Positive ...
Integer size" becomes "@Positive `@Max`(100) ... Integer size" (or another agreed
limit) so the request is rejected when clients request unbounded page sizes.
- Around line 93-95: The `@ApiResponse` on CommentApi currently fixes the 200
response schema to MyCommentResponse which mismatches the actual return type;
remove the content/schema portion of the `@ApiResponse` (or replace it with the
wrapper type that matches the method return, e.g.,
SuccessResponse<PageResponse<MyCommentResponse>> if you choose explicit typing)
so Swagger infers the correct contract from the method signature; look for the
`@ApiResponse` annotation around MyCommentResponse in CommentApi and update it
accordingly.

ℹ️ Review info

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between ae96a7b and 8e675dc.

📒 Files selected for processing (5)
  • apps/api-user/src/main/java/com/ott/api_user/comment/controller/CommentApi.java
  • apps/api-user/src/main/java/com/ott/api_user/comment/controller/CommentController.java
  • apps/api-user/src/main/java/com/ott/api_user/comment/dto/request/CreateCommentRequest.java
  • apps/api-user/src/main/java/com/ott/api_user/comment/dto/request/UpdateCommentRequest.java
  • apps/api-user/src/main/java/com/ott/api_user/comment/dto/response/MyCommentResponse.java
🚧 Files skipped from review as they are similar to previous changes (2)
  • apps/api-user/src/main/java/com/ott/api_user/comment/dto/request/CreateCommentRequest.java
  • apps/api-user/src/main/java/com/ott/api_user/comment/controller/CommentController.java

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

feat 새로운 기능 구현

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[OT-136] [FEAT]: 댓글 CRUD API 개발

2 participants