Skip to content

[Hotfix] 댓글 생성시 댓글 조회와 같은 형식으로 response 반환 #238

Merged
hd0rable merged 12 commits intodevelopfrom
hotfix/#228-comment-response
Aug 16, 2025
Merged

[Hotfix] 댓글 생성시 댓글 조회와 같은 형식으로 response 반환 #238
hd0rable merged 12 commits intodevelopfrom
hotfix/#228-comment-response

Conversation

@hd0rable
Copy link
Member

@hd0rable hd0rable commented Aug 16, 2025

#️⃣ 연관된 이슈

closes #228

📝 작업 내용

  • @Nico1eKim 님의 요청에 따라 댓글 생성시 재랜더링을 위해 댓글 조회와 response 타입을 동일하게 반환하도록 수정하였습니다.
  • 답글 생성시 기존 부모 댓글의 사용자의 좋아요여부와, 댓글작성자의 alias 관련 정보들을 반환하기위하여 불가피하게 조회로직이 추가되었습니다.
  • 관련하여 Querydsl과 Mapper를 사용하여 구현했습니다

📸 스크린샷

image image

💬 리뷰 요구사항

리뷰어가 특별히 봐주었으면 하는 부분이 있다면 작성해주세요

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

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

Summary by CodeRabbit

  • 신규 기능

    • 댓글 작성 시, 단순 ID 대신 생성된 댓글의 상세 정보(본문, 작성자/프로필, 별칭/색상, 작성일, 좋아요 수/상태, 작성자·삭제 여부)와 대댓글 목록을 함께 반환합니다. 클라이언트는 추가 조회 없이 즉시 렌더링할 수 있습니다.
  • 버그 수정

    • 존재하지 않는 부모 댓글에 대댓글을 작성하려 할 때 더 명확한 오류가 반환되어 사용자가 문제를 쉽게 인지할 수 있습니다.

@coderabbitai
Copy link

coderabbitai bot commented Aug 16, 2025

Walkthrough

댓글 생성 API의 응답을 CommentIdResponse에서 CommentCreateResponse로 변경하고, 서비스/매퍼/포트/리포지토리에 단건 조회 및 매핑 로직을 추가하여 생성 직후 완전한 댓글 정보(루트/대댓글 포함)를 반환하도록 흐름을 확장했습니다.

Changes

Cohort / File(s) Summary
Web API 응답 변경
src/main/java/konkuk/thip/comment/adapter/in/web/CommentCommandController.java, src/main/java/konkuk/thip/comment/adapter/in/web/response/CommentCreateResponse.java, src/main/java/konkuk/thip/comment/adapter/in/web/response/CommentIdResponse.java
컨트롤러 응답 타입을 CommentCreateResponse로 교체. CommentCreateResponse 신설, CommentIdResponse 삭제.
애플리케이션 서비스/포트
src/main/java/konkuk/thip/comment/application/service/CommentCreateService.java, .../application/port/in/CommentCreateUseCase.java, .../application/port/out/CommentQueryPort.java
createComment 반환형 변경(Long → CommentCreateResponse). 단건 조회 포트(findRootCommentById, findChildCommentById) 추가 및 사용. 생성 후 매퍼로 응답 조립.
매퍼 확장
src/main/java/konkuk/thip/comment/application/mapper/CommentQueryMapper.java
생성용 매핑 메서드 추가(toRoot, toReply, toRootCommentResponseWithChildren). 날짜 포맷/작성자 여부/별칭 매핑 포함.
퍼시스턴스 어댑터/리포지토리
src/main/java/konkuk/thip/comment/adapter/out/persistence/CommentQueryPersistenceAdapter.java, .../repository/CommentQueryRepository.java, .../repository/CommentQueryRepositoryImpl.java
루트/대댓글 단건 조회 메서드 추가(findRootCommentId, findChildCommentId) 및 구현. 프로젝션으로 CommentQueryDto 반환.

Sequence Diagram(s)

sequenceDiagram
actor Client
participant Controller as CommentCommandController
participant Service as CommentCreateService
participant CmdPort as CommentCommandPort
participant QPort as CommentQueryPort
participant LikePort as CommentLikeQueryPort
participant Mapper as CommentQueryMapper

