@@ -4,20 +4,40 @@ import { Button } from "@repo/ui";
44import MissionLoadingCoin from "@repo/ui/svg/mission-loading-coin.svg" ;
55import { useQuery } from "@tanstack/react-query" ;
66import { useRouter } from "next/navigation" ;
7- import { useEffect } from "react" ;
7+ import { useEffect , useState } from "react" ;
88import { buildMissionCreationResultHref } from "@/app/mission/constants/mission-creation" ;
99import { generationJobStatusOptions } from "@/lib/queries/mission-generation" ;
1010import styles from "./mission-loading.module.css" ;
1111
12+ const MIN_LOADING_DURATION_MS = 8_000 ;
13+ const MAX_LOADING_DURATION_MS = 12_000 ;
14+
15+ function getLoadingDuration ( ) {
16+ return (
17+ MIN_LOADING_DURATION_MS +
18+ Math . floor ( Math . random ( ) * ( MAX_LOADING_DURATION_MS - MIN_LOADING_DURATION_MS + 1 ) )
19+ ) ;
20+ }
21+
1222/**
1323 * AI 미션 초안 생성 job이 끝날 때까지 polling한다. jobId는 설문 제출 단계에서 만들어
1424 * URL로 넘겨받는다 — 생성 화면에서 mutation을 쏘지 않으므로 새로고침해도 새 job이 생기지 않고,
1525 * StrictMode에서 mutation 결과가 유실되던 문제도 없다. 반복 폴링은 refetchInterval에 맡긴다.
1626 */
1727export function MissionLoading ( { jobId } : { jobId : string } ) {
1828 const router = useRouter ( ) ;
29+ const [ loadingDuration ] = useState ( getLoadingDuration ) ;
30+ const [ hasMinimumLoadingTimeElapsed , setHasMinimumLoadingTimeElapsed ] = useState ( false ) ;
1931 const { data : job , isError, refetch } = useQuery ( generationJobStatusOptions ( jobId ) ) ;
2032
33+ useEffect ( ( ) => {
34+ const timeoutId = window . setTimeout (
35+ ( ) => setHasMinimumLoadingTimeElapsed ( true ) ,
36+ loadingDuration ,
37+ ) ;
38+ return ( ) => window . clearTimeout ( timeoutId ) ;
39+ } , [ loadingDuration ] ) ;
40+
2141 useEffect ( ( ) => {
2242 const refetchOnAppActive = ( ) => {
2343 void refetch ( ) ;
@@ -27,10 +47,10 @@ export function MissionLoading({ jobId }: { jobId: string }) {
2747 } , [ refetch ] ) ;
2848
2949 useEffect ( ( ) => {
30- if ( job ?. status === "SUCCEEDED" && job . draftsAvailable ) {
50+ if ( hasMinimumLoadingTimeElapsed && job ?. status === "SUCCEEDED" && job . draftsAvailable ) {
3151 router . replace ( buildMissionCreationResultHref ( jobId ) ) ;
3252 }
33- } , [ job , jobId , router ] ) ;
53+ } , [ hasMinimumLoadingTimeElapsed , job , jobId , router ] ) ;
3454
3555 // 5초마다 폴링하므로 일시적인 조회 실패로 "생성 실패"를 띄우면 안 된다 — 다음 폴링이
3656 // 성공할 수 있다. 서버가 FAILED를 주거나, 첫 조회부터 실패해 상태를 아예 못 받은 경우만 실패다.
0 commit comments