Skip to content

feat: Sprint 03 댓글 도메인(생성·조회·수정·삭제) 및 하이브리드 프리뷰 아키텍처 구현 - #17

Merged
devikae merged 32 commits into
mainfrom
feature/sprint03-comment
Sep 6, 2026
Merged

feat: Sprint 03 댓글 도메인(생성·조회·수정·삭제) 및 하이브리드 프리뷰 아키텍처 구현#17
devikae merged 32 commits into
mainfrom
feature/sprint03-comment

Conversation

@devikae

@devikaedevikae commented Sep 6, 2026

Copy link
Copy Markdown
Owner

📌 개요 (Overview)

  • PR 브랜치: feature/sprint03-commentmain
  • 작업 목적: Snowthing 3차 MVP 핵심 타겟인 댓글/대댓글 도메인의 생성(2-Depth 평탄화, 100개 상한), 하이브리드 프리뷰 및 분리 페이징 조회(ADR-001 채택), 수정(낙관적 락, 403 권한 분기), 삭제(Soft Delete, 4대 권한 매트릭스), 그리고 프론트엔드 UI/UX를 완비하여 main 브랜치로 통합함.

🛠️ 주요 변경 사항 (What Changed)

  1. 아키텍처 의사결정 (ADR-001) 및 하이브리드 프리뷰 조회

    • 조회 구조 채택: 3개 Spike 벤치마크(210KB vs 103KB vs 5.55KB) 실측 데이터 기반으로 [후보 3: Adjacency List 기반 하이브리드 프리뷰(루트 20개 + 대댓글 상위 5개 미리보기) 및 대댓글 20개 단위 분리 페이징] 최종 채택.
    • 대댓글 카운트 일원화: 화면 렌더링 노드 일원화 정책에 따라 삭제 대댓글 placeholder를 포함한 전체 대댓글 수(totalCount)로 replyCount, previewReplies, hasMoreReplies 기준을 100% 통일하여 UI 인지 부조화 방지.
    • 보안 및 가시성 검증: 분리 페이징 API(GET /api/v1/comments/{commentId}/replies) 진입 시 상위 게시글의 삭제/차단 상태(validatePostVisibility)를 필수 검증.
  2. 댓글 생성 (Create) 및 동시성 방어

    • 2-Depth 평탄화: 대댓글 작성 시 대상 노드가 대댓글이더라도 rootParent() while loop를 통해 항상 최상위 조상 루트 엔티티를 찾아 2-Depth 계층을 보장.
    • 루트당 100개 대댓글 상한 및 비관적 락: SELECT ... FOR UPDATE로 동시 대댓글 폭증 시 상한 초과(COMMENT_005)를 물리적으로 방어.
    • 트랜잭션 분리: 회원 조회 및 BCrypt 암호화는 트랜잭션 외부에서 수행하고 짧은 DB 쓰기만 CommentCommandService 독립 Bean 트랜잭션에서 수행.
    • 익명 비밀번호 검증: @Size(min = 4, max = 20)로 입력 경계값 검증.
  3. 댓글 수정 (Update) 및 동시성 제어

    • 낙관적 락(@Version): Comment 엔티티에 버전 필드를 추가해 동시 수정 시 선착순 변경만 반영하고 충돌 시 COMMENT_006 반환.
    • 인가 분기: 관리자 우회 불가, 오직 작성자 본인(일반 회원 세션 일치 / 비회원 익명 비밀번호 일치)만 수정 허용.
    • 네트워크 정보 보호: 익명 댓글만 마스킹된 IP(211.234.***.***)를 응답하고, 일반 회원은 null로 차단하여 네트워크 정보 과다 노출 방지.
  4. 댓글 삭제 (Delete) 및 4대 권한 매트릭스

    • Soft Delete & 계층 보존: 물리 삭제 대신 is_deleted = true 처리 및 "삭제된 댓글입니다." placeholder로 대체 노출하여 하위 대댓글의 대화 맥락 보존.
    • 4대 권한 매트릭스:
      • 최고 관리자(ROLE_ADMIN): 비밀번호 없이 강제 삭제 허용.
      • 일반 회원 & 로그인 익명: 본인 세션 일치 시 비밀번호 없이 즉시 삭제.
      • 비회원 익명: 암호화된 비밀번호 일치 시 삭제.
  5. 프론트엔드 UI/UX 완성 (page.tsx)

    • 브라우저 다이얼로그 전면 퇴출: prompt, confirm 제거 및 헤더 시각 옆 미니 버튼과 인라인 플로팅 팝오버 위젯(CommentDeleteInline) 구현.
    • 인라인 수정 폼 (CommentEditForm): 본문 불변 수정 및 익명 비밀번호 검증.
    • Append-on-Create: 신규 댓글 등록 시 최신 댓글이 첫 페이지 재조회로 사라져 보이는 현상 개선.
    • 가짜 멘션 UI 제거: DB에 저장되지 않는 임시 라벨 대신 순수 2-Depth 토글(toggleReplyEditor)로 단순화.

