From 6a125b55dee5428ae7239e689397ac60c07fc876 Mon Sep 17 00:00:00 2001 From: yoonvinjeong Date: Thu, 4 Jun 2026 21:35:54 +0900 Subject: [PATCH 1/2] =?UTF-8?q?feat:=20=EB=A6=AC=EB=B7=B0=20=EB=B0=98?= =?UTF-8?q?=EC=98=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../components/settings/ProfileSettings.tsx | 2 +- Mine/src/hooks/usePostMagazine.ts | 17 +++--- Mine/src/pages/main/MainPage.tsx | 52 +++++++------------ Mine/src/types/magazine.ts | 2 +- 4 files changed, 29 insertions(+), 44 deletions(-) diff --git a/Mine/src/components/settings/ProfileSettings.tsx b/Mine/src/components/settings/ProfileSettings.tsx index d9b7a34..b2b2291 100644 --- a/Mine/src/components/settings/ProfileSettings.tsx +++ b/Mine/src/components/settings/ProfileSettings.tsx @@ -99,7 +99,7 @@ export default function ProfileSettings({ editMode, onCancelEdit, onSave, refCan autoFocus onChange={(e) => setDraft({ ...draft, nickname: e.target.value })} onBlur={() => setEditingField(null)} - className="border-b border- outlgray-100ine-none font-medium16 pb-1 bg-transparent text-gray-100" + className="border-b border-gray-100 outline-none font-medium16 pb-1 bg-transparent text-gray-100" /> ) : ( { + mutationFn: ({ topic, user_mood }: PostMagazineDto) => postMagazine({ topic, user_mood }), + onMutate: () => { setToast('magazine', 'loading') - try { - const data = await postMagazine({ topic, user_mood }) - setToast('magazine', 'success') - return data - } catch (error) { - setToast('magazine', 'error') - throw error - } }, onSuccess: () => { + setToast('magazine', 'success') queryClient.invalidateQueries({ queryKey: ['mymagazines'] }) }, + onError: () => { + setToast('magazine', 'error') + }, }) -} \ No newline at end of file +} diff --git a/Mine/src/pages/main/MainPage.tsx b/Mine/src/pages/main/MainPage.tsx index 0ecaae4..2a90895 100644 --- a/Mine/src/pages/main/MainPage.tsx +++ b/Mine/src/pages/main/MainPage.tsx @@ -1,11 +1,10 @@ -import { useState, useEffect } from 'react' +import { useState } from 'react' import { useNavigate } from 'react-router-dom' import usePostMagazine from '../../hooks/usePostMagazine' import landingBg from '../../assets/bg1.png' import NewMagazineInput from '../../components/NewMagazineInput' import MakingLoadingPage from './MakingLoadingPage' import { useAuthStore } from '../../stores/auth' -import { useToastStore } from '../../stores/toastStore' import LandingPage from '../landing/LandingPage' import { hasBlockedWord } from '../../utils/blockedWords' import HarmfulKeywordModal from '../../components/common/HarmfulKeywordModal' @@ -17,45 +16,34 @@ export default function MainPage() { const [userMood, setUserMood] = useState('') const [isModalOpen, setIsModalOpen] = useState(false) const { isLoggedIn } = useAuthStore() - const { setToast } = useToastStore() const navigate = useNavigate() const handleSend = () => { - const currentText = topic.trim() - if (!currentText) return - if (hasBlockedWord(currentText)) { + if (!topic.trim()) return + if (hasBlockedWord(topic.trim())) { setIsModalOpen(true) return // 여기서 함수를 끝내버려서 백엔드로 넘어가지 않게 막습니다! } if (isPending) return - postMagazineMutation.mutate({ topic: currentText, user_mood: userMood }) - } - useEffect(() => { - if (postMagazineMutation.isPending) { - setToast('magazine', 'loading') - } else if (postMagazineMutation.isSuccess) { - setToast('magazine', 'success') + // 💡 핵심 수정: mutate 함수 두 번째 인자로 옵션 객체를 넘깁니다! + postMagazineMutation.mutate( + { topic, user_mood: userMood }, + { + // API 통신이 "딱 한 번" 성공했을 때만 이 코드가 실행됩니다. (useEffect 버그 해결!) + onSuccess: (data) => { + // Toast는 usePostMagazine 훅 안에서 이미 띄웠으므로 생략! - // 성공 토스트를 1.5초간 보여준 후 매거진 상세 페이지로 부드럽게 이동 - setTimeout(() => { - // 백엔드 응답(data) 자체가 ID이거나 객체 내부에 있는 경우 모두 대응 - const responseData = postMagazineMutation.data - const newMagazineId = responseData?.magazineId || responseData - if (newMagazineId) navigate(`/${newMagazineId}`) - else navigate('/explore') - }, 1500) - } else if (postMagazineMutation.isError) { - setToast('magazine', 'error') - } - }, [ - postMagazineMutation.isPending, - postMagazineMutation.isSuccess, - postMagazineMutation.isError, - postMagazineMutation.data, - navigate, - setToast, - ]) + // 1.5초 뒤에 넘어가는 로직만 여기에 작성합니다. + setTimeout(() => { + const newMagazineId = data?.magazineId || data + if (newMagazineId) navigate(`/${newMagazineId}`) + else navigate('/explore') + }, 1500) + }, + } + ) + } if (!isLoggedIn) return diff --git a/Mine/src/types/magazine.ts b/Mine/src/types/magazine.ts index d6c19e4..83e4caf 100644 --- a/Mine/src/types/magazine.ts +++ b/Mine/src/types/magazine.ts @@ -70,7 +70,7 @@ type Section = { heading: string thumbnailUrl: string paragraphs: Paragraph[] - displayOrder: 0 + displayOrder: number sourceUrl: string sectionId: number } From b53e0cd590065475f0c0b2d5ccfd26ebd8d26ec4 Mon Sep 17 00:00:00 2001 From: yoonvinjeong Date: Fri, 5 Jun 2026 01:25:14 +0900 Subject: [PATCH 2/2] =?UTF-8?q?feat:=20=EC=A0=9C=EB=AF=B8=EB=82=98?= =?UTF-8?q?=EC=9D=B4=20=EB=A6=AC=EB=B7=B0=20=EB=B0=98=EC=98=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Mine/src/pages/main/MainPage.tsx | 25 ++++++++++++++++--------- 1 file changed, 16 insertions(+), 9 deletions(-) diff --git a/Mine/src/pages/main/MainPage.tsx b/Mine/src/pages/main/MainPage.tsx index 2a90895..a403893 100644 --- a/Mine/src/pages/main/MainPage.tsx +++ b/Mine/src/pages/main/MainPage.tsx @@ -1,4 +1,4 @@ -import { useState } from 'react' +import { useEffect, useRef, useState } from 'react' import { useNavigate } from 'react-router-dom' import usePostMagazine from '../../hooks/usePostMagazine' import landingBg from '../../assets/bg1.png' @@ -18,9 +18,20 @@ export default function MainPage() { const { isLoggedIn } = useAuthStore() const navigate = useNavigate() + const timerRef = useRef | null>(null) + + useEffect(() => { + return () => { + if (timerRef.current) { + clearTimeout(timerRef.current) // 화면이 닫히면 타이머 폭파! + } + } + }, []) + const handleSend = () => { - if (!topic.trim()) return - if (hasBlockedWord(topic.trim())) { + const trimmedTopic = topic.trim() + if (!trimmedTopic) return + if (hasBlockedWord(trimmedTopic)) { setIsModalOpen(true) return // 여기서 함수를 끝내버려서 백엔드로 넘어가지 않게 막습니다! } @@ -28,17 +39,13 @@ export default function MainPage() { // 💡 핵심 수정: mutate 함수 두 번째 인자로 옵션 객체를 넘깁니다! postMagazineMutation.mutate( - { topic, user_mood: userMood }, + { topic: trimmedTopic, user_mood: userMood }, { // API 통신이 "딱 한 번" 성공했을 때만 이 코드가 실행됩니다. (useEffect 버그 해결!) onSuccess: (data) => { - // Toast는 usePostMagazine 훅 안에서 이미 띄웠으므로 생략! - - // 1.5초 뒤에 넘어가는 로직만 여기에 작성합니다. - setTimeout(() => { + timerRef.current = setTimeout(() => { const newMagazineId = data?.magazineId || data if (newMagazineId) navigate(`/${newMagazineId}`) - else navigate('/explore') }, 1500) }, }