Intro
저는 AI로 문서를 검증하는 작업을 수동으로 돌리다가, 한 번에 너무 많은 요청을 보내 Supabase가 버거워하는 모습을 봤습니다. 그래서 cron 엔드포인트를 만들어 제한된 수의 레코드만 안전하게 처리하도록 만들었습니다.
핵심 아이디어 요약
CRON_SECRETBearer 토큰으로 인증된 호출만 허용합니다.pending상태의 레코드를 최대 100개까지 조회하고, 이미 제출됐거나 첨부가 없는 건 건너뜁니다.- 대상 레코드마다 관리자 API를 호출하고 결과를 모아 반환합니다.
준비와 선택
- 배치 크기: 환경 변수로 배치 크기를 조절하되 기본은 20개, 최대 100개로 제한했습니다.
- 첨부 확인: AI가 처리할 수 없는 케이스를 줄이기 위해 첨부 파일이 있는 레코드만 골랐습니다.
- 결과 집계: 성공/실패 여부와 상태 코드를 배열로 모아 모니터링할 수 있게 했습니다.
구현 여정
Step 1: 인증과 설정
요청 헤더의 Authorization이 Bearer ${CRON_SECRET}인지 확인합니다. 설정이 없으면 500을, 키가 다르면 401을 반환합니다.
Step 2: 후보 레코드 선별
verification_status = 'pending'인 레코드를 오래된 순으로 가져옵니다. 이미 제출된 기록이나 첨부 파일이 없는 경우는 건너뜁니다.
Step 3: AI 처리 트리거
각 레코드마다 내부 API를 호출하고 ok, status, body를 기록합니다. 실패해도 루프를 멈추지 않고 계속 진행합니다.
import { NextRequest } from 'next/server';
for (const recordId of targets) {
try {
const response = await fetch(`${request.nextUrl.origin}/api/admin/records`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ recordId }),
});
const payload = await response.json().catch(() => null);
results.push({
recordId,
ok: response.ok,
status: response.status,
body: payload,
});
} catch (error) {
results.push({
recordId,
ok: false,
status: 0,
error: error instanceof Error ? error.message : 'Unknown error',
});
}
}
겪은 이슈와 해결 과정
- 빈 작업: 처리할 레코드가 없을 때도 cron이 성공했다고 보고하면 의미가 없어서, 메시지와 함께 0건 처리 상태를 반환했습니다.
- 네트워크 오류: 내부 API 호출이 실패하면 예외를 잡아 메시지와 함께
status: 0으로 기록했습니다. - 중복 실행: cron이 중복 호출되어도 같은 레코드를 두 번 요청하지 않도록 제출 여부와 첨부 여부를 꼼꼼히 확인했습니다.
결과와 회고
지금은 cron이 5분마다 실행돼도 Supabase가 버거워하지 않고, 처리 결과를 쉽게 모니터링할 수 있습니다. AI 요청이 실패했을 때도 어떤 레코드에서 문제가 났는지 바로 확인할 수 있게 되었죠. 다음에는 결과를 테이블에 로그로 남겨 추세를 분석해볼 계획입니다.
여러분은 배치 작업을 어떻게 관리하고 계신가요? 안전장치 아이디어가 있다면 댓글로 공유해주세요.
Reference
- https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API
- https://nextjs.org/docs/app/building-your-application/routing/route-handlers