🧪 테스트 및 검증 결과 (Verification & QA)

  • 백엔드 전체 단위/통합 테스트: .\gradlew.bat test 실행 결과 BUILD SUCCESSFUL (130+ 개 테스트 전수 통과, 0 failure).
  • 댓글 도메인 8개 전담 테스트 클래스: 총 89개 시나리오 100% 통과 (생성, 조회, 페이징, 수정, 낙관적 락, 삭제, Soft Delete, 권한 매트릭스 등).
  • 프론트엔드 프로덕션 빌드: npm run build 실행 결과 Next.js 16.2.12 Turbopack 100% SUCCESS (TypeScript 컴파일 0 error, 10개 라우트 정상 생성).
  • 코드 포맷팅: .\gradlew.bat spotlessApply 서식 검증 완료.

✅ PR 체크리스트 (Checklist)

  • 코드가 정상적으로 빌드되고 모든 단위/통합 테스트가 통과하는지
  • DTO 컬렉션 방어적 복사 및 불변성을 준수했는지
  • 문자열 리터럴 예외 대신 ErrorCode 기반 커스텀 예외로 일원화했는지
  • Controller ↔ Service ↔ Repository 간 계층 분리 원칙을 준수했는지
  • docs/conception/sprint03/ 하위 모든 설계 문서(API 명세서, 정책 문서, ADR)와 구현이 100% 일치하는지
  • docs/project/work.md 작업 기록지가 최신 상태로 업데이트되었는지

Summary by CodeRabbit

  • New Features

    • Added cursor-based pagination for comments and replies.
    • Added reply browsing with counts, previews, and a 100-reply limit.
    • Added comment editing for authorized members and anonymous authors.
    • Improved comment details with writer information, anonymity handling, timestamps, and edit/delete permissions.
    • Added stronger validation for comment content, page sizes, and anonymous passwords.
  • Bug Fixes

    • Improved handling of deleted comments, permissions, concurrent updates, and invalid requests.
  • Documentation

    • Expanded setup and security guidance, including CSRF and comment behavior documentation.

devikaeand others added 30 commits August 29, 2026 06:25
- 쿼리 최적화: 스칼라 서브쿼리 제거 배치 집계 및 CROSS JOIN LATERAL Top-5 적용
- 동시성 및 가시성: 루트 선행 락을 통한 데드락 방어 및 게시글 가시성 검증 일원화
- 계층 및 정책: Comment.rootParent() 최상위 탐색 루프 및 replyCount 기준 화면 일치화
- 유효성 검증: CommentCreateRequest 익명 비밀번호 길이(@SiZe 4~20자) 검증 및 테스트
- 프론트엔드: 댓글 등록 시 Append-on-Create 적용 및 가짜 멘션 UI 제거
- 테스트 환경: H2 제거 및 CI/로컬 환경 MySQL 8.0 전면 일원화
feat: 댓글 생성(2-Depth 평탄화, 100개 상한) 및 루트 Batch + 대댓글 분리 페이징 조회 구현
feat: 댓글 수정(PUT /api/v1/comments/{commentId}) 기능 및 테스트 추가
feat: 댓글 삭제(Soft Delete)기능 개발 및 단위/통합 테스트 구현
@github-actionsgithub-actionsBot added documentation Improvements or additions to documentation backend frontend ci-cd database labels Sep 6, 2026
@coderabbitai

