From b1628b9cbb2f32d979a480b0cbf5c0c3b0ef2e42 Mon Sep 17 00:00:00 2001 From: Jungmin Date: Wed, 16 Oct 2024 00:42:35 +0900 Subject: [PATCH 01/19] WIP --- src/api/goalAPI.ts | 14 +- src/api/todoAPI.ts | 27 +- src/app/(auth)/components/LoginForm.tsx | 56 +-- src/app/(auth)/components/SignupForm.tsx | 69 ++-- .../dashboard/components/TodoCard copy.tsx | 147 ++++++++ src/app/dashboard/components/TodoCard.tsx | 18 +- src/app/dashboard/components/Todos copy.tsx | 265 ++++++++++++++ src/app/dashboard/components/Todos.tsx | 54 +-- src/app/dashboard/goal/[goalId]/page.tsx | 74 ++-- src/app/dashboard/note/[noteId]/page.tsx | 95 ++--- src/app/dashboard/page copy.tsx | 190 ++++++++++ src/app/dashboard/page.tsx | 130 ++++--- src/app/dashboard/todoboard/page.tsx | 84 +++-- src/components/CreateNewTodo copy.tsx | 325 ++++++++++++++++++ src/components/CreateNewTodo.tsx | 143 ++++---- src/store/goalStore.ts | 2 +- src/store/todoStore copy.ts | 128 +++++++ src/store/todoStore.ts | 130 +++++++ 18 files changed, 1568 insertions(+), 383 deletions(-) create mode 100644 src/app/dashboard/components/TodoCard copy.tsx create mode 100644 src/app/dashboard/components/Todos copy.tsx create mode 100644 src/app/dashboard/page copy.tsx create mode 100644 src/components/CreateNewTodo copy.tsx create mode 100644 src/store/todoStore copy.ts create mode 100644 src/store/todoStore.ts diff --git a/src/api/goalAPI.ts b/src/api/goalAPI.ts index 0bd95d6..63b7ca2 100644 --- a/src/api/goalAPI.ts +++ b/src/api/goalAPI.ts @@ -14,13 +14,17 @@ export interface ErrorType { } // 목표 목록 가져오기 (GET) +// 모든 목표를 가져오는 함수 export const getGoals = async () => { try { const response = await api.get(`/goals`); return response.data; } catch (error) { - const axiosError = error as AxiosError; - toast.error(axiosError.message); + console.error("Goals fetch error:", error); + // const axiosError = error as AxiosError; + // toast.error( + // axiosError.response?.data?.message || axiosError.message || "목표 목록을 가져오는 중 오류가 발생했습니다.", + // ); } }; @@ -35,13 +39,15 @@ export const PostGoal = async (title: string) => { } }; +// 특정 ID로 목표를 가져오는 함수 export const getGoal = async (id: number) => { try { const response = await api.get(`/goals/${id}`); return response.data; } catch (error) { - const axiosError = error as AxiosError; - toast.error(axiosError.message); + console.error("Goal fetch error:", error); + // const axiosError = error as AxiosError; + // toast.error(axiosError.response?.data?.message || axiosError.message || "목표를 가져오는 중 오류가 발생했습니다."); } }; diff --git a/src/api/todoAPI.ts b/src/api/todoAPI.ts index 72248a6..311a54e 100644 --- a/src/api/todoAPI.ts +++ b/src/api/todoAPI.ts @@ -5,13 +5,36 @@ import api from "@/lib/api"; import { ErrorType } from "./goalAPI"; -export type TodoType = { +export type Todo = { title: string; linkUrl: string | null; fileUrl: string | null; goalId: number; }; +export type TodoType = { + noteId?: number | null; + done: boolean; + linkUrl?: string | null; + fileUrl?: string | null; + title: string; + id: number; + goal: GoalType; + userId: number; + teamId: string; + updatedAt: string; + createdAt: string; +}; + +export type GoalType = { + id: number; + teamId: string; + title: string; + userId: number; + createdAt: string; + updatedAt: string; +}; + export const getAllData = async () => { try { const response = await api.get(`/todos`); @@ -120,7 +143,7 @@ export const editTodo = async ( fileUrl?: string | null, linkUrl?: string | null, todoId?: number, -) => { +): Promise => { try { const payload: { title: string; diff --git a/src/app/(auth)/components/LoginForm.tsx b/src/app/(auth)/components/LoginForm.tsx index 716393c..825028d 100644 --- a/src/app/(auth)/components/LoginForm.tsx +++ b/src/app/(auth)/components/LoginForm.tsx @@ -1,23 +1,20 @@ "use client"; +import { useState } from "react"; import { useRouter } from "next/navigation"; import { zodResolver } from "@hookform/resolvers/zod"; import { useForm } from "react-hook-form"; import { z } from "zod"; import { AxiosError } from "axios"; -import { useMutation } from "@tanstack/react-query"; -import { toast } from "react-toastify"; import { login } from "@/utils/authUtils"; import LoadingSpinner from "@/components/LoadingSpinner"; -import { ErrorResponse } from "@/app/Types/AuthType"; - -type FormFields = "email" | "password"; type FormData = z.infer; export default function LoginForm() { const router = useRouter(); + const [loginError, setLoginError] = useState(null); const { register, @@ -29,30 +26,22 @@ export default function LoginForm() { resolver: zodResolver(schema), }); - const loginMutation = useMutation({ - mutationFn: (data: { email: string; password: string }) => login(data.email, data.password), - onSuccess: (data) => { - if (data) { - toast.success("로그인 되었습니다."); + const onSubmit = async (data: FormData) => { + clearErrors(); + setLoginError(null); + + try { + const success = await login(data.email, data.password); + if (success) { router.push("/dashboard"); } else { - toast.error("로그인에 실패했습니다. 다시 시도해 주세요."); - } - }, - onError: (error: AxiosError) => { - if (error.response) { - const statusCode = error.response.status; - const errorInfo = errorMessage[statusCode]; - if (errorInfo) { - toast.error(errorInfo.message); - } + setLoginError("로그인에 실패했습니다. 다시 시도해 주세요."); } - }, - }); - - const onSubmit = (data: FormData) => { - clearErrors(); - loginMutation.mutate(data); + } catch (error) { + const axiosError = error as AxiosError; // Type assertion + console.error("로그인 중 오류 발생:", axiosError); + setLoginError("로그인 실패했습니다. 다시 시도해 주세요."); + } }; return ( @@ -88,9 +77,9 @@ export default function LoginForm() { onBlur={() => trigger("password")} /> {errors.password && {errors.password.message}} + {loginError && {loginError}} + + +
+ {" "} +
+ +
+
+

