Skip to content

TJ 노래방 공식 차트 API 연동으로 popular 페이지 개편 #295

Description

@GulSam00

TJ미디어 공식 사이트(tjmedia.com)에서 제공하는 topAndHot100 API를 매달 1일 배치로 크롤링하여 Supabase에 저장하고, popular 페이지에서 장르별/월별로 조회할 수 있도록 개편한다.

  • API: https://www.tjmedia.com/legacy/api/topAndHot100?chartType=TOP&searchStartDate=YYYY-MM-01&searchEndDate=YYYY-MM-DD&strType=N
  • CORS 제약 없음 (APIDOG 테스트로 확인됨)
  • strType 값에 따라 장르가 달라짐 (참고: https://www.tjmedia.com/chart/top100)
  • 기존 popular 페이지는 포인트 기반 자체 추천(엄지척) 시스템으로 동작 중 — 이번 작업으로 TJ 공식 차트 데이터 기반으로 대체

확정된 결정 사항

  • HOT100(chartType=HOT)은 이번 범위에서 제외, TOP100만 수집
  • 기존 thumb_logs//api/songs/thumb-up/ThumbUpModal은 코드 그대로 유지, popular 페이지 노출 데이터만 TJ 차트로 교체
  • 장르 구분값은 별도 공유 패키지 없이 크롤링/웹 양쪽에 각각 StrType enum으로 중복 정의 (의도적)
  • num_tj 매칭 실패 곡은 저장하지 않고 로그 파일에만 기록 (자동 생성 안 함)
  • 테이블명·컬럼명은 최종적으로 chart_rankings / type 으로 확정 (초안의 tj_chart_rankings / str_type 에서 변경)

최종 구현 결과

DB

create table chart_rankings (
  id uuid primary key default gen_random_uuid(),
  chart_month date not null,
  type text not null default '',
  rank smallint not null,
  song_id uuid not null references songs(id),
  created_at timestamptz not null default now(),
  unique (chart_month, type, rank)
);

postTjChartRankingsDB()onConflict: 'chart_month,type,rank' 로 upsert 하므로 재실행이 안전하다.

크롤링 (packages/crawling)

파일 역할
src/utils/tjChart.ts 차트 조회(fetchTjChart, timeout 10초) · num_tj 매칭 · 로그 테이블 출력 공용 로직
src/cron/crawlTjChart.ts 정기 실행. 전월 1개월분 × 전체 장르 수집 (pnpm tj-chart)
src/cron/crawlTjChartBackfill.ts 과거 월 일괄 백필. 기간은 파일 상단 상수로 지정 (pnpm tj-chart-backfill)
src/supabase/postDB.ts postTjChartRankingsDB() 추가
src/types.ts StrType / STR_TYPE_LABEL / STR_TYPE_API_PARAM / TjChartItem
.github/workflows/crawl_tj_chart.yml 매달 1일 KST 10:00 + 수동 실행

미매칭 목록은 src/assets/tjChartUnmatched.txt(정기) / tjChartBackfillUnmatched.txt(백필)에 append 된다.

웹 (apps/web)

파일 역할
src/app/api/tj-chart/route.ts month / genre 파라미터로 차트 조회 (BFF)
src/app/popular/ChartRankingList.tsx 월 이동 화살표 + 순위 목록 UI
src/app/popular/ChartGenreFilter.tsx 장르 뱃지 필터 (한 줄 가로 스크롤, 단일 선택)
src/app/popular/page.tsx PopularRankingListChartRankingList 교체, force-dynamic 지정
src/lib/api/tjChart.ts, src/queries/tjChartQuery.ts, src/types/tjChart.ts API 함수 / TanStack Query 훅 / 타입
src/utils/kst.ts getPrevMonthFirstDayKST() — KST 기준 전월 1일 계산

UI 동작:

  • 접속 시 KST 기준 전월 차트를 기본 조회 (이번 달은 아직 마감되지 않음)
  • 좌우 화살표로 월 이동. 다음 달은 전월까지, 이전 달은 수집된 가장 오래된 월까지만 이동 가능
  • 장르는 12종을 한 줄 가로 스크롤 뱃지로 렌더링하고 하나만 선택
  • 월/장르 전환 시 keepPreviousData로 목록이 사라지지 않게 처리
  • 전월 계산이 시간 의존이라 프리렌더 시 값이 낡으므로 /popularforce-dynamic

작업 체크리스트

  • packages/crawling에 TJ topAndHot100 API 호출 스크립트 추가 — src/cron/crawlTjChart.ts
  • strType ↔ 장르 매핑 정의 — StrType enum (crawling: src/types.ts, web: src/types/tjChart.ts)
  • Supabase chart_rankings 테이블 생성
  • crawl_tj_chart.yml GitHub Actions 워크플로우 추가 (매달 1일 KST + 수동 실행)
  • popular 페이지 조회용 API 라우트 추가 — /api/tj-chart (month, genre 파라미터)
  • popular 페이지 UI를 장르별/월별 필터 UI로 개편 — ChartRankingList.tsx
  • 월 이동을 좌우 화살표로, 장르 선택을 한 줄 스크롤 뱃지로 개편 (기본 조회 월 = KST 전월)
  • 기존 thumb_count 기반 추천 시스템 처리 방향 결정 — 유지, 페이지 노출만 교체로 확정
  • 과거 월 백필 스크립트 추가 및 2025-01 ~ 2026-01 백필 실행 (미매칭 로그 커밋)
  • /verifypnpm lint, apps/web pnpm build, prettier 통과
  • /commita049b93, 471a452, feeee00(UI 개편), a8c4fd1(timeout·월 파라미터), 8a22dd8(문서)
  • /prFeat : TJ 공식 차트 API 연동으로 popular 페이지 개편 (#295) #296 (base: develop), Vercel 프리뷰 배포 Ready
  • packages/crawling/CLAUDE.md 갱신 (Commands / 워크플로우 표 / 테이블 / TJ 차트 파이프라인)

🚨 남은 블로커: chart_rankings RLS SELECT 정책 누락

chart_rankings에 데이터는 정상 적재되어 있으나(14,434건, type 12종 모두 존재), anon 키로 조회하면 에러 없이 0건이 반환된다. 웹에서 차트가 아무것도 보이지 않는 원인이며, 아래 SQL을 Supabase 대시보드에서 실행해야 해결된다.

alter table chart_rankings enable row level security;

create policy "chart_rankings_select_all"
on chart_rankings for select
to anon, authenticated
using (true);

실측 (crawling service key vs web anon key, 동일 프로젝트):

count 비고
crawling SUPABASE_KEY 14434 월 13개, type 12종 정상
web NEXT_PUBLIC_SUPABASE_ANON_KEY 0 에러 없이 빈 배열 (RLS 차단 패턴)

Qodo 리뷰(#296) 처리 현황

  • fetchTjChart axios timeout 부재 → timeout: 10000 추가 (웹앱 axios 클라이언트와 동일)
  • /api/tj-chart availableMonths 조회 범위 → 종합 차트 상위 10위로 좁혀 14,434건 전송을 129건으로 축소
    • 정정: Qodo가 지적한 "row 상한(1000)에 걸려 과거 월이 잘린다"는 이 프로젝트에서는 재현되지 않았다. 실측 시 전체 select로도 14,434 row가 모두 반환되고 월 목록도 13개월 온전했다(프로젝트 max-rows 설정이 기본값보다 큼). 따라서 기능 결함이 아니라 불필요한 전송량 문제였고, 개선 자체는 유지했다
  • 정기 크롤링 미매칭 로그가 Actions 러너에서 유실 → actions/upload-artifact 스텝 추가 필요
  • 백필 스크립트 체크포인트 부재 → upsert로 재실행이 안전하고 일회성 스크립트이므로 우선순위 낮음 (보류)

남은 작업

  • chart_rankings RLS SELECT 정책 추가 (위 SQL) — 이것 없이는 웹에 차트가 표시되지 않음
  • 정책 추가 후 /popular에서 월 이동·장르 뱃지·빈 상태 UI 최종 확인
  • 미매칭 로그 artifact 업로드 스텝 추가
  • PopularRankingList.tsx 처리 결정 — 렌더 경로가 끊긴 미사용 파일 상태 (유지 / 삭제)
  • 루트 pnpm buildtwa#build에서 bubblewrap 대화형 프롬프트로 실패 — 이번 변경과 무관하나 별도 확인 필요

현재 상태

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions