h-lab/docs/superpowers/specs/2026-06-29-comment-cards-design.md
hehihoho3@gmail.com f309db46b6 docs(comment-cards): 유튜브 댓글 카드 기능 설계서 추가
링크 입력→댓글 수집(commentThreads)→유튜브 스타일 카드 렌더→전체 모자이크→카드별 PNG 클립보드 복사 흐름 정의

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-29 16:42:20 +09:00

6.8 KiB

댓글 카드(Comment Cards) 기능 설계

작성일: 2026-06-29

1. 목적

유튜브 영상 링크를 입력하면 해당 영상의 댓글을 가져와, 댓글마다 유튜브 댓글 UI를 그대로 재현한 카드로 보여준다. 사용자가 카드의 프로필 사진과 아이디를 모자이크(블러) 처리한 뒤, 각 카드를 PNG 이미지로 클립보드에 복사해 영상 편집툴(캡컷 등)에 바로 붙여넣어 영상 하단 소스로 쓰는 것이 목표다.

콘텐츠 제작용 보조 도구이며, DB 저장 없이 일회성 조회/생성으로 동작하는 독립 페이지다.

2. 사용 흐름

  1. 사이드바 "제작" 그룹의 댓글 카드 메뉴 진입 (/comment-cards)
  2. 유튜브 링크(또는 영상 ID) 입력 → [가져오기]
  3. 서버가 YouTube Data API로 댓글을 여러 페이지에 걸쳐 수집해 한 번에 전달
  4. 프론트에서 정렬/필터:
    • 정렬: 좋아요순 / 답글순 / 최신순
    • 필터: 좋아요 N개 이상, 답글 있는 것만
  5. [전체 모자이크] 토글 → 모든 카드의 프로필 + 아이디 블러 처리
  6. 각 카드의 [복사] 버튼 → 그 카드를 PNG 이미지로 만들어 클립보드에 복사
  7. 캡컷 등에서 Ctrl+V로 붙여넣기

3. 아키텍처

기존 web/ + service/ 레이어 컨벤션을 따른다 (YoutubeSearchService / YoutubeSearchApiController와 동일 패턴). DB·엔티티는 추가하지 않는다.

3.1 백엔드

service/YoutubeCommentService (신규)

  • List<CommentCardDto> fetchComments(String videoIdOrUrl)
  • 입력에서 videoId 추출(정규식: v=, youtu.be/, /shorts/, 또는 11자리 ID 그대로)
  • YouTube Data API commentThreads.list 호출:
    • part=snippet
    • videoId={id}
    • order=relevance
    • maxResults=100
    • key={youtube.api.key}
    • pageToken으로 페이지네이션 (최대 N페이지까지만 — 기본 5페이지 = 약 500개, 쿼터/응답크기 보호)
  • 각 thread의 snippet.topLevelComment.snippet에서 추출:
    • authorDisplayName → authorName
    • authorProfileImageUrl → profileImageUrl
    • textDisplay → text (HTML 포함 가능, 프론트에서 안전 렌더링)
    • likeCount → likeCount
    • publishedAt → publishedAt
    • thread의 snippet.totalReplyCount → replyCount
  • 댓글이 비활성화된 영상(403 commentsDisabled) 등은 빈 목록 + 사유 메시지로 처리

web/CommentCardApiController (신규, /api/comment-cards)

  • POST /api/comment-cards/fetch — body { "url": "..." }, 응답 ApiResponse<List<CommentCardDto>>
  • GET /api/comment-cards/avatar?url=... — 프로필 이미지 프록시.
    • 이유: 카드를 PNG로 캡처할 때 외부 도메인(yt3.ggpht.com 등) 이미지는 canvas를 오염(taint)시켜 클립보드 복사가 막힘. 동일 출처로 프록시해 회피.
    • 화이트리스트: *.ggpht.com, *.googleusercontent.com 만 허용(SSRF 방지). 그 외 URL은 거부.
    • 응답: 원본 바이트 + 적절한 Content-Type, 캐시 헤더.

web/dto/CommentCardDto (신규)

  • authorName, profileImageUrl, text, likeCount, replyCount, publishedAt