Client->>Controller: POST /comments
Controller->>Service: createComment(command)

alt Reply comment
  Service->>CmdPort: save(reply)
  Service->>QPort: findRootCommentById(rootId)
  Service->>LikePort: existsLike(userId, rootId)
  Service->>QPort: findChildCommentById(rootId, savedReplyId)
  Service->>Mapper: toRootCommentResponseWithChildren(parentDto, childDto, isLiked, userId)
  Mapper-->>Service: CommentCreateResponse
else Root comment
  Service->>CmdPort: save(root)
  Service->>QPort: findRootCommentById(savedRootId)
  Service->>Mapper: toRoot(rootDto, isLike=false, userId)
  Mapper-->>Service: CommentCreateResponse
end

Service-->>Controller: CommentCreateResponse
Controller-->>Client: BaseResponse<CommentCreateResponse>
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Assessment against linked issues

Objective Addressed Explanation
댓글 생성 시 재랜더링을 위한 완전한 댓글 정보 반환하도록 수정 (#228)
단건 조회로 생성 직후 루트/대댓글 포함 데이터 제공 (#228)
컨트롤러/유스케이스 응답 스키마 교체 (ID → 상세 DTO) (#228)

Assessment against linked issues: Out-of-scope changes

(해당 없음)

Possibly related PRs

Suggested labels

🛠️ feat

Suggested reviewers

  • seongjunnoh
  • buzz0331

Poem

폴짝! 새 댓글 싹 틔운 순간에,
아이디 한 알이었는데 숲이 되었네.
닉네임, 색, 좋아요, 답글 줄줄이—
렌더링 토끼는 박수 짝짝 뛰지! 🐇✨
“완전체로 돌아왔어요!” 하고 귀를 쫑긋.

Tip

🔌 Remote MCP (Model Context Protocol) integration is now available!

Pro plan users can now connect to remote MCP servers from the Integrations page. Connect with popular remote MCPs such as Notion and Linear to add more context to your reviews and chats.

✨ Finishing Touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch hotfix/#228-comment-response

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.
    • 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.
  • 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 the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.

Support

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

CodeRabbit Commands (Invoked using PR/Issue comments)

Type @coderabbitai help to get the list of available commands.

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

Status, Documentation and Community

  • Visit our Status Page to check the current availability of CodeRabbit.
  • 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.

@github-actions
Copy link

github-actions bot commented Aug 16, 2025

Test Results

404 tests  ±0   404 ✅ ±0   31s ⏱️ -6s
120 suites ±0     0 💤 ±0 
120 files   ±0     0 ❌ ±0 

Results for commit fb17d32. ± Comparison against base commit e71a592.

♻️ This comment has been updated with latest results.

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: 4

🔭 Outside diff range comments (1)
src/main/java/konkuk/thip/comment/application/service/CommentCreateService.java (1)

82-87: 부모 댓글 상태(ACTIVE) 및 루트 댓글 여부 검증 로직 추가 필요

현재 commentCommandPort.findById()는 삭제(INACTIVE)된 댓글도 조회하며, Comment.validateReplyCommentCreate()에는 상태 및 깊이 검증이 빠져 있어

  • 비활성(삭제)된 댓글에도 답글 생성 가능
  • 루트 댓글이 아닌(대댓글에 대한 답글) 경우 서비스/쿼리 로직 오류 발생

아래 위치에 검증 로직을 추가해주세요.

• src/main/java/konkuk/thip/comment/application/service/CommentCreateService.java#createCommentDomain

  • findById() 직후에 parentComment.getStatus() == StatusType.ACTIVE 검증
  • 루트 댓글만 답글 가능하도록 parentComment.getParentCommentId() == null 검증

• src/main/java/konkuk/thip/comment/domain/Comment.java#validateReplyCommentCreate

  • 상태 및 깊이 검증을 도메인 레벨로 이관하여 재사용성 확보

예시 (서비스 레벨):

// 3-1. (답글일 경우) 부모 댓글 조회
Comment parent = commentCommandPort.findById(command.parentId())
  .orElseThrow(...);
if (parent.getStatus() != StatusType.ACTIVE) {
  throw new InvalidStateException(..., new IllegalArgumentException("비활성된 댓글에는 답글을 달 수 없습니다."));
}
if (parent.getParentCommentId() != null) {
  throw new InvalidStateException(..., new IllegalArgumentException("루트 댓글에만 답글을 달 수 있습니다."));
}

위 두 검증을 추가하면 비활성 댓글 방지 및 허용 깊이를 일관되게 관리할 수 있습니다.

🧹 Nitpick comments (11)
src/main/java/konkuk/thip/comment/adapter/in/web/response/CommentCreateResponse.java (3)

16-18: reply DTO에 isDeleted 누락 가능성 — 조회 응답 포맷과의 정합성 확인 필요

PR 목표가 “댓글 조회와 동일한 형식”인 만큼, 루트에는 isDeleted가 있으나 ReplyCommentCreateDto에는 없는 점이 프론트에서 공통 렌더러를 사용할 때 차이를 유발할 수 있습니다. 조회 응답의 reply 항목에 isDeleted가 존재한다면 여기도 포함해 주세요.

필요 시 아래와 같이 추가를 제안합니다(매퍼/서비스 매핑도 함께 보완 필요):

     public record ReplyCommentCreateDto(
             Long commentId,
             String parentCommentCreatorNickname,
             Long creatorId,
             String creatorProfileImageUrl,
             String creatorNickname,
             String aliasName,
             String aliasColor,
             String postDate,        // 댓글 작성 시각 (~ 전 형식)
             String content,
             int likeCount,
             boolean isLike,
+            boolean isDeleted,
             boolean isWriter
     ) {}

Also applies to: 20-33


18-18: replyList null 방지(빈 리스트 보장) 요청

레코드 자체로는 문제없지만, 매퍼/서비스에서 replyList를 null이 아닌 빈 리스트로 반환하도록 보장해 주세요. 공통 렌더러에서 NPE 또는 분기 처리를 줄일 수 있습니다.


14-15: likeCount 타입 검토(선택)

좋아요 수가 커질 가능성이 있거나 기존 조회 응답이 long/Integer를 사용한다면 타입 정합성 확인을 권장합니다. 현재로도 기능상 문제는 없으며 선택사항입니다.

src/main/java/konkuk/thip/comment/application/port/out/CommentQueryPort.java (2)

19-21: 단건 조회용 Port 추가는 목적에 부합하나, NotFound 처리 방식 명확화 필요

반환 타입이 CommentQueryDto(nullable)인지, 조회 실패 시 예외를 던지는지 규약을 명시해 주세요. NPE/500 방지 차원에서 Optional 반환 또는 @nullable 주석, 혹은 Javadoc으로 계약을 분명히 하는 것을 권장합니다.


21-21: 사소한 스타일: 불필요한 공백 제거

콤마 앞의 공백을 제거하면 코드 스타일 일관성이 좋아집니다.

-    CommentQueryDto findChildCommentById(Long rootCommentId , Long replyCommentId);
+    CommentQueryDto findChildCommentById(Long rootCommentId, Long replyCommentId);
src/main/java/konkuk/thip/comment/adapter/in/web/CommentCommandController.java (1)

37-50: REST 관점의 상태코드/스웨거 문서 검토(선택)

생성 API인 만큼 201 Created와 Location 헤더 노출을 고려할 수 있습니다. 프로젝트에서 일괄 200 OK를 사용한다면 현재도 무방하나, 스웨거 응답 스키마가 CommentCreateResponse로 반영되었는지(예: @apiresponse 등)만 확인 부탁드립니다.

src/main/java/konkuk/thip/comment/adapter/out/persistence/repository/CommentQueryRepository.java (2)

18-21: 메서드 명명 일관성 정리 제안(Port와 ById 접미사 정렬)

Port는 findRootCommentById/findChildCommentById인데, Repository는 findRootCommentId/findChildCommentId로 ById가 빠져 있어 반환값이 ID인지 DTO인지 혼동을 줄 수 있습니다. 또한 파라미터 명도 루트/자식 의미가 드러나도록 정리되면 가독성이 좋아집니다.

다음과 같이 변경을 제안드립니다(구현/어댑터도 함께 수정 필요):

-    CommentQueryDto findRootCommentId(Long commentId);
-    CommentQueryDto findChildCommentId(Long rootCommentId, Long commentId);
+    CommentQueryDto findRootCommentById(Long rootCommentId);
+    CommentQueryDto findChildCommentById(Long rootCommentId, Long childCommentId);

18-21: 조회 실패 시 처리 계약 명확화(Optional 또는 예외 규약)

Port와 동일하게, 존재하지 않을 때 null 반환인지, 예외인지가 명확하면 상위 서비스에서 방어 로직을 통일할 수 있습니다. Optional 사용 또는 Javadoc으로 계약을 문서화하는 것을 권장합니다.

src/main/java/konkuk/thip/comment/adapter/out/persistence/CommentQueryPersistenceAdapter.java (1)

47-55: ID 단건 조회 추가 자체는 적절. 다만 null 반환 시 상위 레이어 NPE 위험이 있습니다.

두 메서드가 JPA/Querydsl 쿼리의 fetchOne 결과를 그대로 반환하므로, 데이터가 없거나 조인 조건으로 누락될 경우 null이 전달됩니다. 현재 create 흐름(서비스)에서 즉시 DTO 필드를 접근하므로 NPE 가능성이 있습니다. 최소한 서비스 레이어에서 null 체크 후 도메인 예외를 던지거나, 포트/어댑터에서 Optional 반환 또는 NotFound 예외로 변환하는 방식을 고려해주세요. 또한 메서드명/위임 대상의 네이밍이 findRootCommentByIdfindRootCommentId로 어긋납니다(의미상 root 제약이 없다면 findCommentById 류로 통일 권장).

src/main/java/konkuk/thip/comment/adapter/out/persistence/repository/CommentQueryRepositoryImpl.java (1)

190-216: 메서드 명/의미 재검토 권장: ‘findRootCommentId’가 루트 제약 없이 동작합니다.

현재 WHERE절에 parent is null 제약이 없어 “루트만” 찾는 의미가 보장되지 않습니다. 실제 의도가 “루트 댓글만”이라면 필터를 추가하거나, 의도가 “ID로 단건 조회”라면 메서드명을 findCommentById로 변경해 혼동을 줄여주세요. (서비스에서 이 메서드를 parentId에 사용 중이라, 루트 제약 추가 시 상위 로직도 함께 정리되어야 합니다.)

src/main/java/konkuk/thip/comment/application/mapper/CommentQueryMapper.java (1)

49-55: 답글 생성 매핑에서 필요한 필드(예: parentId, parentNickname) 누락 여부 확인

QCommentQueryDto의 child 프로젝션에는 부모 ID/부모 닉네임이 포함됩니다. ReplyCommentCreateDto에 해당 필드가 존재한다면 현재 매핑에 누락되어 응답 정합성이 깨질 수 있습니다(unmappedTargetPolicy = IGNORE라 컴파일 시 경고 없이 넘어갑니다). 필요한 경우 명시 매핑을 추가해 주세요.

📜 Review details

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

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 7e56075 and 47e1f1a.

📒 Files selected for processing (10)
  • src/main/java/konkuk/thip/comment/adapter/in/web/CommentCommandController.java (2 hunks)
  • src/main/java/konkuk/thip/comment/adapter/in/web/response/CommentCreateResponse.java (1 hunks)
  • src/main/java/konkuk/thip/comment/adapter/in/web/response/CommentIdResponse.java (0 hunks)
  • src/main/java/konkuk/thip/comment/adapter/out/persistence/CommentQueryPersistenceAdapter.java (1 hunks)
  • src/main/java/konkuk/thip/comment/adapter/out/persistence/repository/CommentQueryRepository.java (1 hunks)
  • src/main/java/konkuk/thip/comment/adapter/out/persistence/repository/CommentQueryRepositoryImpl.java (1 hunks)
  • src/main/java/konkuk/thip/comment/application/mapper/CommentQueryMapper.java (4 hunks)
  • src/main/java/konkuk/thip/comment/application/port/in/CommentCreateUseCase.java (1 hunks)
  • src/main/java/konkuk/thip/comment/application/port/out/CommentQueryPort.java (1 hunks)
  • src/main/java/konkuk/thip/comment/application/service/CommentCreateService.java (4 hunks)
💤 Files with no reviewable changes (1)
  • src/main/java/konkuk/thip/comment/adapter/in/web/response/CommentIdResponse.java
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2025-07-03T03:05:05.031Z
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로부터 조회하는 메서드를 추가함.

Applied to files:

  • src/main/java/konkuk/thip/comment/application/port/out/CommentQueryPort.java
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: build
🔇 Additional comments (6)
src/main/java/konkuk/thip/comment/adapter/in/web/response/CommentCreateResponse.java (1)

5-19: 생성 응답을 조회 응답 포맷에 맞춘 레코드 설계, 전반적으로 적절합니다

필요한 필드가 균형 있게 포함되어 있고, FE 재랜더링 용도로 충분해 보입니다. 중첩 Reply DTO로 계층을 표현하는 것도 명확합니다.

src/main/java/konkuk/thip/comment/application/port/in/CommentCreateUseCase.java (1)

3-8: createComment 반환 타입 변경점 정상 반영됨

CommentIdResponse 또는 BaseResponse<CommentIdResponse> 사용 흔적이 전혀 없습니다.
– 모든 createComment 호출부가 CommentCreateResponse 반환으로 올바르게 업데이트되었습니다.

고생 많으셨습니다!

src/main/java/konkuk/thip/comment/adapter/in/web/CommentCommandController.java (1)

45-50: 생성 API가 즉시 재랜더링 가능한 응답을 반환하도록 변경된 점, 적합합니다

컨트롤러는 UseCase 결과를 그대로 감싸서 반환하므로 간결하며, FE 요구사항(조회 포맷과 동일)과 일치합니다.

src/main/java/konkuk/thip/comment/application/service/CommentCreateService.java (1)

39-39: 생성 UseCase의 반환타입 전환(L → CommentCreateResponse) 잘 반영되었습니다.

컨트롤러까지의 응답 포맷 정합성을 위해 필요한 변경으로 보이며, 트랜잭션 경계 내에서 저장→조회→매핑 순서도 적절합니다.

src/main/java/konkuk/thip/comment/application/mapper/CommentQueryMapper.java (2)

31-39: 생성 경로 전용 루트 매핑 추가(LGTM).

생성 직후 ‘좋아요 여부’ 입력을 boolean으로 직접 주입하는 선택이 합리적입니다. replyList를 가변 리스트로 초기화한 점도 create 후 append 시나리오에 적합합니다.


81-88: 부분 재랜더링 전략에 부합. 단, ‘루트’ 식별만 정확하면 충분

생성 응답에서 루트 1개 + 신규 답글 1개만 담아 클라이언트가 머지하도록 한 전략은 합리적입니다. 위에서 언급한대로 루트 식별(대댓글 시 루트 승격)만 보장되면 문제 없습니다.

Copy link
Collaborator

@seongjunnoh seongjunnoh left a comment

Choose a reason for hiding this comment

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

굳굳 확인했습니다

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.

굿입니다~

Comment on lines +64 to +76
// 5. 매퍼로 DTO 변환 후 반환
if (command.isReplyRequest()) {
// 부모 댓글 조회
CommentQueryDto parentCommentDto = commentQueryPort.findRootCommentById(command.parentId());
// 사용자 부모 댓글 좋아요 여부 조회
boolean isLikedParentComment = commentLikeQueryPort.isLikedCommentByUser(command.userId(),parentCommentDto.commentId());

CommentQueryDto savedReplyCommentDto = commentQueryPort.findChildCommentById(command.parentId(), savedCommentId);
return commentQueryMapper.toRootCommentResponseWithChildren(parentCommentDto, savedReplyCommentDto,isLikedParentComment,command.userId());
} else {
CommentQueryDto savedCommentDto = commentQueryPort.findRootCommentById(savedCommentId);
return commentQueryMapper.toRoot(savedCommentDto, false, command.userId());
}
Copy link
Contributor

Choose a reason for hiding this comment

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

LGTM

@hd0rable hd0rable merged commit 9b1192f into develop Aug 16, 2025
4 checks passed
@hd0rable hd0rable deleted the hotfix/#228-comment-response branch August 16, 2025 18:36
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-273] [hotfix] 댓글 재랜더링 하기 위한 완전한 댓글 정보 반환하도록 수정

3 participants