To do

+
    + {activeTodos.slice(0, 5).map((todo) => ( + + ))} +
+ {activeTodos.length === 0 && ( +
+
해야할 일이 아직 없어요.
+
+ )} +
+
+

Done

+
    + {completedTodos.slice(0, 5).map((todo) => ( + + ))} +
+ {completedTodos.length === 0 && ( +
+
다 한 일이 아직 없어요.
+
+ )} +
+
+ + {showMore && ( +
+ +
+ )} + + + + + + ); +} diff --git a/src/app/dashboard/components/TodoCard.tsx b/src/app/dashboard/components/TodoCard.tsx index 7e9a814..0d3ee2b 100644 --- a/src/app/dashboard/components/TodoCard.tsx +++ b/src/app/dashboard/components/TodoCard.tsx @@ -14,7 +14,6 @@ import ProgressBar from "./ProgressBar"; export type TodoCardProps = { id: number; - onTodoUpdate: (updatedTodo: TodoType) => void; }; export type GoalType = { @@ -40,21 +39,22 @@ export type TodoType = { createdAt: string; }; -export default function TodoCard({ id, onTodoUpdate }: TodoCardProps) { +export default function TodoCard({ id }: TodoCardProps) { const router = useRouter(); const { Modal, openModal, closeModal } = useModal(); const [progress, setProgress] = useState(0); const [goals, setGoals] = useState(null); const [todos, setTodos] = useState([]); + //const { todos, setTodos, updateTodo } = useTodoStore(); const activeTodos = Array.isArray(todos) ? todos.filter((todo) => !todo.done) : []; const completedTodos = Array.isArray(todos) ? todos.filter((todo) => todo.done) : []; const showMore = activeTodos.length > 5 || completedTodos.length > 5; - const handleTodoUpdate = async (updatedTodo: TodoType) => { - onTodoUpdate(updatedTodo); - setTodos((prevTodos) => prevTodos.map((todo) => (todo.id === updatedTodo.id ? { ...todo, ...updatedTodo } : todo))); - }; + // const handleTodoUpdate = async (updatedTodo: TodoType) => { + // onTodoUpdate(updatedTodo); + // setTodos((prevTodos) => prevTodos.map((todo) => (todo.id === updatedTodo.id ? { ...todo, ...updatedTodo } : todo))); + // }; useEffect(() => { setProgress(Math.round((completedTodos.length / todos.length) * 100)); @@ -102,7 +102,7 @@ export default function TodoCard({ id, onTodoUpdate }: TodoCardProps) {

To do

    {activeTodos.slice(0, 5).map((todo) => ( - + ))}
{activeTodos.length === 0 && ( @@ -115,7 +115,7 @@ export default function TodoCard({ id, onTodoUpdate }: TodoCardProps) {

Done

    {completedTodos.slice(0, 5).map((todo) => ( - + ))}
{completedTodos.length === 0 && ( @@ -139,7 +139,7 @@ export default function TodoCard({ id, onTodoUpdate }: TodoCardProps) { )} - + ); diff --git a/src/app/dashboard/components/Todos copy.tsx b/src/app/dashboard/components/Todos copy.tsx new file mode 100644 index 0000000..4edb47b --- /dev/null +++ b/src/app/dashboard/components/Todos copy.tsx @@ -0,0 +1,265 @@ +"use client"; + +import Image from "next/image"; +import { useRouter } from "next/navigation"; +import { useEffect, useRef, useState } from "react"; + +import { deleteTodos, patchTodo } from "@/api/todoAPI"; +import useModal from "@/hook/useModal"; +import CreateNewTodo from "@/components/CreateNewTodo"; +import { getNotes } from "@/api/noteAPI"; +import useTodoStore from "@/store/todoStore"; + +import NoteViewer from "./NoteViewer"; + +export type GoalType = { + updatedAt: string; + createdAt: string; + title: string; + id: number; + userId: number; + teamId: string; +}; + +export type TodoType = { + noteId: number | null; + done: boolean; + linkUrl: string | null; + fileUrl: string | null; + title: string; + id: number; + goal: GoalType; + userId: number; + teamId: string; + updatedAt: string; + createdAt: string; +}; + +type TodoProps = { + todo: TodoType; + isGoal?: boolean; + isInGoalSection?: boolean; +}; + +type toggleTodoStatusType = { + title: string; + goalId: number; + fileUrl: string; + linkUrl: string; + done: boolean; + todoId: number; +}; + +export interface NoteType { + content: string; + createdAt: string; + goal: { + id: number; + title: string; + }; + id: number; + linkUrl: string; + teamId: string; + title: string; + todo: { + done: boolean; + fileUrl: string | null; + id: number; + linkUrl: string | null; + title: string; + }; + updatedAt: string; + userId: number; +} + +export default function Todos({ todo, isGoal = false, isInGoalSection = false }: TodoProps) { + const router = useRouter(); + const [isOpen, setIsOpen] = useState(false); + const dropdownRef = useRef(null); + const { Modal, openModal, closeModal } = useModal(); + const [isNoteOpen, setIsNoteOpen] = useState(false); + const [noteContent, setNoteContent] = useState(); + const { updateTodo } = useTodoStore(); + + const toggleTodoStatus = async ({ title, goalId, fileUrl, linkUrl, done, todoId }: toggleTodoStatusType) => { + const updatedTodo = { ...todo, done: !done }; + onTodoUpdate(updatedTodo); + try { + await patchTodo(title, goalId, !done, todoId, fileUrl, linkUrl); + } catch (error) { + console.error("할 일 상태 변경 중 오류 발생:", error); + onTodoUpdate({ ...updatedTodo, done: done }); + } + }; + + const fetchNoteContent = async () => { + if (todo.noteId) { + const response = await getNotes(todo.noteId); + if (response) { + setNoteContent(response); + } + } + }; + + // const handleTodoUpdate = (updatedTodo: TodoType) => { + // setTodos((prevTodos) => + // prevTodos.map((prevTodo) => (prevTodo.id === updatedTodo.id ? { ...prevTodo, ...updatedTodo } : prevTodo)), + // ); + // }; + + const handleDelete = async () => { + try { + const response = await deleteTodos(todo.id as number); + return response; + } catch (error) { + console.error("목표 삭제 중 오류 발생:", error); + } + }; + + useEffect(() => { + const handleClickOutside = (event: MouseEvent) => { + if (isOpen && dropdownRef.current && !dropdownRef.current.contains(event.target as Node)) { + setIsOpen(false); + } + }; + + document.addEventListener("mousedown", handleClickOutside); + + return () => { + document.removeEventListener("mousedown", handleClickOutside); + }; + }, [isOpen]); + + useEffect(() => { + fetchNoteContent(); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, []); + + return ( +
+
    +
  • + checkbox-icon + toggleTodoStatus({ + title: todo.title, + goalId: todo.goal.id, + fileUrl: todo.fileUrl as string, + linkUrl: todo.linkUrl as string, + done: todo.done, + todoId: todo.id, + }) + } + /> + + {todo.title} +
  • + {isGoal && ( +
    + goal-summit-icon +

    {todo.goal?.title}

    +
    + )} + +
    + {todo.fileUrl && ( + + 첨부 파일 + + )} + {todo.linkUrl && ( + + 첨부 링크 + + )} + 노트 작성/수정 router.push(`/dashboard/note/${todo.id}?goalId=${todo.goal.id}`)} + /> + {todo.noteId && ( + 노트 보기 setIsNoteOpen((prev) => !prev)} + /> + )} + 수정/삭제 { + setIsOpen((prev) => !prev); + }} + /> + + {isOpen && ( +
    +

    { + openModal("EDIT_TODO"); + if (isOpen) setIsOpen(false); + }} + > + 수정하기 +

    +

    + 삭제하기 +

    +
    + )} +
    +
+ + + + +
+ ); +} diff --git a/src/app/dashboard/components/Todos.tsx b/src/app/dashboard/components/Todos.tsx index 0f95589..59f4ee8 100644 --- a/src/app/dashboard/components/Todos.tsx +++ b/src/app/dashboard/components/Todos.tsx @@ -8,6 +8,7 @@ import { deleteTodos, patchTodo } from "@/api/todoAPI"; import useModal from "@/hook/useModal"; import CreateNewTodo from "@/components/CreateNewTodo"; import { getNotes } from "@/api/noteAPI"; +import useTodoStore, { TodoType } from "@/store/todoStore"; import NoteViewer from "./NoteViewer"; @@ -20,23 +21,8 @@ export type GoalType = { teamId: string; }; -export type TodoType = { - noteId: number | null; - done: boolean; - linkUrl: string | null; - fileUrl: string | null; - title: string; - id: number; - goal: GoalType; - userId: number; - teamId: string; - updatedAt: string; - createdAt: string; -}; - type TodoProps = { todo: TodoType; - onTodoUpdate: (updatedTodo: TodoType) => void; isGoal?: boolean; isInGoalSection?: boolean; }; @@ -72,22 +58,28 @@ export interface NoteType { userId: number; } -export default function Todos({ todo, onTodoUpdate, isGoal = false, isInGoalSection = false }: TodoProps) { +export default function Todos({ todo, isGoal = false, isInGoalSection = false }: TodoProps) { const router = useRouter(); const [isOpen, setIsOpen] = useState(false); const dropdownRef = useRef(null); const { Modal, openModal, closeModal } = useModal(); const [isNoteOpen, setIsNoteOpen] = useState(false); const [noteContent, setNoteContent] = useState(); + const { updateTodo, deleteTodo } = useTodoStore(); - const toggleTodoStatus = async ({ title, goalId, fileUrl, linkUrl, done, todoId }: toggleTodoStatusType) => { - const updatedTodo = { ...todo, done: !done }; - onTodoUpdate(updatedTodo); + const toggleTodoStatus = async (updatedTodo: TodoType) => { try { - await patchTodo(title, goalId, !done, todoId, fileUrl, linkUrl); + await patchTodo( + updatedTodo.title, + updatedTodo.goal.id, + !updatedTodo.done, + updatedTodo.id, + updatedTodo.fileUrl, + updatedTodo.linkUrl, + ); + updateTodo(updatedTodo); } catch (error) { console.error("할 일 상태 변경 중 오류 발생:", error); - onTodoUpdate({ ...updatedTodo, done: done }); } }; @@ -107,12 +99,7 @@ export default function Todos({ todo, onTodoUpdate, isGoal = false, isInGoalSect // }; const handleDelete = async () => { - try { - const response = await deleteTodos(todo.id as number); - return response; - } catch (error) { - console.error("목표 삭제 중 오류 발생:", error); - } + await deleteTodo(todo.id); // Use deleteTodo from store }; useEffect(() => { @@ -144,16 +131,7 @@ export default function Todos({ todo, onTodoUpdate, isGoal = false, isInGoalSect width={todo.done === true ? 18 : 24} height={todo.done === true ? 18 : 24} alt="checkbox-icon" - onClick={() => - toggleTodoStatus({ - title: todo.title, - goalId: todo.goal.id, - fileUrl: todo.fileUrl as string, - linkUrl: todo.linkUrl as string, - done: todo.done, - todoId: todo.id, - }) - } + onClick={() => toggleTodoStatus({ ...todo, done: !todo.done })} /> {todo.title} @@ -250,13 +228,11 @@ export default function Todos({ todo, onTodoUpdate, isGoal = false, isInGoalSect diff --git a/src/app/dashboard/goal/[goalId]/page.tsx b/src/app/dashboard/goal/[goalId]/page.tsx index 4f847bd..0c930e3 100644 --- a/src/app/dashboard/goal/[goalId]/page.tsx +++ b/src/app/dashboard/goal/[goalId]/page.tsx @@ -4,6 +4,7 @@ import Image from "next/image"; import { useEffect, useRef, useState } from "react"; import { useRouter } from "next/navigation"; import { AxiosError } from "axios"; +import { useQuery } from "@tanstack/react-query"; import { useGoalStore } from "@/store/goalStore"; import { deleteGoal, ErrorType } from "@/api/goalAPI"; @@ -11,6 +12,8 @@ import { getTodos } from "@/api/todoAPI"; import useModal from "@/hook/useModal"; import CreateNewTodo from "@/components/CreateNewTodo"; import EditGoalTitleModal from "@/components/EditGoalTitleModal"; +import LoadingScreen from "@/components/LoadingScreen"; +import useTodoStore, { TodoType } from "@/store/todoStore"; import Todos from "../../components/Todos"; import ProgressBar from "../../components/ProgressBar"; @@ -19,20 +22,6 @@ type GoalPageProps = { params: { goalId: string }; }; -export type TodoType = { - noteId: number | null; - done: boolean; - linkUrl: string | null; - fileUrl: string | null; - title: string; - id: number; - goal: GoalType; - userId: number; - teamId: string; - updatedAt: string; - createdAt: string; -}; - export type GoalType = { id: number; teamId: string; @@ -48,28 +37,37 @@ export default function GoalPage({ params }: GoalPageProps) { const { Modal, openModal, closeModal } = useModal(); const [progress, setProgress] = useState(0); const { goals } = useGoalStore(); - const [todos, setTodos] = useState([]); + //const [todos, setTodos] = useState([]); + const { todos, setTodos } = useTodoStore(); const [goal, setGoal] = useState(null); const [isOpen, setIsOpen] = useState(false); const dropdownRef = useRef(null); + // 목표 찾기 useEffect(() => { setGoal(goals.find((goal) => goal.id === Number(goalId)) || null); }, [goals, goalId]); // 할 일 데이터 가져오기 - const fetchTodos = async () => { - if (goal) { - const todoResponse = await getTodos(goal.id); - const todosArray = Array.isArray(todoResponse?.todos) ? todoResponse.todos : []; - setTodos(todosArray); - updateProgress(todosArray); - } + const fetchTodos = async (goalId: number) => { + const todoResponse = await getTodos(goalId); + return Array.isArray(todoResponse?.todos) ? todoResponse.todos : []; }; + // React Query를 사용하여 할 일 데이터를 가져옴 + const { data: todosData, isLoading: isTodosLoading } = useQuery({ + queryKey: ["todos", goalId], + queryFn: () => fetchTodos(goal ? goal.id : 0), // goal이 있을 때만 호출 + enabled: !!goal, // goal이 있을 때만 쿼리 실행 + }); + + // 할 일 업데이트 및 진행률 계산 useEffect(() => { - fetchTodos(); - }, []); + if (todosData) { + setTodos(todosData); + updateProgress(todosData); + } + }, [todosData]); const updateProgress = (todosArray: TodoType[]) => { if (todosArray.length > 0) { @@ -81,6 +79,7 @@ export default function GoalPage({ params }: GoalPageProps) { } }; + // 목표 삭제 처리 const handleDelete = async () => { if (goal) { try { @@ -96,12 +95,14 @@ export default function GoalPage({ params }: GoalPageProps) { } }; - const handleTodoUpdate = (updatedTodo: TodoType) => { - setTodos((prevTodos) => - prevTodos.map((prevTodo) => (prevTodo.id === updatedTodo.id ? { ...prevTodo, ...updatedTodo } : prevTodo)), - ); - }; + // 할 일 업데이트 처리 + // const handleTodoUpdate = (updatedTodo: TodoType) => { + // setTodos((prevTodos) => + // prevTodos.map((prevTodo) => (prevTodo.id === updatedTodo.id ? { ...prevTodo, ...updatedTodo } : prevTodo)), + // ); + // }; + // 목표 제목 수정 모달 열기 const handleEditGoal = () => { if (goal) { openModal("EDIT_GOAL_TITLE"); @@ -113,6 +114,7 @@ export default function GoalPage({ params }: GoalPageProps) { updateProgress(todos); }, [todos]); + // 외부 클릭 시 드롭다운 닫기 useEffect(() => { const handleClickOutside = (event: MouseEvent) => { if (isOpen && dropdownRef.current && !dropdownRef.current.contains(event.target as Node)) { @@ -127,6 +129,10 @@ export default function GoalPage({ params }: GoalPageProps) { }; }, [isOpen]); + if (isTodosLoading) { + return ; + } + return (
@@ -184,9 +190,7 @@ export default function GoalPage({ params }: GoalPageProps) {
    {Array.isArray(todos) && - todos - .filter((todo) => !todo.done) - .map((todo) => )} + todos.filter((todo) => !todo.done).map((todo) => )}
{Array.isArray(todos) && todos.filter((todo) => !todo.done).length === 0 && (
해야할 일이 아직 없어요.
@@ -199,9 +203,7 @@ export default function GoalPage({ params }: GoalPageProps) {
    {Array.isArray(todos) && - todos - .filter((todo) => todo.done) - .map((todo) => )} + todos.filter((todo) => todo.done).map((todo) => )}
{Array.isArray(todos) && todos.filter((todo) => todo.done).length === 0 && (
다 한 일이 아직 없어요.
@@ -210,7 +212,7 @@ export default function GoalPage({ params }: GoalPageProps) { - + diff --git a/src/app/dashboard/note/[noteId]/page.tsx b/src/app/dashboard/note/[noteId]/page.tsx index 397e490..f0f22ff 100644 --- a/src/app/dashboard/note/[noteId]/page.tsx +++ b/src/app/dashboard/note/[noteId]/page.tsx @@ -3,11 +3,13 @@ import { toast } from "react-toastify"; import Image from "next/image"; import { ChangeEvent, useEffect, useState } from "react"; import { usePathname, useRouter, useSearchParams } from "next/navigation"; +import { useQuery } from "@tanstack/react-query"; import useModal from "@/hook/useModal"; import { getNotes, patchNotes, postNotes } from "@/api/noteAPI"; import { getTodos } from "@/api/todoAPI"; import UploadLinkModal from "@/components/UploadLinkModal"; +import LoadingScreen from "@/components/LoadingScreen"; export type GoalType = { id: number; @@ -67,35 +69,52 @@ export default function NotePage() { const todoId = Number(pathName.split("/").at(-1)); const goalId = Number(searchParams.get("goalId")); - const getNote = async () => { + // 노트와 할 일 가져오는 함수 + const fetchNote = async () => { const todoResponse = await getTodos(goalId); const findTodo = todoResponse.todos.find((todo: TodoType) => todo.id === todoId); setTodo(findTodo); - if (note?.id) { - const noteResponse = await getNotes(note.id); - setNote(noteResponse); - setTitle(noteResponse.title); - setContent(noteResponse.content); - setLink(noteResponse.linkUrl); + if (findTodo?.noteId) { + const noteResponse = await getNotes(findTodo.noteId); + return noteResponse; // 노트 데이터 반환 } + + return null; // 노트가 없을 경우 null 반환 }; - const handleSubmit = async (type: string) => { - if (type === "write") { - const response = await postNotes(todoId, title, content, link ? link : null); - if (response) { - setNote(response); - toast.success("작성완료"); - router.back(); - } - } else if (type === "edit") { - const response = await patchNotes(Number(note?.id), title, content, link ? link : null); - if (response) { - setNote(response); - toast.success("수정완료"); - router.back(); - } + // React Query를 사용하여 노트 데이터를 가져옴 + const { + data: fetchedNote, + isLoading, + isError, + } = useQuery({ + queryKey: ["note", todoId], // 고유 쿼리 키 설정 + queryFn: fetchNote, + enabled: !!todoId, // todoId가 존재할 때만 쿼리 실행 + }); + + // fetchedNote가 변경되면 상태 업데이트 + useEffect(() => { + if (fetchedNote) { + setNote(fetchedNote); + setTitle(fetchedNote.title); + setContent(fetchedNote.content); + setLink(fetchedNote.linkUrl); + } + }, [fetchedNote]); + + // 제출 처리 + const handleSubmit = async (type: "write" | "edit") => { + const response = + type === "write" + ? await postNotes(todoId, title, content, link || null) + : await patchNotes(Number(note?.id), title, content, link || null); + + if (response) { + setNote(response); + toast.success(type === "write" ? "작성이 완료되었습니다." : "수정이 완료되었습니다."); + router.back(); } }; @@ -138,36 +157,20 @@ export default function NotePage() { }; useEffect(() => { - getNote(); autoSaveDraft(); // eslint-disable-next-line react-hooks/exhaustive-deps }, []); - return ( -
- {/*
-
- close-icon {}} - /> -
+ if (isError) { + return

노트를 불러오는 데 오류가 발생했습니다.

; + } -