완료되거나 실패한 Job도 30초마다 계속 폴링하고 있었다. 조회수 갱신 때문에 넣어둔 건데, 굳이 그렇게 자주 두들길 필요가 없었다.
refetchInterval: (query) => {
const job = query.state.data;
if (!job) return 2000;
- if (job.status === 'COMPLETED' || job.status === 'FAILED') return 30000;
+ if (job.status === 'COMPLETED' || job.status === 'FAILED') return false;
return 2000;
},
대신 채널 페이지 마운트 시 10분마다 sync-videos를 한 번씩 호출해서 조회수·비공개·삭제 상태를 갱신하는 방식으로 바꿨다.
useEffect(() => {
if (!activeChannelId) return;
const timer = setInterval(() => {
apiPost(`/channels/${activeChannelId}/sync-videos`, {}, userHeaders)
.then(() => queryClient.invalidateQueries({ queryKey: ['jobs', activeChannelId] }))
.catch(() => {});
}, 10 * 60 * 1000);
return () => clearInterval(timer);
}, [activeChannelId, queryClient]);
처리 중인(PENDING 등) Job은 여전히 2초 폴링 유지. 안 끝난 것만 자주 보고, 끝난 건 탭 포커스 시에만 자동 재조회하도록 정리했다.
sync-videos를 자주 호출하게 되면서 Analytics 동기화가 계속 실패하고 있다는 걸 눈치챘다. ChannelAnalytics 테이블에 데이터가 안 쌓이고 있었다.
원인을 보니 ids: channel=={youtubeId} 형식이 문제였다. 이 형식은 CMS 환경 전용이라 일반 OAuth 토큰으로는 400 에러가 난다. 근데 이 에러가 .catch(warn)으로 조용히 무시되고 있어서 알아채기 어려웠다.
const res = await ytAnalytics.reports.query({
- ids: `channel==${youtubeId}`,
+ ids: 'channel==MINE',
channel==MINE으로 고쳤더니 이번엔 에러는 안 나는데 값이 전부 0이었다. MINE은 로그인된 Google 계정의 기본 채널 데이터를 반환하는데, 연결된 채널이 브랜드 계정이면 로그인 계정과 다르니까 항상 0이 나온 거다. 결국 원래대로 되돌렸다.
const res = await ytAnalytics.reports.query({
- ids: 'channel==MINE',
+ ids: `channel==${youtubeId}`,
400 에러가 나던 진짜 이유는 파라미터 형식이 아니라 YouTube Analytics API 자체가 아직 활성화 안 돼있었던 거였다. API를 활성화하고 나니 channel=={youtubeId} 형식이 맞는 방식이었다.
원인을 못 찾아서 중간에 응답에 에러를 임시로 노출시켜 디버깅했다.
log.info({ channelId, youtubeId, rowCount: rows.length, sampleRow: rows[0] ?? null }, 'Analytics API 응답');
정렬 방향도 하나 고쳤다. getAnalytics가 오래된 날짜부터 30개를 가져오고 있어서, 데이터가 30일 넘게 쌓이면 최신 데이터가 잘려나가는 문제가 있었다. desc로 최신 30일을 가져온 다음 화면에 표시할 땐 다시 reverse()로 시간순 정렬했다.