diff --git a/components/CardList.tsx b/components/CardList.tsx index d533353c1..6ab8b683d 100644 --- a/components/CardList.tsx +++ b/components/CardList.tsx @@ -7,6 +7,7 @@ import star_icon from "@/public/images/star.svg"; import kebab_icon from "@/public/images/kebab.svg"; interface CardListProps { + linkId: number; url: string; createdAt: string; desc: string; @@ -14,6 +15,7 @@ interface CardListProps { } export default function CardList({ + linkId, url, createdAt, desc, @@ -74,7 +76,9 @@ export default function CardList({ return (
- {isDeleteLinkModal && } + {isDeleteLinkModal && ( + + )} {isAddModal && } ([]); - - useEffect(() => { - async function getProfileFolder() { - try { - const { - folder: { links }, - } = await getFolder(); - setCardList(links); - } catch (error) { - console.error(error); - } - } - - getProfileFolder(); - }, []); +export default function CardSection({ id }: any) { + const cardList = useQuery({ + queryKey: ["individualList", Number(id)], + }); const dummyFunc = () => {}; @@ -38,17 +24,26 @@ export default function CardSection() {
- {cardList.map(({ id, createdAt, url, description, imageSource }) => { - return ( - - ); - })} + {Array.isArray(cardList.data) && + cardList.data.map( + ({ + id, + created_at, + url, + description, + image_source, + }: CardListType) => { + return ( + + ); + } + )}
diff --git a/components/FolderBar.tsx b/components/FolderBar.tsx index 4364d469d..988cc4e24 100644 --- a/components/FolderBar.tsx +++ b/components/FolderBar.tsx @@ -1,35 +1,38 @@ import React from "react"; -import { useEffect, useState } from "react"; -import { getFolder } from "@/pages/api/api"; +import { useQuery } from "@tanstack/react-query"; +import { getFolder, getFolderUser } from "@/pages/api/api"; import styles from "@/styles/FolderBar.module.css"; -export default function FolderBar() { - const [folderName, setFolderName] = useState(""); - const [userName, setUserName] = useState(""); - const [profileImage, setProfileImage] = useState(""); +export default function FolderBar({ id }: any) { + //폴더 이름을 가져오기 위한 쿼리 + const folderInfo = useQuery({ + queryKey: ["folderInfo"], + queryFn: () => getFolder(id), + staleTime: 0, + enabled: id !== undefined, + }); - useEffect(() => { - async function getProFileFolder() { - try { - const { - folder: { name, owner }, - } = await getFolder(); - setFolderName(name); - setUserName(owner.name); - setProfileImage(owner.profileImageSource); - } catch (error) { - console.error(error); - } - } + //폴더 소유자를 가져오기 위한 쿼리 + const folderOwner = useQuery({ + queryKey: ["folderOwner"], + queryFn: () => getFolderUser(folderInfo.data[0].user_id), + enabled: !!folderInfo.data, + }); - getProFileFolder(); - }, []); return (
- 폴더 이미지 - @{userName} - {folderName} + 폴더 이미지 + + @{folderOwner.data && folderOwner.data[0]?.name} + + + {folderInfo.data && folderInfo.data[0]?.name} +
); diff --git a/components/FolderSection.tsx b/components/FolderSection.tsx index c2ed1bc9a..c3feb3715 100644 --- a/components/FolderSection.tsx +++ b/components/FolderSection.tsx @@ -2,6 +2,7 @@ import React from "react"; import Image from "next/image"; import { useEffect, useState } from "react"; import { getFolderList, getAllLinks, getFolderLink } from "@/pages/api/api"; +import { useQuery, useQueryClient } from "@tanstack/react-query"; import CardList from "@/components/CardList"; import SearchBar from "@/components/SearchBar"; import addImg from "@/public/images/add.svg"; @@ -28,69 +29,99 @@ interface CardListType { } export default function FolderSection() { - const [folderName, setFolderName] = useState("폴더를 선택해주세요"); - const [folderList, setFolderList] = useState([]); + const [folderName, setFolderName] = useState( + "폴더를 선택해주세요" + ); + const [folderId, setFolderId] = useState(null); + //카드리스트에 관한 const [cardList, setCardList] = useState([]); + const [filteredCardList, setFilteredCardList] = useState([]); + //모달에 관한 const [isEditNameModal, setIsEditNameModal] = useState(false); const [isAddFolderModal, setIsAddFolderModal] = useState(false); const [isShareModal, setIsShareModal] = useState(false); const [isDeleteFolderModal, setIsDeleteFolderModal] = useState(false); + //선택한 id 버튼 활성화를 위해 const [selectedFolderId, setSelectedFolderId] = useState(); + //선택한 id 개별 폴더 링크를 가져오기 위해 + const [selectedId, setSelectedId] = useState(); const [searchInput, setSearchInput] = useState(""); - const [filteredCardList, setFilteredCardList] = useState([]); - //전체 폴더 클릭 - - async function getAllList() { - try { - const { data } = await getAllLinks(); - setCardList(data); - setFilteredCardList(data); - setFolderName("전체"); - console.log(folderList); - } catch (error) { - console.error(error); - } - } + const queryClient = useQueryClient(); - async function folderAllNameClick(all: string) { - setFolderName(all); - await getAllList(); - } - //전체 폴더 클릭 + //현재 폴더 이름을 최신으로 가져오기 위한 + const currentFolderName = useQuery({ + queryKey: ["folderName"], + }); - //개별 폴더 클릭 + //전체 폴더 가져오기 + const allList = useQuery({ + queryKey: ["allList"], + queryFn: async () => await getAllLinks(), + }); - async function getList(id: number) { - try { - const { data } = await getFolderLink(id); - setCardList(data); - } catch (error) { - console.error(error); - } + //전체 폴더 클릭 + async function folderAllNameClick() { + setFolderId(null); + queryClient.setQueryData(["folderId"], null); + queryClient.setQueryData(["folderName"], "전체"); } - async function folderNameClick(name: string, id: number) { - setFolderName(name); - await getList(id); - } + //개별 폴더 가져오기 + const individualList = useQuery({ + queryKey: ["individualList", selectedId], + queryFn: async () => { + if (selectedId) { + const data = await getFolderLink(selectedId); + return data; + } else { + return []; // 선택된 폴더가 없는 경우 빈 배열 반환 + } + }, + }); //개별 폴더 클릭 - - //폴더 버튼 + async function folderNameClick(name: string, id: number) { + setFolderId(id); + queryClient.setQueryData(["folderId"], id); + queryClient.setQueryData(["folderName"], name); + } + //폴더이름을 클릭했을 때 즉각적으로 링크 데이터들이 바뀌도록 useEffect(() => { - async function getList() { - try { - const { data } = await getFolderList(); - setFolderList(data); - } catch (error) { - console.error(error); - } + setFolderName(currentFolderName.data); + if (individualList.data && currentFolderName.data !== "전체") { + setCardList(individualList.data); + setFilteredCardList(individualList.data); + } else if (currentFolderName.data === "전체") { + setCardList(allList.data); + setFilteredCardList(allList.data); } + }, [individualList.data, currentFolderName.data, allList.data]); + + //폴더 버튼 + const folderList = useQuery({ + queryKey: ["folderList"], + queryFn: async () => await getFolderList(), + }); + + //링크 추가를 위해 현재 폴더 id를 쿼리에 저장 + //처음 랜더링 될때 한번 + useQuery({ + queryKey: ["folderId"], + queryFn: async () => { + return folderId; + }, + }); - getList(); - }, []); + //폴더 이름 변경을 위해 현재 폴더 이름을 쿼리에 저장 + //처음 랜더링 될때 한번 + useQuery({ + queryKey: ["folderName"], + queryFn: async () => { + return folderName; + }, + }); //이름변경 아이콘 클릭시 뜨는 모달창 함수 const clickEditName = () => { @@ -137,25 +168,40 @@ export default function FolderSection() {
{isEditNameModal && ( - + )} {isAddFolderModal && } - {isShareModal && } + {isShareModal && ( + + )} {isDeleteFolderModal && ( - + )}
- {folderList.map(({ name, id }) => { + {folderList.data?.map(({ name, id }: FolderListType) => { return (
- {cardList[0] ? ( + {cardList && cardList[0] ? ( <>
{folderName} @@ -207,6 +254,7 @@ export default function FolderSection() { return ( ({ + mutationFn: (linkData) => addLink(linkData.url, Number(linkData.folderId)), + onSuccess: () => { + queryClient.invalidateQueries({ + queryKey: ["individualList", Number(folderId.data)], + }); + queryClient.invalidateQueries({ + queryKey: ["allList"], + }); + }, + }); + + // input 값이 변경될 때마다 상태 업데이트 + const handleInputChange = (event: React.ChangeEvent) => { + setUrl(event.target.value); + }; + + // 추가하기 버튼 클릭 시 실행되는 함수 + const handleAddClick = (e: any) => { + e.preventDefault(); + //링크를 추가하는 API 호출 등의 작업 수행 + if (folderId.data !== null) { + const linkData: LinkData = { url, folderId: folderId.data }; + addLinkMutation.mutate(linkData, { + onSuccess: () => { + console.log("onSuccess in mutate"); + }, + }); + } + }; return (
@@ -14,9 +56,13 @@ export default function LinkAdd() { type="text" id={styles.inputText} placeholder="링크를 추가해보세요." + value={url} // input 값과 상태를 연결 + onChange={handleInputChange} // input 값 변경 시 호출되는 함수 />
- +
diff --git a/components/NavigationBar.tsx b/components/NavigationBar.tsx index 96317f371..b2b84392e 100644 --- a/components/NavigationBar.tsx +++ b/components/NavigationBar.tsx @@ -1,33 +1,14 @@ import React from "react"; import Image from "next/image"; import Link from "next/link"; -import { useEffect, useState } from "react"; +import { useQuery } from "@tanstack/react-query"; import { getUser } from "@/pages/api/api"; import styles from "@/styles/NavigationBar.module.css"; import Linkbrary from "@/public/images/logo.svg"; -interface ProfileObj { - profileImageSource: string; - email: string; -} - export default function NavigationBar() { - const [profile, setProfile] = useState(null); - - useEffect(() => { - async function getProFile() { - try { - const user = await getUser(); - if (user) { - setProfile(user); - } - } catch (error) { - console.error(error); - } - } - - getProFile(); - }, []); + const userInfo = useQuery({ queryKey: ["getUserInfo"], queryFn: getUser }); + const user = userInfo.data; return (
@@ -35,14 +16,14 @@ export default function NavigationBar() { Linkbrary - {profile ? ( + {user ? (
MyProfile - {profile.email} + {user[0].email}
) : ( diff --git a/components/modal/AddFolder.tsx b/components/modal/AddFolder.tsx index 2236eefbb..5f07460d0 100644 --- a/components/modal/AddFolder.tsx +++ b/components/modal/AddFolder.tsx @@ -1,5 +1,7 @@ -import React from "react"; +import React, { useState } from "react"; import Image from "next/image"; +import { useMutation, useQueryClient } from "@tanstack/react-query"; +import { addFolder } from "@/pages/api/api"; import styles from "@/styles/AddFolder.module.css"; import closeIcon from "@/public/images/close.svg"; @@ -7,7 +9,40 @@ interface AddFolderProps { onClose: any; } +interface FolderData { + name: string; +} + export default function AddFolder({ onClose }: AddFolderProps) { + const [name, setName] = useState(""); // input 값에 대한 상태 + const queryClient = useQueryClient(); + + // input 값이 변경될 때마다 상태 업데이트 + const handleInputChange = (event: React.ChangeEvent) => { + setName(event.target.value); + }; + + const addFolderMutation = useMutation({ + mutationFn: (folderData) => addFolder(folderData.name), + onSuccess: () => { + queryClient.invalidateQueries({ + queryKey: ["folderList"], + }); + onClose(); + }, + }); + + const clickChangeName = (name: string) => { + if (name) { + const folderData: FolderData = { name }; + addFolderMutation.mutate(folderData, { + onSuccess: () => { + console.log("onSuccess in mutate"); + }, + }); + } + }; + return (
@@ -18,8 +53,14 @@ export default function AddFolder({ onClose }: AddFolderProps) { alt="closeIcon" onClick={onClose} /> - - + +
); diff --git a/components/modal/DeleteFolder.tsx b/components/modal/DeleteFolder.tsx index 55517810b..ab4730afa 100644 --- a/components/modal/DeleteFolder.tsx +++ b/components/modal/DeleteFolder.tsx @@ -1,17 +1,49 @@ import React from "react"; import Image from "next/image"; +import { useMutation, useQueryClient } from "@tanstack/react-query"; +import { deleteFolder } from "@/pages/api/api"; import styles from "@/styles/DeleteFolder.module.css"; import closeIcon from "@/public/images/close.svg"; interface DeleteFolderProps { - folderName: string; + folderId: number | null; + folderName: string | undefined; onClose: any; } +interface FolderData { + folderId: number; +} + export default function DeleteFolder({ + folderId, folderName, onClose, }: DeleteFolderProps) { + const queryClient = useQueryClient(); + + const deleteFolderMutation = useMutation({ + mutationFn: (folderData) => deleteFolder(folderData.folderId), + onSuccess: () => { + queryClient.invalidateQueries({ + queryKey: ["folderList"], + }); + onClose(); + }, + }); + + const clickChangeName = (folderId: number | null) => { + if (folderId) { + const folderData: FolderData = { folderId }; + deleteFolderMutation.mutate(folderData, { + onSuccess: () => { + console.log("onSuccess in mutate"); + queryClient.setQueryData(["folderName"], "폴더를 선택해주세요"); + }, + }); + } + }; + return (
@@ -25,7 +57,9 @@ export default function DeleteFolder({ alt="closeIcon" onClick={onClose} /> - +
); diff --git a/components/modal/DeleteLink.tsx b/components/modal/DeleteLink.tsx index 69cafc75f..222c498a7 100644 --- a/components/modal/DeleteLink.tsx +++ b/components/modal/DeleteLink.tsx @@ -1,14 +1,51 @@ import React from "react"; import Image from "next/image"; +import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query"; +import { deleteLink } from "@/pages/api/api"; import styles from "@/styles/DeleteLink.module.css"; import closeIcon from "@/public/images/close.svg"; interface DeleteLinkProps { + linkId: number; link: string; onClose: any; } -export default function DeleteFolder({ link, onClose }: DeleteLinkProps) { +interface LinkData { + linkId: number; +} + +export default function DeleteFolder({ + linkId, + link, + onClose, +}: DeleteLinkProps) { + const queryClient = useQueryClient(); + const folderId = useQuery({ queryKey: ["folderId"] }); + + const deleteFolderMutation = useMutation({ + mutationFn: (linkData) => deleteLink(linkData.linkId), + onSuccess: () => { + queryClient.invalidateQueries({ + queryKey: ["individualList", Number(folderId.data)], + }); + queryClient.invalidateQueries({ + queryKey: ["allList"], + }); + }, + }); + + const clickDeleteLink = (linkId: number) => { + if (linkId) { + const linkData: LinkData = { linkId }; + deleteFolderMutation.mutate(linkData, { + onSuccess: () => { + console.log("onSuccess in mutate"); + }, + }); + } + }; + return (
@@ -22,7 +59,9 @@ export default function DeleteFolder({ link, onClose }: DeleteLinkProps) { alt="closeIcon" onClick={onClose} /> - +
); diff --git a/components/modal/Edit.tsx b/components/modal/Edit.tsx index 939a35de2..7e44d859f 100644 --- a/components/modal/Edit.tsx +++ b/components/modal/Edit.tsx @@ -1,14 +1,53 @@ -import React from "react"; +import React, { useState } from "react"; import Image from "next/image"; +import { useMutation, useQueryClient } from "@tanstack/react-query"; +import { changeName } from "@/pages/api/api"; import styles from "@/styles/Edit.module.css"; import closeIcon from "@/public/images/close.svg"; interface EditProps { - folderName: string; + folderId: number | null; + folderName: string | undefined; onClose: any; } -export default function Edit({ folderName, onClose }: EditProps) { +interface FolderData { + name: string; + folderId: number; +} + +export default function Edit({ folderId, folderName, onClose }: EditProps) { + const [name, setName] = useState(""); // input 값에 대한 상태 + const queryClient = useQueryClient(); + + // input 값이 변경될 때마다 상태 업데이트 + const handleInputChange = (event: React.ChangeEvent) => { + setName(event.target.value); + }; + + const changeFolderNameMutation = useMutation({ + mutationFn: (folderData) => + changeName(folderData.name, folderData.folderId), + onSuccess: () => { + queryClient.invalidateQueries({ + queryKey: ["folderList"], + }); + onClose(); + }, + }); + + const clickChangeName = (name: string, folderId: number | null) => { + if (folderId) { + const folderData: FolderData = { name, folderId }; + changeFolderNameMutation.mutate(folderData, { + onSuccess: () => { + console.log("onSuccess in mutate"); + queryClient.setQueryData(["folderName"], name); + }, + }); + } + }; + return (
@@ -19,8 +58,14 @@ export default function Edit({ folderName, onClose }: EditProps) { alt="closeIcon" onClick={onClose} /> - - + +
); diff --git a/components/modal/Share.tsx b/components/modal/Share.tsx index 94f294549..9ead684fb 100644 --- a/components/modal/Share.tsx +++ b/components/modal/Share.tsx @@ -1,4 +1,5 @@ import React from "react"; +import { useRouter } from "next/navigation"; import Image from "next/image"; import styles from "@/styles/Share.module.css"; import closeIcon from "@/public/images/close.svg"; @@ -7,11 +8,20 @@ import facebookIcon from "@/public/images/facebook_icon.svg"; import linkIcon from "@/public/images/link.svg"; interface ShareProps { - folderName: string; + folderId: number | null; + folderName: string | undefined; onClose: any; } -export default function Share({ folderName, onClose }: ShareProps) { +export default function Share({ folderId, folderName, onClose }: ShareProps) { + const router = useRouter(); + + const moveToSharePage = (id: number | null) => { + if (id !== null) { + router.push(`/shared/${id}`); + } + }; + return (
@@ -27,20 +37,31 @@ export default function Share({ folderName, onClose }: ShareProps) { />
- kakaoIcon -

카카오톡

+ moveToSharePage(folderId)} + alt="kakaoIcon" + /> +

공유페이지

moveToSharePage(folderId)} alt="facebookIcon" /> -

페이스북

+

공유페이지

- linkIcon -

링크 복사

+ moveToSharePage(folderId)} + alt="linkIcon" + /> +

공유페이지

diff --git a/package-lock.json b/package-lock.json index baa2b6655..ad286bea2 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8,6 +8,8 @@ "name": "fe-weekly-mission", "version": "0.1.0", "dependencies": { + "@tanstack/react-query": "^5.35.5", + "@tanstack/react-query-devtools": "^5.35.5", "next": "13.5.6", "react": "^18", "react-dom": "^18" @@ -329,6 +331,55 @@ "tslib": "^2.4.0" } }, + "node_modules/@tanstack/query-core": { + "version": "5.35.5", + "resolved": "https://registry.npmjs.org/@tanstack/query-core/-/query-core-5.35.5.tgz", + "integrity": "sha512-OMWvlEqG01RfGj+XZb/piDzPp0eZkkHWSDHt2LvE/fd1zWburP/xwm0ghk6Iv8cuPlP+ACFkZviKXK0OVt6lhg==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/tannerlinsley" + } + }, + "node_modules/@tanstack/query-devtools": { + "version": "5.32.1", + "resolved": "https://registry.npmjs.org/@tanstack/query-devtools/-/query-devtools-5.32.1.tgz", + "integrity": "sha512-7Xq57Ctopiy/4atpb0uNY5VRuCqRS/1fi/WBCKKX6jHMa6cCgDuV/AQuiwRXcKARbq2OkVAOrW2v4xK9nTbcCA==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/tannerlinsley" + } + }, + "node_modules/@tanstack/react-query": { + "version": "5.35.5", + "resolved": "https://registry.npmjs.org/@tanstack/react-query/-/react-query-5.35.5.tgz", + "integrity": "sha512-sppX7L+PVn5GBV3In6zzj0zcKfnZRKhXbX1MfIfKo1OjIq2GMaopvAFOP0x1bRYTUk2ikrdYcQYOozX7PWkb8A==", + "dependencies": { + "@tanstack/query-core": "5.35.5" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/tannerlinsley" + }, + "peerDependencies": { + "react": "^18.0.0" + } + }, + "node_modules/@tanstack/react-query-devtools": { + "version": "5.35.5", + "resolved": "https://registry.npmjs.org/@tanstack/react-query-devtools/-/react-query-devtools-5.35.5.tgz", + "integrity": "sha512-4Xll14B9uhgEJ+uqZZ5tqZ7G1LDR7wGYgb+NOZHGn11TTABnlV8GWon7zDMqdaHeR5mjjuY1UFo9pbz39kuZKQ==", + "dependencies": { + "@tanstack/query-devtools": "5.32.1" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/tannerlinsley" + }, + "peerDependencies": { + "@tanstack/react-query": "^5.35.5", + "react": "^18.0.0" + } + }, "node_modules/@types/json5": { "version": "0.0.29", "resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz", diff --git a/package.json b/package.json index 1ce24924f..4a2f94d1f 100644 --- a/package.json +++ b/package.json @@ -9,16 +9,18 @@ "lint": "next lint" }, "dependencies": { + "@tanstack/react-query": "^5.35.5", + "@tanstack/react-query-devtools": "^5.35.5", + "next": "13.5.6", "react": "^18", - "react-dom": "^18", - "next": "13.5.6" + "react-dom": "^18" }, "devDependencies": { - "typescript": "^5", "@types/node": "^20", "@types/react": "^18", "@types/react-dom": "^18", "eslint": "^8", - "eslint-config-next": "13.5.6" + "eslint-config-next": "13.5.6", + "typescript": "^5" } } diff --git a/pages/_app.tsx b/pages/_app.tsx index 8e301a9e8..994edfc89 100644 --- a/pages/_app.tsx +++ b/pages/_app.tsx @@ -1,6 +1,27 @@ import "@/styles/reset.css"; +import React from "react"; import type { AppProps } from "next/app"; +import { QueryClient, QueryClientProvider } from "@tanstack/react-query"; +import { ReactQueryDevtools } from "@tanstack/react-query-devtools"; export default function App({ Component, pageProps }: AppProps) { - return ; + const [queryClient] = React.useState( + () => + new QueryClient({ + defaultOptions: { + queries: { + // 보통 SSR에서는 staleTime을 0 이상으로 해줌으로써 + // 클라이언트 사이드에서 바로 다시 데이터를 refetch 하는 것을 피한다. + staleTime: 60 * 1000, + }, + }, + }) + ); + + return ( + + + + + ); } diff --git a/pages/api/api.tsx b/pages/api/api.tsx index 7679f9237..8f9f4c507 100644 --- a/pages/api/api.tsx +++ b/pages/api/api.tsx @@ -1,16 +1,42 @@ -const BASE_URL = "https://bootcamp-api.codeit.kr"; +const BASE_URL = "https://bootcamp-api.codeit.kr/api/linkbrary/v1"; +//네비게이션바에 필요한 회원정보를 받아오는 api함수 export async function getUser() { - const response = await fetch(`${BASE_URL}/api/sample/user`); - if (!response.ok) { + const token = localStorage.getItem("accessToken"); + + try { + const response = await fetch(`${BASE_URL}/users`, { + method: "GET", + headers: { + Accept: "*/*", + Authorization: `Bearer ${token}`, + }, + }); + + if (!response.ok) { + throw new Error("유저 정보를 불러올 수 없습니다."); + } + + const user = await response.json(); + return user; + } catch (error) { throw new Error("유저 정보를 불러올 수 없습니다."); } - const user = await response.json(); - return user; } -export async function getFolder() { - const response = await fetch(`${BASE_URL}/api/sample/folder`); +//폴더 소유자의 정보를 얻기위한 api함수 +export async function getFolderUser(userId: number) { + const response = await fetch(`${BASE_URL}/users/${userId}`); + if (!response.ok) { + throw new Error("폴더 정보를 불러올 수 없습니다."); + } + const folderUser = await response.json(); + return folderUser; +} + +//폴더의 정보를 얻기위한 api함수 +export async function getFolder(id: any) { + const response = await fetch(`${BASE_URL}/folders/${id}`); if (!response.ok) { throw new Error("폴더 정보를 불러올 수 없습니다."); } @@ -18,26 +44,56 @@ export async function getFolder() { return folder; } +//현재 폴더 목록들을 얻기위한 api함수 export async function getFolderList() { - const response = await fetch(`${BASE_URL}/api/users/1/folders`); - if (!response.ok) { + const token = localStorage.getItem("accessToken"); + try { + const response = await fetch(`${BASE_URL}/folders`, { + method: "GET", + headers: { + Accept: "*/*", + Authorization: `Bearer ${token}`, + }, + }); + + if (!response.ok) { + throw new Error("폴더 목록을 불러올 수 없습니다.."); + } + + const folderList = await response.json(); + return folderList; + } catch (error) { throw new Error("폴더 목록을 불러올 수 없습니다.."); } - const folderList = await response.json(); - return folderList; } +//전체 폴더의 링크 데이터를 얻기위한 api함수 export async function getAllLinks() { - const response = await fetch(`${BASE_URL}/api/users/1/links`); - if (!response.ok) { + const token = localStorage.getItem("accessToken"); + + try { + const response = await fetch(`${BASE_URL}/links`, { + method: "GET", + headers: { + Accept: "*/*", + Authorization: `Bearer ${token}`, + }, + }); + + if (!response.ok) { + throw new Error("전체 폴더 링크를 불러오는데 실패했습니다"); + } + + const allLinks = await response.json(); + return allLinks; + } catch (error) { throw new Error("전체 폴더 링크를 불러오는데 실패했습니다"); } - const allLinks = await response.json(); - return allLinks; } +//개별 폴더의 링크 데이터를 얻기위한 api함수 export async function getFolderLink(id: number) { - const response = await fetch(`${BASE_URL}/api/users/1/links?folderId=${id}`); + const response = await fetch(`${BASE_URL}/folders/${id}/links`); if (!response.ok) { throw new Error("해당 폴더 링크를 불러오는데 실패했습니다"); } @@ -45,8 +101,128 @@ export async function getFolderLink(id: number) { return folderLink; } +//링크 추가를 위한 api 함수 +export async function addLink(url: string, folderId: number) { + const token = localStorage.getItem("accessToken"); + + try { + const response = await fetch(`${BASE_URL}/links`, { + method: "POST", + headers: { + "Content-Type": "application/json", + Accept: "*/*", + Authorization: `Bearer ${token}`, + }, + body: JSON.stringify({ + url: url, + folderId: folderId, + }), + }); + + if (!response.ok) { + throw new Error("링크를 추가할 수 없습니다."); + } + } catch (error) { + throw new Error("링크를 추가할 수 없습니다."); + } +} + +//폴더의 이름 변경을 위한 api 함수 +export async function changeName(name: string, folderId: number) { + const token = localStorage.getItem("accessToken"); + + try { + const response = await fetch(`${BASE_URL}/folders/${folderId}`, { + method: "PUT", + headers: { + "Content-Type": "application/json", + Accept: "*/*", + Authorization: `Bearer ${token}`, + }, + body: JSON.stringify({ + name: name, + }), + }); + + if (!response.ok) { + throw new Error("폴더의 이름 변경을 할 수 없습니다."); + } + } catch (error) { + throw new Error("폴더의 이름 변경을 할 수 없습니다."); + } +} + +//폴더의 추가를 위한 api 함수 +export async function addFolder(name: string) { + const token = localStorage.getItem("accessToken"); + + try { + const response = await fetch(`${BASE_URL}/folders`, { + method: "POST", + headers: { + "Content-Type": "application/json", + Accept: "*/*", + Authorization: `Bearer ${token}`, + }, + body: JSON.stringify({ + name: name, + }), + }); + + if (!response.ok) { + throw new Error("폴더를 추가할 수 없습니다."); + } + } catch (error) { + throw new Error("폴더를 추가할 수 없습니다."); + } +} + +//폴더의 삭제를 위한 api 함수 +export async function deleteFolder(folderId: number) { + const token = localStorage.getItem("accessToken"); + + try { + const response = await fetch(`${BASE_URL}/folders/${folderId}`, { + method: "DELETE", + headers: { + "Content-Type": "application/json", + Accept: "*/*", + Authorization: `Bearer ${token}`, + }, + }); + + if (!response.ok) { + throw new Error("폴더를 삭제할 수 없습니다."); + } + } catch (error) { + throw new Error("폴더를 삭제할 수 없습니다."); + } +} + +//링크 데이터의 삭제를 위한 api 함수 +export async function deleteLink(linkId: number) { + const token = localStorage.getItem("accessToken"); + + try { + const response = await fetch(`${BASE_URL}/links/${linkId}`, { + method: "DELETE", + headers: { + Accept: "*/*", + Authorization: `Bearer ${token}`, + }, + }); + + if (!response.ok) { + throw new Error("링크 데이터를 삭제할 수 없습니다."); + } + } catch (error) { + throw new Error("링크 데이터를 삭제할 수 없습니다."); + } +} + +//로그인 요청을 위한 api함수 export async function postSignIn(id: string, password: string) { - const response = await fetch(`${BASE_URL}/api/sign-in`, { + const response = await fetch(`${BASE_URL}/auth/sign-in`, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ @@ -58,8 +234,9 @@ export async function postSignIn(id: string, password: string) { return response; } +//이메일 중복 체크 api 함수 export async function postCheckEmail(id: string) { - const response = await fetch(`${BASE_URL}/api/check-email`, { + const response = await fetch(`${BASE_URL}/users/check-email`, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ @@ -70,8 +247,9 @@ export async function postCheckEmail(id: string) { return response; } +//회원가입 요청을 위한 api 함수 export async function postSignUp(id: string, password: string) { - const response = await fetch(`${BASE_URL}/api/sign-up`, { + const response = await fetch(`${BASE_URL}/auth/sign-up`, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ diff --git a/pages/folder.tsx b/pages/folder.tsx index 202ea8b48..0a554cb82 100644 --- a/pages/folder.tsx +++ b/pages/folder.tsx @@ -1,4 +1,5 @@ import React from "react"; + import NavigationBar from "@/components/NavigationBar"; import Footer from "@/components/Footer"; import LinkAdd from "@/components/LinkAdd"; diff --git a/pages/index.tsx b/pages/index.tsx index 598a78ae0..a3833c75f 100644 --- a/pages/index.tsx +++ b/pages/index.tsx @@ -6,9 +6,6 @@ const inter = Inter({ subsets: ["latin"] }); export default function Home() { return ( <> - -

shared페이지로 이동

-

folder페이지로 이동

diff --git a/pages/shared.tsx b/pages/shared/[id].tsx similarity index 66% rename from pages/shared.tsx rename to pages/shared/[id].tsx index 083772069..f5f80db4a 100644 --- a/pages/shared.tsx +++ b/pages/shared/[id].tsx @@ -1,4 +1,5 @@ -import React from "react"; +import React, { useEffect, useState } from "react"; +import { useRouter } from "next/router"; import NavigationBar from "@/components/NavigationBar"; import FolderBar from "@/components/FolderBar"; import CardSection from "@/components/CardSection"; @@ -6,11 +7,13 @@ import Footer from "@/components/Footer"; import styles from "@/styles/SharedPage.module.css"; function SharedPage() { + const router = useRouter(); + const { id } = router.query; return (
- - + +
); diff --git a/pages/signin.tsx b/pages/signin.tsx index e7bcb134a..6eb998346 100644 --- a/pages/signin.tsx +++ b/pages/signin.tsx @@ -29,11 +29,11 @@ export default function SignIn() { const response = await postSignIn(emailValue, passwordValue); - const { data } = await response.json(); + const { accessToken } = await response.json(); if (response.status === 200) { router.push("/folder"); - localStorage.setItem("accessToken", data.accessToken); + localStorage.setItem("accessToken", accessToken); } else { setEmailError("이메일을 확인해 주세요."); setPasswordError("비밀번호를 확인해 주세요."); diff --git a/pages/signup.tsx b/pages/signup.tsx index 923c68793..9189edc6a 100644 --- a/pages/signup.tsx +++ b/pages/signup.tsx @@ -33,10 +33,10 @@ export default function SignUp() { } else { //회원가입 로직 const response = await postSignUp(emailValue, passwordValue); - const { data } = await response.json(); + const { accessToken } = await response.json(); if (response.status === 200) { router.push("/folder"); - localStorage.setItem("accessToken", data.accessToken); + localStorage.setItem("accessToken", accessToken); } else { } }