coderabbitaiBot commented Sep 6, 2026

Copy link
Copy Markdown

Review Change Stack

Caution

Review failed

The pull request is closed.

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Advanced

Run ID: abe54794-b139-411b-a465-1fc617885b59

📥 Commits

Reviewing files that changed from the base of the PR and between 44d134f and 33562c2.

⛔ Files ignored due to path filters (26)
  • docs/conception/sprint03/ADR-001-댓글아키텍처.md is excluded by !docs/**
  • docs/conception/sprint03/comment_api_spec.md is excluded by !docs/**
  • docs/conception/sprint03/comment_policy.md is excluded by !docs/**
  • docs/conception/sprint03/spike_루트커서_대댓글전체배치.md is excluded by !docs/**
  • docs/conception/sprint03/spike_메모리전체트리조립.md is excluded by !docs/**
  • docs/conception/sprint03/spike_하이브리드프리뷰_분리API.md is excluded by !docs/**
  • docs/conception/sprint03/기술부채 해결_4.md is excluded by !docs/**
  • docs/project/work.md is excluded by !docs/**
  • docs/study/sprint03/comment/test/spike_experiment_guide.md is excluded by !docs/**
  • docs/study/sprint03/comment/test/spike_result_template.md is excluded by !docs/**
  • docs/study/studySprint02BoardIssuesAndSolutions260821.md is excluded by !docs/**
  • docs/study/studySprint02PostDomainIssues260821.md is excluded by !docs/**
  • docs/studyApiDesign260808.md is excluded by !docs/**
  • docs/studyArchConcepts260806.md is excluded by !docs/**
  • docs/studyArchPrinciples260810.md is excluded by !docs/**
  • docs/studyCommunityPostCommentMaster260821.md is excluded by !docs/**
  • docs/studyDomainErd260807.md is excluded by !docs/**
  • docs/studyPkStrategy260807.md is excluded by !docs/**
  • docs/studySessionAuth260806.md is excluded by !docs/**
  • docs/studySessionFlow260810.md is excluded by !docs/**
  • docs/studySystemArch260810.md is excluded by !docs/**
  • docs/study_sprint01_session_concurrency_jpa_260817.md is excluded by !docs/**
  • frontend/app/components/DeleteConfirmModal.tsx is excluded by !frontend/**
  • frontend/app/lib/api.ts is excluded by !frontend/**
  • frontend/app/posts/[publicId]/page.tsx is excluded by !frontend/**
  • frontend/tsconfig.tsbuildinfo is excluded by !frontend/**
📒 Files selected for processing (38)
  • .env.example
  • .github/workflows/gradle.yml
  • README.md
  • backend/build.gradle
  • backend/src/main/java/com/ikae/snowthing/domain/comment/controller/CommentController.java
  • backend/src/main/java/com/ikae/snowthing/domain/comment/dto/CommentCreateRequest.java
  • backend/src/main/java/com/ikae/snowthing/domain/comment/dto/CommentReplyListResponse.java
  • backend/src/main/java/com/ikae/snowthing/domain/comment/dto/CommentResponse.java
  • backend/src/main/java/com/ikae/snowthing/domain/comment/dto/CommentUpdateRequest.java
  • backend/src/main/java/com/ikae/snowthing/domain/comment/dto/CommentUpdateResponse.java
  • backend/src/main/java/com/ikae/snowthing/domain/comment/dto/PostCommentListResponse.java
  • backend/src/main/java/com/ikae/snowthing/domain/comment/entity/Comment.java
  • backend/src/main/java/com/ikae/snowthing/domain/comment/repository/CommentRepository.java
  • backend/src/main/java/com/ikae/snowthing/domain/comment/repository/CommentRepositoryCustom.java
  • backend/src/main/java/com/ikae/snowthing/domain/comment/repository/CommentRepositoryImpl.java
  • backend/src/main/java/com/ikae/snowthing/domain/comment/service/CommentCommandService.java
  • backend/src/main/java/com/ikae/snowthing/domain/comment/service/CommentService.java
  • backend/src/main/java/com/ikae/snowthing/global/config/DataInitializer.java
  • backend/src/main/java/com/ikae/snowthing/global/error/ErrorCode.java
  • backend/src/main/java/com/ikae/snowthing/global/exception/GlobalExceptionHandler.java
  • backend/src/main/resources/application.yml
  • backend/src/test/java/com/ikae/snowthing/domain/comment/CommentReadTest.java
  • backend/src/test/java/com/ikae/snowthing/domain/comment/controller/CommentControllerTest.java
  • backend/src/test/java/com/ikae/snowthing/domain/comment/dto/CommentResponseTest.java
  • backend/src/test/java/com/ikae/snowthing/domain/comment/entity/CommentTest.java
  • backend/src/test/java/com/ikae/snowthing/domain/comment/service/CommentCreateTest.java
  • backend/src/test/java/com/ikae/snowthing/domain/comment/service/CommentDeleteTest.java
  • backend/src/test/java/com/ikae/snowthing/domain/comment/service/CommentServiceTest.java
  • backend/src/test/java/com/ikae/snowthing/domain/comment/service/CommentUpdateTest.java
  • backend/src/test/java/com/ikae/snowthing/domain/comment/spike/CommentSpikeBenchmarkHarness.java
  • backend/src/test/java/com/ikae/snowthing/domain/comment/spike/CommentSpikeDataInitializer.java
  • backend/src/test/java/com/ikae/snowthing/domain/comment/spike/CommentSpikeDataSeederTest.java
  • backend/src/test/java/com/ikae/snowthing/domain/post/repository/PostRepositoryCustomTest.java
  • backend/src/test/resources/application-test.yml
  • backend/src/test/resources/application.yml
  • database/ddl.sql
  • database/spike_seed_comments.sql
  • docker-compose.yml

📝 Walkthrough

Walkthrough

The change migrates local, test, CI, and container databases to MySQL. It adds cursor-paginated comment reads, bounded reply previews, comment updates, permission handling, locking, reply limits, scale-test utilities, and extensive integration coverage.

Changes

Comment domain and MySQL migration

Layer / File(s)Summary
MySQL environment and initialization
.env.example, .github/workflows/*, backend/.../application*.yml, docker-compose.yml, backend/build.gradle
MySQL 8.0 replaces H2 for tests and uses environment-based credentials across local, CI, and container setups.
Comment model and write operations
backend/src/main/java/.../comment/{controller,dto,entity,repository,service}/*, backend/src/main/java/.../global/*, backend/src/test/java/.../comment/{controller,dto,entity,service}/*
Comment creation, updates, deletion, validation, anonymous access, reply limits, locking, and permission metadata are implemented and tested.
Cursor-paginated comment reads
backend/src/main/java/.../comment/{controller,dto,repository,service}/*, database/ddl.sql, backend/src/test/java/.../comment/CommentReadTest.java
Root comments and replies support cursor pagination, bounded previews, reply statistics, masked identities, deleted-comment handling, and continuation metadata.
Comment scale validation
backend/src/test/java/.../comment/spike/*, database/spike_seed_comments.sql
Distributed and hotspot datasets, benchmark measurements, SQL plans, and Markdown reports support comment-query evaluation.
Documentation and security contracts
README.md
The README documents MySQL usage, CSRF behavior, comment architecture, pagination, indexing, query comparisons, measured results, and updated section structure.

Estimated code review effort: 4 (Complex) | ~60 minutes

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feature/sprint03-comment

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

@devikae
devikae merged commit 24e664c into mainSep 6, 2026
3 of 4 checks passed
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

backendci-cddatabasedocumentationImprovements or additions to documentationfrontend

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant

@devikae