fix(frontend): 봇 관리 페이지 useEffect → useQuery 변경

- React 18 Strict Mode 중복 요청 방지
- getBots, getQuotaWarning을 useQuery로 전환
- toggleBot에서 queryClient.setQueryData 사용

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
caadiq 2026-01-21 14:25:16 +09:00
parent 2d7d82baf3
commit e3278c81de

View file

@ -1,5 +1,6 @@
import { useState, useEffect } from 'react'; import { useState, useEffect } from 'react';
import { useNavigate, Link } from 'react-router-dom'; import { useNavigate, Link } from 'react-router-dom';
import { useQuery, useQueryClient } from '@tanstack/react-query';
import { motion, AnimatePresence } from 'framer-motion'; import { motion, AnimatePresence } from 'framer-motion';
import { import {
Home, ChevronRight, Bot, Play, Square, Home, ChevronRight, Bot, Play, Square,
@ -44,46 +45,42 @@ const MeilisearchIcon = ({ size = 20 }) => (
function AdminScheduleBots() { function AdminScheduleBots() {
const navigate = useNavigate(); const navigate = useNavigate();
const queryClient = useQueryClient();
const { user, isAuthenticated } = useAdminAuth(); const { user, isAuthenticated } = useAdminAuth();
const { toast, setToast } = useToast(); const { toast, setToast } = useToast();
const [bots, setBots] = useState([]);
const [loading, setLoading] = useState(true);
const [isInitialLoad, setIsInitialLoad] = useState(true); // () const [isInitialLoad, setIsInitialLoad] = useState(true); // ()
const [syncing, setSyncing] = useState(null); // ID const [syncing, setSyncing] = useState(null); // ID
const [quotaWarning, setQuotaWarning] = useState(null); // const [quotaWarning, setQuotaWarning] = useState(null); //
useEffect(() => {
if (isAuthenticated) {
fetchBots();
fetchQuotaWarning();
}
}, [isAuthenticated]);
// //
const fetchBots = async () => { const { data: bots = [], isLoading: loading, isError, refetch: fetchBots } = useQuery({
setLoading(true); queryKey: ['admin', 'bots'],
try { queryFn: botsApi.getBots,
const data = await botsApi.getBots(); enabled: isAuthenticated,
setBots(data); staleTime: 30000,
} catch (error) { });
console.error('봇 목록 조회 오류:', error);
setToast({ type: 'error', message: '봇 목록을 불러올 수 없습니다.' });
} finally {
setLoading(false);
}
};
// //
const fetchQuotaWarning = async () => { const { data: quotaData } = useQuery({
try { queryKey: ['admin', 'bots', 'quota'],
const data = await botsApi.getQuotaWarning(); queryFn: botsApi.getQuotaWarning,
if (data.active) { enabled: isAuthenticated,
setQuotaWarning(data); staleTime: 60000,
} });
} catch (error) {
console.error('할당량 경고 조회 오류:', error); //
useEffect(() => {
if (isError) {
setToast({ type: 'error', message: '봇 목록을 불러올 수 없습니다.' });
} }
}; }, [isError, setToast]);
//
useEffect(() => {
if (quotaData?.active) {
setQuotaWarning(quotaData);
}
}, [quotaData]);
// //
const handleDismissQuotaWarning = async () => { const handleDismissQuotaWarning = async () => {
@ -106,12 +103,14 @@ function AdminScheduleBots() {
await botsApi.stopBot(botId); await botsApi.stopBot(botId);
} }
// ( ) // ( )
setBots(prev => prev.map(bot => queryClient.setQueryData(['admin', 'bots'], (prev) =>
bot.id === botId prev?.map(bot =>
? { ...bot, status: action === 'start' ? 'running' : 'stopped' } bot.id === botId
: bot ? { ...bot, status: action === 'start' ? 'running' : 'stopped' }
)); : bot
)
);
setToast({ setToast({
type: 'success', type: 'success',
message: action === 'start' ? `${botName} 봇이 시작되었습니다.` : `${botName} 봇이 정지되었습니다.` message: action === 'start' ? `${botName} 봇이 시작되었습니다.` : `${botName} 봇이 정지되었습니다.`