5.6 KiB
발굴(Discovery) 전용 페이지 — 설계
작성일: 2026-05-30 상태: 승인됨 (구현 대기)
목적
수집된 영상(ChannelVideo) 중 "재가공할 만한 떡상 후보"를 빠르게 골라내는 일일 작업 화면을 제공한다.
파이프라인 "수집→분류/발굴→재가공→유통"에서 발굴 단계를 전용 화면으로 끌어올린다.
제약: 비용 0 — 새로운 외부 API 호출이나 신규 수집 없이, 이미 보유한 파생 지표(viewsPerHour, viewsPerSubRatio, viewCount, publishedAt)만으로 랭킹/필터한다.
배경 / 현재 상태
- 떡상 발굴은 현재
GET /api/v1/channel-videos/outperformers하나뿐 —viewsPerSubRatio >= minRatio인 Shorts를 비율순으로만 반환하며, 대시보드 TOP 리스트로만 노출된다. 전용 작업 화면이 없다. - 큐레이션 액션(북마크/상태변경/메모/재가공 이동)은
ChannelVideoCurationController(/api/v1/channel-videos/...)에 이미 구현되어 있다 — 재사용한다. - UI 패턴은
collection.html(필터 바 + 테이블 + 행별 인라인 액션)을 따른다.
비범위 (YAGNI)
- 복합 "발굴 점수"(가중치 합산) — 도입하지 않음. 기존 단일 지표 정렬로 충분.
- AI/외부 호출 — 없음.
- 새 액션 엔드포인트 — 없음(기존 큐레이션 API 재사용).
아키텍처 / 컴포넌트
신규로 추가되는 단위는 다음 5개이며 각각 책임이 단일하다.
1. ChannelVideoRepository.discover(...) (신규 쿼리)
수집 영상을 발굴 조건으로 필터링하는 조회 쿼리.
입력(모두 nullable, null이면 해당 필터 미적용):
publishedAfter: LocalDateTime— 이 시각 이후 업로드분만 (최근성 필터)minRatio: BigDecimal—viewsPerSubRatio >= minRatioshortsOnly: boolean— true면isShorts = truesource: String—CHANNEL|SEARCHunprocessedOnly: boolean— true면 상태가NEW또는REVIEWING인 것만sort: Sort— 정렬 기준
규칙: interestStatus = 'EXCLUDED'는 항상 제외한다.
2. ChannelVideoCurationService.discover(...) (신규 서비스 메서드)
파라미터 검증·기본값 적용 후 리포지토리 호출.
periodDays(Integer) →publishedAfter = now - periodDays일(null/0이면 전체)sortBy→ 기존ALLOWED_SORT화이트리스트(viewsPerHour|viewsPerSubRatio|viewCount|publishedAt|durationSec) 재사용, 기본viewsPerSubRatio내림차순minRatio그대로 전달(null이면 미적용)- 결과 개수는
limit(기본 100, 최대 200)으로 캡
3. GET /api/v1/channel-videos/discover (신규 엔드포인트)
ChannelVideoCurationController에 추가.
쿼리 파라미터: periodDays, minRatio, shortsOnly(기본 false), source, unprocessedOnly(기본 false), sortBy, limit.
응답: ApiResponse<List<ChannelVideo>>.
4. discover.html + /discover 라우트
WebController에GET /discover추가 (currentPage = "discover", 뷰discover).- 템플릿은
collection.html구조를 따른다(공유layout/base). - 필터 바: 기간(전체/7/14/30일), 최소 배율(전체/≥2x/≥3x/≥5x), Shorts만 토글, 출처(전체/CHANNEL/SEARCH), 미처리만 토글, 정렬 드롭다운(떡상속도/배율/조회수/최신).
- 결과 테이블: 썸네일 · 제목 · 채널 · 조회수 · 구독자 · 배율(viewsPerSubRatio) · 떡상속도(viewsPerHour) · 업로드일 · 상태뱃지.
- 행별 액션(기존 API 재사용): 북마크 토글(
POST /{id}/bookmark), TARGET 지정(POST /{id}/status), 🪄 재가공(/rework/{id}이동), YouTube 열기(외부 링크).
5. 사이드바 링크
layout/sidebar.html에 "발굴" 항목 추가(수집함과 보드 사이 권장). 아이콘 후보: lucide radar 또는 telescope. currentPage == 'discover'일 때 active.
데이터 흐름
discover.html (필터 변경/진입)
→ GET /api/v1/channel-videos/discover?필터
→ CurationService.discover() → Repository.discover()
→ 행 렌더링
→ 행별 액션 → 기존 POST 엔드포인트(bookmark/status) → 낙관적 UI 갱신
기본값 (페이지 진입 시)
- 기간: 전체
- 최소 배율: 전체(미적용)
- Shorts만: off
- 출처: 전체
- 미처리만: off (단, EXCLUDED는 항상 숨김)
- 정렬: 구독자 대비 배율(viewsPerSubRatio) 내림차순
→ 진입 즉시 "구독자 적은데 조회수 터진" 영상이 상단에 온다.
에러 / 빈 상태
- 결과 0건: "조건에 맞는 발굴 후보가 없습니다" 안내 행.
- API 실패: 테이블에 에러 메시지(
ApiResponse.message) 표시. (collection.html방식과 동일) - 백필 안 된 옛 데이터(
viewsPerSubRatio = null)는 배율 정렬/필터에서 자연히 하단·제외됨 — 별도 처리 불필요(기존 백필 기능으로 보정 가능).
검증 방법
이 프로젝트는 테스트 인프라가 없고(src/test 부재) 원격 운영 DB에 붙으므로, 기존 관행대로 검증한다:
gradlew.bat clean compileJava통과.gradlew.bat bootRun기동(빈 와이어링·엔티티 매핑 정상 확인).- Swagger(
/swagger-ui.html)에서/discover응답 확인 + 브라우저에서/discover페이지 필터/정렬/액션 동작 수동 확인.
영향 / 변경 파일 요약
신규: discover.html, 스펙 문서.
수정: ChannelVideoRepository(메서드 1), ChannelVideoCurationService(메서드 1), ChannelVideoCurationController(엔드포인트 1), WebController(라우트 1), layout/sidebar.html(링크 1).
기존 액션 API·엔티티·다른 페이지는 변경 없음.