h-lab/docs/superpowers/specs/2026-06-25-recommended-channel-discovery-design.md
hehihoho3@gmail.com 9fed1e04b9 docs(spec): 추천 채널 발굴(떡상 Shorts) 설계 추가
지역(KR,JP,US) 인기 Shorts 주기 검색 → 작은구독자·고배율 떡상 채널 발굴 →
RecommendedChannel 저장 → /recommend 페이지에서 등록/제외. 쿼터 가드·기존 검색 재사용.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-26 10:07:23 +09:00

6.9 KiB
Raw Blame History

추천 채널 발굴 (떡상 Shorts 기반) 설계

  • 날짜: 2026-06-25
  • 상태: 설계 확정(구현 대기)

1. 목적

사이트가 주기적으로(일 1회) 지역별 인기 Shorts 를 광범위 검색해, "구독자는 적은데 조회수가 폭발한"(떡상) 채널을 자동 발굴하고, 상위 N개를 추천 채널로 모아 사용자가 /recommend 페이지에서 접속해 보고, 마음에 들면 내 채널로 등록하거나 제외할 수 있게 한다.

기존 /discover 발굴은 이미 수집된 영상 중 떡상 후보를 찾는 것이고, 이 기능은 새로운 채널을 능동적으로 발굴해 추천하는 별도 기능이다.

2. 배경 / 재사용 대상

  • service/YoutubeSearchService.searchYoutubeVideos(YoutubeSearchCondition): 지역(regionCode) 단위로 YouTube search.list 검색 + 각 영상의 채널 구독자/메타 조회. 결과에 channelId·channelTitle·구독자·조회수 포함. → 광범위 Shorts 검색에 재사용(type=video, videoDuration=short, order=viewCount, regionCode 별).
  • global/schedule/YoutubeQuotaGuard: 일일 쿼터 예산 가드. → 발굴도 이 가드 안에서 동작.
  • global/schedule/ScheduledCollectionService: 기존 일별 스케줄. → 발굴 작업을 여기에 추가(또는 병행).
  • domain/channel/Channel + ChannelService.saveChannelFromUrl/채널 저장: "내 채널 등록" 시 재사용.
  • 배율(조회수÷구독자) 개념: 기존 viewsPerSubRatio 와 동일 기준.

3. 데이터 흐름

[스케줄러 일1회 (cron 설정)]  enabled & 쿼터 여유?
   └─ regions(KR,JP,US) 각각:
        YoutubeSearchService 로 인기 Shorts 검색(order=viewCount, short, pages 설정)
        → 영상별 채널 구독자 → 배율 계산
   ▼
채널 단위 집계: 채널별 '최고 배율 영상' 1건 선정
   ▼
필터: 구독자 ≤ maxSubscribers AND 배율 ≥ minRatio
       AND 이미 등록된 채널(Channel) 아님
       AND RecommendedChannel.status = EXCLUDED 아님
   ▼
RecommendedChannel upsert(channelId 기준): 메타(대표영상·배율·구독자·발견일) 저장/갱신
   ▼
[/recommend 페이지] status=NEW 를 배율 desc 정렬, 상위 N 표시
   카드: 썸네일·채널명·구독자·대표 떡상영상(제목·조회수·배율)·샘플 링크
   액션: [내 채널 등록] → Channel 생성 + status=REGISTERED
         [제외]        → status=EXCLUDED (다음 발굴에서 제외)

4. 컴포넌트

4.1 RecommendedChannel 엔티티 + RecommendedChannelRepository (신규, domain/channel)

  • 필드: id, channelId(unique), channelTitle, thumbnailUrl, subscriberCount, status(NEW|REGISTERED|EXCLUDED), 대표영상(topVideoId,topVideoTitle,topVideoViewCount,ratio), region(발견 지역), discoveredAt, updatedAt(@CreationTimestamp/@UpdateTimestamp).
  • Repo: findByChannelId, findByStatusOrderByRatioDesc, 존재여부 조회 등.

