Reflect removal of Video/YtVideo/Opal: ChannelVideo is now the single video master with the collect->curate->rework->publish pipeline. Update external-integration section (Python transcript via ChannelService, YouTube API via YoutubeSearchService; Google Docs/Opal gone) and the security note (OAuth creds now unused, gitignored).
6.5 KiB
CLAUDE.md
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
개요
h-lab(아티팩트명 yanalyst)은 유튜브 데이터를 수집·분석하고, 이를 바탕으로 다단계 콘텐츠 제작 파이프라인을 구동하는 개인용 웹 서비스입니다. Spring Boot 3.4.0 / Java 21 백엔드에 서버 사이드 렌더링(Thymeleaf) UI를 사용합니다 (README.md에 언급된 React/Vite 프론트엔드는 SSR로 대체되어 제거됨). 모든 UI는 동일한 Spring Boot 앱에서 제공됩니다.
명령어
Windows에서는 gradlew.bat을 사용합니다. Gradle wrapper는 저장소 루트에 있습니다 (README.md에는 backend/ 디렉토리가 있다고 되어 있으나 실제로는 없음).
# 앱 실행 (UI + API를 http://localhost:8088 에서 제공)
.\gradlew.bat bootRun
# 빌드
.\gradlew.bat build
# 테스트 실행
.\gradlew.bat test
# 단일 테스트 클래스 / 메서드 실행
.\gradlew.bat test --tests "com.hlab.yanalyst.SomeTest"
.\gradlew.bat test --tests "com.hlab.yanalyst.SomeTest.someMethod"
- 서버 포트는 8088 (
application.yml)입니다. README의 8080이 아닙니다. Swagger UI:http://localhost:8088/swagger-ui.html. - 현재
src/test디렉토리가 없습니다 —.\gradlew.bat test는 사실상 빈 상태로 통과합니다. 테스트는src/test/java/com/hlab/yanalyst/아래에 추가하세요.
아키텍처
두 가지 패키지 컨벤션이 공존 (중요)
코드베이스에 두 가지 구조 스타일이 섞여 있습니다. 코드를 추가하기 전에 어느 쪽인지 파악하세요:
domain/<aggregate>/— DDD 스타일 패키지 (channel,production,category,publish). 각 패키지가 Entity + Repository + Service +@RestController(Swagger 문서화,/api/v1/...경로) +dto/를 함께 묶습니다.web/+service/— 이후에 추가된 두 번째 레이어.web/에는 Thymeleaf 페이지 컨트롤러(WebController,ChannelDetailController)와 추가 JSON@RestController들(/api/...경로,v1없음)이 있습니다.service/에는 횡단 관심사인YoutubeSearchService(YouTube Data API 검색)가 있습니다.
영상 모델은 domain/channel/ChannelVideo(테이블 channel_videos)가 단일 마스터입니다. 수집(채널 동기화 + 조회수 검색)→큐레이션→재가공→발행 전 과정이 이 엔티티 하나를 중심으로 돕니다. (과거 Video/YtVideo 및 Opal 파이프라인은 제거됨 — DB에는 videos/yt_video/scriptgen/opal_* 테이블이 ddl-auto:update 특성상 고아 상태로 남아있을 수 있으나 더 이상 매핑되지 않음.) domain/production/ProductionVideo(테이블 production_video)는 별개 개념으로, n8n 크롤 랭킹 스냅샷을 담습니다 — 수집 영상과 혼동하지 마세요.
ChannelVideo 콘텐츠 파이프라인 (앱의 핵심)
ChannelVideo를 "수집→분류→재가공→유통" 흐름으로 진행시키는 것이 앱의 핵심입니다.
- 수집 → 등록 채널 동기화(
source=CHANNEL) 또는 조회수 검색 수집(source=SEARCH,ChannelVideo.fromSearch). 검색은service/YoutubeSearchService(POST /api/youtube/search), 일괄 저장은domain/channel/SearchCollectionService(POST /api/youtube/collect, 중복 자동 스킵). 정기 자동 수집은global/schedule/ScheduledCollectionService. - 큐레이션 →
domain/channel/ChannelVideoCurationService.interestStatus(NEW/REVIEWING/TARGET/DONE/EXCLUDED), 카테고리(domain/category), 북마크, 떡상 지표(viewsPerSubRatio등). 수집함/collection, 칸반 보드/board. - 재가공 → 재가공 작업공간
/rework/{id}. 원본 자막 추출(ChannelService.extractScript→ Python transcript 서비스 호출,channel_video_scripts저장) + 재작성 에디터(ChannelVideo.reworkText, 저장 시 status TARGET 승격). - 발행(유통) →
domain/publish/PublishService+PublishPackage(ChannelVideo와 1:1). 발행 큐/publish, 재가공 화면 하단 "발행 준비" 섹션. 실제 업로드는 수동(URL 기록) — 플랫폼 API 미연동.
외부 연동
- Python transcript 마이크로서비스 (
http://h-python.tolag.shop/transcript) —domain/channel/ChannelService.extractScript가RestTemplate로 직접 호출해 자막을 가져와channel_video_scripts에 저장합니다. - YouTube Data API —
service/YoutubeSearchService(검색)와domain/channel/ChannelService(채널/영상 메타 수집)가youtube.api.key(envYOUTUBE_API_KEY)로 호출. 일일 쿼터는global/schedule/YoutubeQuotaGuard로 가드. - n8n webhook —
production/도메인이 랭킹/크롤 데이터를 위해 연동.
컨벤션
- 영속성: PostgreSQL(원격,
application.yml에 설정), JPAddl-auto: update— 스키마가 엔티티에서 자동 관리되며 마이그레이션 파일은 없습니다. Lombok 전면 사용,@EnableJpaAuditing과@CreationTimestamp/@UpdateTimestamp. p6spy가global/config/P6SpyFormatter로 포맷된 SQL을 로깅합니다. - API 응답: JSON 결과는
global/common/ApiResponse<T>로 감쌉니다 (ApiResponse.ok(...)/.created(...)/.error(...)). 에러는global/error/GlobalExceptionHandler에서 중앙 처리됩니다. - Thymeleaf: 템플릿은
src/main/resources/templates/에 있고, 공유layout/base.html+layout/sidebar.html을 사용합니다(thymeleaf-layout-dialect). 페이지 컨트롤러는 사이드바 하이라이트를 위해currentPage모델 속성을 설정합니다. 정적 CSS/JS는static/에 있으며 다크모드 디자인은variables.css기반입니다. - CORS는
global/config/WebMvcConfig에서 전면 개방되어 있습니다 (allowedOriginPatterns("*"), credentials 허용).
보안 참고
application.yml은 시크릿을 환경변수로 받되 fallback 값이 하드코딩되어 있습니다(DB_URL/DB_USERNAME/DB_PASSWORD, YOUTUBE_API_KEY). 또한 credentials.json / tokens/StoredCredential에 Google OAuth 시크릿이 남아있습니다 — 이는 제거된 Opal(Google Docs 연동) 전용이라 현재 코드에서는 미사용이지만 파일은 여전히 저장소에 존재할 수 있습니다(.gitignore에 추가되어 git 추적에서는 제외). 이 값들을 로그나 외부 서비스에 노출하지 말고, 추가 시크릿을 커밋하라는 요청이 있으면 경고하세요.