feat: 사이트 전체 검사 기능 추가
도메인 하위 링크를 BFS로 자동 크롤링하여 페이지별 검사 수행. - BFS 링크 크롤러 (같은 도메인 필터링, max_pages/max_depth 설정) - 사이트 검사 오케스트레이션 (크롤링→순차 검사→집계) - SSE 실시간 진행 상태 (크롤링/검사/완료) - 페이지 트리 + 집계 결과 UI - UrlInputForm에 "사이트 전체 검사" 버튼 추가 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
142
frontend/src/hooks/useSiteInspectionSSE.ts
Normal file
142
frontend/src/hooks/useSiteInspectionSSE.ts
Normal file
@ -0,0 +1,142 @@
|
||||
"use client";
|
||||
|
||||
import { useEffect, useRef } from "react";
|
||||
import { useRouter } from "next/navigation";
|
||||
import { useSiteInspectionStore } from "@/stores/useSiteInspectionStore";
|
||||
import { api } from "@/lib/api";
|
||||
import type {
|
||||
SSECrawlProgress,
|
||||
SSECrawlComplete,
|
||||
SSEPageStart,
|
||||
SSEPageComplete,
|
||||
SSEAggregateUpdate,
|
||||
SSESiteComplete,
|
||||
} from "@/types/site-inspection";
|
||||
|
||||
/**
|
||||
* SSE를 통해 사이트 전체 검사 진행 상태를 수신하는 커스텀 훅.
|
||||
* EventSource로 크롤링 + 검사 진행 상태를 실시간 수신하고
|
||||
* Zustand 스토어를 업데이트한다.
|
||||
*/
|
||||
export function useSiteInspectionSSE(siteInspectionId: string | null) {
|
||||
const {
|
||||
setCrawlProgress,
|
||||
setCrawlComplete,
|
||||
updatePageStatus,
|
||||
setPageComplete,
|
||||
updateAggregateScores,
|
||||
setCompleted,
|
||||
setError,
|
||||
} = useSiteInspectionStore();
|
||||
const router = useRouter();
|
||||
const eventSourceRef = useRef<EventSource | null>(null);
|
||||
|
||||
useEffect(() => {
|
||||
if (!siteInspectionId) return;
|
||||
|
||||
const streamUrl = api.getSiteStreamUrl(siteInspectionId);
|
||||
const eventSource = new EventSource(streamUrl);
|
||||
eventSourceRef.current = eventSource;
|
||||
|
||||
/** 크롤링 진행 이벤트 */
|
||||
eventSource.addEventListener("crawl_progress", (e: MessageEvent) => {
|
||||
try {
|
||||
const data: SSECrawlProgress = JSON.parse(e.data);
|
||||
setCrawlProgress(data.pages_found, data.current_url);
|
||||
} catch {
|
||||
// JSON 파싱 실패 무시
|
||||
}
|
||||
});
|
||||
|
||||
/** 크롤링 완료 이벤트 */
|
||||
eventSource.addEventListener("crawl_complete", (e: MessageEvent) => {
|
||||
try {
|
||||
const data: SSECrawlComplete = JSON.parse(e.data);
|
||||
setCrawlComplete(data);
|
||||
} catch {
|
||||
// JSON 파싱 실패 무시
|
||||
}
|
||||
});
|
||||
|
||||
/** 개별 페이지 검사 시작 이벤트 */
|
||||
eventSource.addEventListener("page_start", (e: MessageEvent) => {
|
||||
try {
|
||||
const data: SSEPageStart = JSON.parse(e.data);
|
||||
updatePageStatus(data.page_url, "inspecting");
|
||||
} catch {
|
||||
// JSON 파싱 실패 무시
|
||||
}
|
||||
});
|
||||
|
||||
/** 개별 페이지 검사 완료 이벤트 */
|
||||
eventSource.addEventListener("page_complete", (e: MessageEvent) => {
|
||||
try {
|
||||
const data: SSEPageComplete = JSON.parse(e.data);
|
||||
setPageComplete(data);
|
||||
} catch {
|
||||
// JSON 파싱 실패 무시
|
||||
}
|
||||
});
|
||||
|
||||
/** 집계 점수 업데이트 이벤트 */
|
||||
eventSource.addEventListener("aggregate_update", (e: MessageEvent) => {
|
||||
try {
|
||||
const data: SSEAggregateUpdate = JSON.parse(e.data);
|
||||
updateAggregateScores(data);
|
||||
} catch {
|
||||
// JSON 파싱 실패 무시
|
||||
}
|
||||
});
|
||||
|
||||
/** 사이트 검사 완료 이벤트 */
|
||||
eventSource.addEventListener("complete", (e: MessageEvent) => {
|
||||
try {
|
||||
const data: SSESiteComplete = JSON.parse(e.data);
|
||||
setCompleted(data.aggregate_scores);
|
||||
eventSource.close();
|
||||
// 결과 페이지로 자동 이동
|
||||
router.push(`/site-inspections/${siteInspectionId}`);
|
||||
} catch {
|
||||
// JSON 파싱 실패 무시
|
||||
}
|
||||
});
|
||||
|
||||
/** 에러 이벤트 */
|
||||
eventSource.addEventListener("error", (e: Event) => {
|
||||
if (e instanceof MessageEvent) {
|
||||
try {
|
||||
const data = JSON.parse(e.data);
|
||||
setError(data.message || "사이트 검사 중 오류가 발생했습니다");
|
||||
} catch {
|
||||
setError("사이트 검사 중 오류가 발생했습니다");
|
||||
}
|
||||
}
|
||||
// 네트워크 에러인 경우
|
||||
if (eventSource.readyState === EventSource.CLOSED) {
|
||||
setError("서버와의 연결이 끊어졌습니다");
|
||||
}
|
||||
});
|
||||
|
||||
// SSE 연결 타임아웃 (10분 - 사이트 전체 검사는 시간이 더 소요됨)
|
||||
const timeout = setTimeout(() => {
|
||||
eventSource.close();
|
||||
setError("사이트 검사 시간이 초과되었습니다 (10분)");
|
||||
}, 600000);
|
||||
|
||||
return () => {
|
||||
clearTimeout(timeout);
|
||||
eventSource.close();
|
||||
eventSourceRef.current = null;
|
||||
};
|
||||
}, [
|
||||
siteInspectionId,
|
||||
setCrawlProgress,
|
||||
setCrawlComplete,
|
||||
updatePageStatus,
|
||||
setPageComplete,
|
||||
updateAggregateScores,
|
||||
setCompleted,
|
||||
setError,
|
||||
router,
|
||||
]);
|
||||
}
|
||||
Reference in New Issue
Block a user