검색 K
밝은/어두운 배경
밝은/어두운 배경
게시물 정보
스크래퍼가 중간에 비정상 종료되었는데, 이후 실행에서 다음과 같은 오류가 발생하거나 실행이 즉시 종료됩니다.
크리티컬 런타임 에러: ...또는 GitHub Actions 로그에서 스크래퍼가 락 획득 직후 종료되는 패턴이 반복될 때.
scraper_lock 테이블의 locked = true 상태가 해제되지 않은 것입니다. 정상 종료 시에는 releaseScraperLock()이 호출되지만, 프로세스가 강제 종료되면 finally 블록도 실행되지 않을 수 있습니다.
Neon DB 콘솔 또는 DB 클라이언트에서 다음 쿼리를 실행합니다.
UPDATE scraper_lock
SET locked = false,
locked_at = NULL
WHERE id = 1;현재 상태 확인:
SELECT * FROM scraper_lock WHERE id = 1;로그에서 동일 피드에 [FETCH] ✗ 최종 실패가 반복됩니다.
── [FEED 5/12] 예시 피드 (example.com) ── FAILED
[FETCH] → https://example.com/rss
[FETCH] ⚠ 시도 1/3 실패: 타임아웃 (15s) (1.5s 후 재시도)
[FETCH] ⚠ 시도 2/3 실패: 타임아웃 (15s) (3s 후 재시도)
[FETCH] ✗ 최종 실패: ...registered_feeds 테이블에서 consecutive_failures 값 확인합니다.SELECT feed_url, consecutive_failures, last_failure_at
FROM registered_feeds
WHERE feed_url = 'https://example.com/rss';feed_url을 새 URL로 업데이트합니다.피드 fetch는 성공하지만 신규 포스트가 0건으로 집계됩니다.
[STORE] 파싱 20건 → 당월 0건 → 신규 없음 (누적 15건)당월 필터 확인: 파싱 N건 → 당월 0건이면 RSS 피드에 당월 발행 포스트가 없는 것입니다. 피드 자체의 발행 주기를 확인합니다.
scope 필터 확인: 파싱 0건 → 당월 0건이면 scope 설정이 너무 좁을 수 있습니다. DB에서 해당 피드의 scope 값을 확인합니다.
SELECT feed_url, scope FROM registered_feeds
WHERE feed_url = 'https://example.com/rss';날짜 파싱 확인: 일부 피드는 비표준 날짜 포맷을 사용합니다. feedItemExtractor.ts의 extractPubDate()가 올바르게 파싱하는지 확인합니다. 파싱 실패 시 현재 시각으로 대체되므로 날짜 필터에서 누락될 수 있습니다.
JSON 파일은 정상적으로 저장되는데 Markdown 파일이 생성되지 않습니다.
rebuildMarkdown() 내 경로 조합이 올바른지 src/{folderName}/{year}/{MM}/ 경로를 직접 확인합니다.folderName이나 feedTitle에 파일 시스템에서 허용되지 않는 문자가 포함된 경우 → DB에서 해당 값을 수정합니다.피드를 철회했는데 예상치 못한 파일이 함께 삭제되었습니다.
cleanupService.ts는 withdrawn_at IS NOT NULL인 피드의 Markdown 파일을 전부 삭제합니다. 동일 feedTitle을 가진 다른 피드가 있을 경우 의도치 않게 영향을 받을 수 있습니다.
JSON 파일이 남아 있다면 스크래퍼를 재실행하면 Markdown이 재생성됩니다. JSON 파일도 삭제된 경우에는 Git 이력에서 복구합니다.
git log --oneline -- "src/_data/2026/06/example-com.json"
git checkout {commit-hash} -- "src/_data/2026/06/example-com.json"| 태그 | 의미 |
|---|---|
[INIT] | index.md 생성 등 초기화 작업 |
[FETCH] | RSS XML 수신 시도 및 결과 |
[STORE] | JSON 스토어 집계 결과 |
[BUILD] | Markdown 파일 생성 결과 |
[SKIP] | 중복 경로 등으로 건너뜀 |
[CLEAN] | 고아 파일 삭제 |
[ERROR] | 크리티컬 오류 (glog.err) |
-- 락 상태 확인
SELECT * FROM scraper_lock WHERE id = 1;
-- 락 강제 해제
UPDATE scraper_lock SET locked = false, locked_at = NULL WHERE id = 1;
-- 최근 실패 피드 목록
SELECT feed_url, consecutive_failures, last_failure_at
FROM registered_feeds
WHERE consecutive_failures > 0
ORDER BY consecutive_failures DESC;
-- 철회된 피드 목록 (고아 파일 정리 대상)
SELECT folder_name, feed_title, feed_url, withdrawn_at
FROM registered_feeds
WHERE active = false AND withdrawn_at IS NOT NULL;
-- 활성 피드 전체 목록
SELECT folder_name, feed_title, feed_url, scope, active
FROM registered_feeds
WHERE active = true
ORDER BY folder_name, feed_title;send-reminders.yml 워크플로우가 성공으로 끝났는데 운영자 이메일이 도착하지 않습니다.
SELECT id, folder_name, feed_title, requested_at,
reminder_count, last_reminded_at
FROM feed_requests
WHERE status = 'pending'
AND requested_at < NOW() - INTERVAL '3 days'
AND reminder_count < 5;결과가 없으면 조건에 해당하는 행이 없는 것입니다.
RESEND_API_KEY · ADMIN_EMAIL · REVIEW_BASE_URL Secret이 올바르게 설정되어 있는지 확인합니다.
Resend 대시보드에서 발송 실패 로그를 확인합니다.
이메일 내 승인/반려 링크를 클릭했는데 410 이미 처리되었거나 유효하지 않은 링크입니다. 응답이 옵니다.
| 원인 | 대응 |
|---|---|
| 이미 처리된 링크를 다시 클릭 | 정상 동작. DB에서 현재 상태 확인 |
| 전체 처리와 개별 처리를 동시에 클릭 | 정상 동작. 동시 요청 방어가 작동한 것 |
| 다른 운영자가 먼저 처리 | DB에서 reviewed_at · status 확인 |
-- 처리 상태 확인
SELECT id, feed_title, status, reviewed_at, rejected_reason
FROM feed_requests
WHERE id = <문제의 id>;-- 검토 대기 중인 등록 신청 목록
SELECT id, batch_id, folder_name, feed_title, feed_url,
requested_at, reminder_count
FROM feed_requests
WHERE status = 'pending'
ORDER BY requested_at ASC;
-- 검토 대기 중인 철회 신청 목록
SELECT id, folder_name, feed_title, feed_url, requested_at
FROM withdraw_requests
WHERE status = 'pending'
ORDER BY requested_at ASC;
-- 특정 배치 전체 상태 확인
SELECT id, feed_title, status, reviewed_at, rejected_reason
FROM feed_requests
WHERE batch_id = '<uuid>'
ORDER BY id ASC;
-- 재알림 대상 확인 (5회 미만, 3일 이상 대기)
SELECT id, folder_name, feed_title, requested_at,
reminder_count, last_reminded_at
FROM feed_requests
WHERE status = 'pending'
AND requested_at < NOW() - INTERVAL '3 days'
AND reminder_count < 5;