From bdb581459d7f9d474abf0cef40d98aba882843c9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EC=84=B1=EA=B2=BD?= Date: Wed, 19 Aug 2026 10:19:54 +0900 Subject: [PATCH] =?UTF-8?q?feat(social):=20=EC=97=85=EB=A1=9C=EB=93=9C=20?= =?UTF-8?q?=ED=8F=BC=EC=97=90=20=EC=A0=80=EC=9E=A5=EB=90=9C=20=EC=98=81?= =?UTF-8?q?=EC=83=81=20SEO=20=EB=A9=94=ED=83=80=EB=8D=B0=EC=9D=B4=ED=84=B0?= =?UTF-8?q?=EB=A5=BC=20=ED=91=9C=EC=8B=9C=20=EA=B0=92=EC=9D=B4=20=EC=9E=88?= =?UTF-8?q?=EC=9C=BC=EB=A9=B4=20DB=20=EA=B2=B0=EA=B3=BC=EB=A5=BC=20?= =?UTF-8?q?=EB=B0=94=EB=A1=9C=20=EC=B1=84=EC=9A=B0=EA=B3=A0,=20=EC=97=86?= =?UTF-8?q?=EC=9C=BC=EB=A9=B4=20video=5Fid=20=EA=B8=B0=EC=A4=80=EC=9C=BC?= =?UTF-8?q?=EB=A1=9C=20=ED=95=9C=20=EB=B2=88=EB=A7=8C=20=EC=83=9D=EC=84=B1?= =?UTF-8?q?=ED=95=9C=EB=8B=A4.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/SocialPostingModal.tsx | 56 +++++++++++---------------- src/types/api.ts | 5 ++- 2 files changed, 27 insertions(+), 34 deletions(-) diff --git a/src/components/SocialPostingModal.tsx b/src/components/SocialPostingModal.tsx index 501c977..50e8cd8 100644 --- a/src/components/SocialPostingModal.tsx +++ b/src/components/SocialPostingModal.tsx @@ -142,10 +142,7 @@ const SocialPostingModal: React.FC = ({ const channelDropdownRef = useRef(null); const privacyDropdownRef = useRef(null); const hasBeenOpenedRef = useRef(false); - const loadedForTaskIdRef = useRef(null); - const loadedAtRef = useRef(0); - const seoCache = useRef>(new Map()); - const SEO_CACHE_TTL = 50 * 60 * 1000; + const loadedForVideoIdRef = useRef(null); // Upload progress modal state const [showUploadProgress, setShowUploadProgress] = useState(false); @@ -212,28 +209,30 @@ const SocialPostingModal: React.FC = ({ // 소셜 계정 로드 useEffect(() => { - if (!isOpen) return; - - const now = Date.now(); + if (!isOpen) { + loadedForVideoIdRef.current = null; + return; + } loadSocialAccounts(); - const taskId = video?.task_id ?? null; - const expired = now - loadedAtRef.current > SEO_CACHE_TTL; - - if (taskId && (taskId !== loadedForTaskIdRef.current || expired)) { - loadedForTaskIdRef.current = taskId; - loadedAtRef.current = now; - loadAutocomplete(); - } else if (taskId) { - const cached = seoCache.current.get(taskId); - if (cached) { - setTitle(cached.title); - setDescription(cached.description); - setTags(cached.tags); - } + const videoId = video?.video_id ?? null; + if (!videoId || videoId === loadedForVideoIdRef.current) { + return; } - }, [isOpen, video?.task_id]); + + loadedForVideoIdRef.current = videoId; + + if (video?.title) { + setTitle(video.title); + setDescription(video.description || ''); + setTags((video.hashtags || []).join(',')); + setIsLoadingAutoDescription(false); + return; + } + + loadAutocomplete(); + }, [isOpen, video?.video_id, video?.title, video?.description, video?.hashtags]); const loadSocialAccounts = async () => { setIsLoadingAccounts(true); @@ -259,29 +258,20 @@ const SocialPostingModal: React.FC = ({ }; const loadAutocomplete = async () => { - if (!video?.task_id) return; + if (!video?.video_id) return; setIsLoadingAutoDescription(true); try { const requestPayload = { - task_id : video.task_id, + video_id: video.video_id, }; - // Call autoSEO API - console.log('[Upload] Request payload:', requestPayload); const autoSeoResponse = await getAutoSeoYoutube(requestPayload); - // 각 필드가 있을 때만 덮어씌움 (기존 값 보호) if (autoSeoResponse.title) setTitle(autoSeoResponse.title); if (autoSeoResponse.description) setDescription(autoSeoResponse.description); if (autoSeoResponse.keywords) setTags(autoSeoResponse.keywords.join(',')); - seoCache.current.set(video.task_id, { - title: autoSeoResponse.title || '', - description: autoSeoResponse.description || '', - tags: autoSeoResponse.keywords?.join(',') || '', - }); } catch (error) { console.error('Failed to load autocomplete:', error); - // 실패해도 사용자에게 별도 알림 없이 조용히 처리 } finally { setIsLoadingAutoDescription(false); } diff --git a/src/types/api.ts b/src/types/api.ts index e75525c..854442e 100644 --- a/src/types/api.ts +++ b/src/types/api.ts @@ -278,6 +278,9 @@ export interface VideoListItem { result_movie_url: string; poster_url?: string | null; thumbnail_url?: string; + title?: string | null; + description?: string | null; + hashtags?: string[] | null; created_at: string; like_count: number; comment_count: number; @@ -394,7 +397,7 @@ export interface SocialDisconnectResponse { // 유튜브 SEO Description 자동완성 요청 export interface YTAutoSeoRequest { - task_id: string; // 아카이브의 비디오 ID + video_id: number; } // 유튜브 SEO Description 자동완성 응답