쿼터: commentThreads.list는 호출당 1유닛. 기존 YoutubeQuotaGuard.tryConsume(pageCount) 적용 — 예산 초과 시 수집 중단하고 그때까지 모은 댓글 반환 + 안내.

3.2 프론트엔드

페이지 라우트: WebController@GetMapping("/comment-cards") 추가, currentPage="comment-cards", 템플릿 comment-cards.html 반환.

사이드바: layout/sidebar.html의 "제작" 그룹에 메뉴 항목 추가 (아이콘 예: message-square-quote).

템플릿 templates/comment-cards.html: layout/base.html 사용, 다크모드 디자인시스템(variables.css) 준수.

  • 상단 입력바: URL input + [가져오기]
  • 필터바: 정렬 셀렉트 + 좋아요 임계값 input + "답글만" 체크 + [전체 모자이크] 토글 버튼
  • 카드 그리드: 유튜브 댓글 스타일 카드
    • 좌측 원형 프로필(프록시 경유 /api/comment-cards/avatar?url=...)
    • 상단 아이디 + 작성시간(상대표기)
    • 본문 댓글 텍스트
    • 하단 좋아요수(👍) + 답글수
    • 우상단/하단 [복사] 버튼
  • 모자이크 상태일 때 프로필 + 아이디에 블러 적용

정적 JS static/js/comment-cards.js:

  • fetch 호출 → 카드 렌더 → 클라이언트 정렬/필터
  • 전체 모자이크 토글 → 카드에 .mosaic 클래스 토글
  • 복사 → 카드 DOM을 PNG로 캡처 → 클립보드(navigator.clipboard.write([new ClipboardItem({'image/png': blob})]))

DOM → 이미지 캡처 라이브러리:

  • modern-screenshot(또는 snapdom) 사용 — CSS filter: blur()를 SVG foreignObject로 충실히 렌더하므로 모자이크가 캡처 결과에 그대로 반영됨. (html2canvas는 blur 필터 미지원이라 부적합.)
  • CDN <script>로 로드(기존 정적 자산 방식과 일관). 빌드 파이프라인 없음.
  • 프로필 이미지는 프록시 동일출처라 taint 없이 캡처 가능.

모자이크 방식: 프로필 사진은 filter: blur(6px)(필요시 추가로 축소-확대 픽셀화), 아이디 텍스트는 filter: blur(5px). 보내준 레퍼런스 이미지 수준의 식별 불가 처리.

4. 데이터 흐름

[브라우저] URL 입력
   → POST /api/comment-cards/fetch
[서버] videoId 추출 → commentThreads.list (페이지네이션, QuotaGuard)
   → List<CommentCardDto> (ApiResponse)
[브라우저] 카드 렌더 (프로필은 /api/comment-cards/avatar 프록시로 로드)
   → 정렬/필터 (클라이언트)
   → 전체 모자이크 토글 (CSS 블러)
   → [복사] → modern-screenshot로 카드 PNG 캡처 → 클립보드

5. 에러 처리

  • 잘못된 URL/videoId 추출 실패 → 400, "유효한 유튜브 링크가 아닙니다"
  • 댓글 비활성화 영상 → 빈 목록 + "이 영상은 댓글이 비활성화되어 있습니다"
  • API 키 오류/쿼터 초과 → GlobalExceptionHandler 경유 에러 응답 + 프론트 토스트
  • 프록시 화이트리스트 외 URL → 400 (SSRF 방지)
  • 클립보드 복사 미지원 브라우저 → PNG 다운로드로 폴백 + 안내

6. 범위 밖 (YAGNI)

  • 댓글 DB 저장/이력
  • 영상 합성/편집 (사용자가 편집툴에서 직접)
  • 답글(대댓글) 펼치기 — 최상위 댓글만
  • 감정/키워드 분석
  • 카드 디자인 커스터마이징(폰트/색상 옵션)

7. 검증

  • 인기 영상 링크로 댓글 100+ 수집 확인
  • 정렬(좋아요/답글/최신), 필터(임계값) 동작
  • 전체 모자이크 토글 → 프로필+아이디 블러
  • [복사] → 클립보드에 PNG 들어가고 캡컷/그림판에 붙여넣기 확인 (모자이크 반영 포함)
  • 댓글 비활성화 영상/잘못된 링크 에러 처리
  • 쿼터 가드 동작(페이지 상한)