4.2 ChannelDiscoveryService (신규, domain/channel 또는 service)

  • 검색 API 선택(구현 시 확정): 키워드 없는 "광범위 인기 Shorts"는 search.list(q 필요, 100units)보다 videos.list chart=mostPopular + regionCode(1 unit, 키워드 불필요) 가 적합하다. mostPopular 결과를 duration ≤ 60s(Shorts)로 필터링해 후보로 삼는다. 단 mostPopular 는 카테고리 제한이 있을 수 있어, 결과가 빈약하면 search.list(order=viewCount, videoDuration=short, 일반 q 또는 카테고리)로 보강한다. → YoutubeSearchService 에 region 기반 인기영상 조회 메서드를 추가하거나 신규 호출 경로를 둔다.
  • Map<String,Object> runDiscovery():
    1. regions 별 인기 Shorts 조회(쿼터 가드로 지역마다 잔여 확인 후 진행).
    2. 결과 영상 → 채널별 최고 배율 집계(Map<channelId, Candidate>).
    3. 필터(구독자 상한·배율 하한·등록됨 제외·EXCLUDED 제외).
    4. RecommendedChannel upsert(있으면 더 좋은 메트릭으로 갱신, status 가 NEW 일 때만).
    5. 요약 반환(검색 지역수, 후보수, 신규 저장수, 소비 쿼터).
  • 쿼터 부족 시 해당 지역 건너뛰고 로그(기존 패턴).

4.3 스케줄 연결

  • ScheduledCollectionService@Scheduled(cron=${hlab.scheduler.channel-discovery.cron}) 추가 → channelDiscoveryService.runDiscovery(). enabled 플래그로 on/off. 수동 실행용 메서드도 제공.

4.4 컨트롤러 (RecommendedChannelController, 신규)

  • GET /api/recommended-channels?limit= → status=NEW, 배율 desc 상위 목록(ApiResponse).
  • POST /api/recommended-channels/{id}/register → 내 채널 등록(Channel 생성) + status=REGISTERED.
  • POST /api/recommended-channels/{id}/exclude → status=EXCLUDED.
  • POST /api/recommended-channels/run → 수동 발굴 트리거(선택, 관리용).

4.5 웹 페이지 /recommend (신규 recommend.html + WebController 라우트)

  • 사이드바에 "추천 채널" 메뉴 추가(currentPage="recommend").
  • 추천 채널 카드 그리드: 썸네일·채널명·구독자·대표 떡상영상(배율 배지)·YouTube 링크.
  • 각 카드 [내 채널 등록]/[제외] 버튼 → 위 API 호출 후 목록 갱신.
  • 빈 상태/로딩/사용법 안내(기존 페이지 톤 따름).

5. 설정 (application.yml, 기본값 有)

hlab:
  scheduler:
    channel-discovery:
      enabled: ${CHANNEL_DISCOVERY_ENABLED:true}
      cron: ${CHANNEL_DISCOVERY_CRON:0 30 4 * * *}   # 매일 04:30 (기존 수집 04:00 이후)
  discovery:
    regions: ${DISCOVERY_REGIONS:KR,JP,US}
    max-subscribers: ${DISCOVERY_MAX_SUBS:100000}    # 작은 채널 상한
    min-ratio: ${DISCOVERY_MIN_RATIO:5.0}            # 떡상 배율 하한
    pages-per-region: ${DISCOVERY_PAGES:1}           # 지역당 검색 페이지 수
    top-n: ${DISCOVERY_TOP_N:30}                     # 추천 페이지 노출 상한

6. 쿼터/성능

  • search.list = 지역·페이지당 약 100 units, channels.list(구독자) = 50채널당 1 unit. KR,JP,US × 1페이지 ≈ 300 units + 수십 units → 일일 한도(기본 10000) 내 충분.
  • YoutubeQuotaGuard.remaining() 확인 후 지역 단위로 진행/건너뜀.

7. 에러 처리

  • 특정 지역 검색 실패 → 그 지역만 건너뛰고 나머지 진행(로그).
  • 채널 등록 실패(이미 존재 등) → 사용자에게 메시지, 추천 항목은 REGISTERED 로 정리.
  • 쿼터 소진 → 발굴 중단, 다음 주기 재시도.

8. 테스트 (src/test)

순수 로직 위주:

  • 집계/랭킹: 영상 목록 → 채널별 최고 배율 선정, 구독자 상한·배율 하한 필터.
  • dedup: 등록된 channelId·EXCLUDED 제외.
  • 실제 YouTube/스케줄은 라이브/수동 검증.

9. 범위 제외 (YAGNI)

  • 구독자 증가율 추적(스냅샷 누적 필요)
  • 키워드 기반 검색(이번엔 지역 광범위)
  • 자동 등록(수동 검토 유지)
  • 추천 사유 다중 지표(이번엔 배율 단일 기준)