diff --git a/client/src/app/layout.tsx b/client/src/app/layout.tsx index f4d9abb..71e5e31 100644 --- a/client/src/app/layout.tsx +++ b/client/src/app/layout.tsx @@ -4,7 +4,7 @@ import { nameOfApp, oneLineMessage } from "@/const/brand"; import OverrideCSS from "@/const/overrideCSS"; import { Box, GlobalStyles } from "@mui/material"; import Pretendard from "~/assets/font/Pretendard"; -import NavigationBar from "~/components/NavigationBar"; +import NavigationBar from "~/components/Navigation/NavigationBar"; import "./globals.css"; import CustomQueryClientProvider from "@/components/queryClient/CustomQueryClientProvider"; import { ReactQueryDevtools } from "@tanstack/react-query-devtools"; diff --git a/client/src/app/page.tsx b/client/src/app/page.tsx index 6aed334..7005b37 100644 --- a/client/src/app/page.tsx +++ b/client/src/app/page.tsx @@ -1,11 +1,11 @@ "use server"; import PostCardList from "@/components/post/PostCardList"; import { getPostListQueryFn } from "@/queries/post/useGetPostListInfiniteQuery"; +import getTokenFromCookies from "@/utils/getTokenFromCookies"; import { Container, Paper } from "@mui/material"; -import { cookies } from "next/headers"; export default async function Home() { - const accessToken = cookies().get("accessToken")?.value; + const accessToken = await getTokenFromCookies() const initialData = await getPostListQueryFn({ page: 0, diff --git a/client/src/app/search/page.tsx b/client/src/app/search/page.tsx index 03f6393..f77fa1e 100644 --- a/client/src/app/search/page.tsx +++ b/client/src/app/search/page.tsx @@ -1,6 +1,7 @@ "use server"; import SearchArea from "@/components/search/SearchArea"; import { getPostListQueryFn } from "@/queries/post/useGetPostListInfiniteQuery"; +import getTokenFromCookies from "@/utils/getTokenFromCookies"; import { Container } from "@mui/material"; import { cookies } from "next/headers"; @@ -9,7 +10,7 @@ const SearchPage = async ({ }: { searchParams?: { [key: string]: string | undefined }; }) => { - const accessToken = cookies().get("accessToken")?.value; + const accessToken = await getTokenFromCookies() const initialData = await getPostListQueryFn({ searchKeyword: searchParams?.keyword, headers: { Authorization: accessToken }, diff --git a/client/src/components/Navigation/NavbarUserImage.tsx b/client/src/components/Navigation/NavbarUserImage.tsx new file mode 100644 index 0000000..2101e99 --- /dev/null +++ b/client/src/components/Navigation/NavbarUserImage.tsx @@ -0,0 +1,20 @@ +"use client"; +import { useUserInfoQuery } from "@/queries/auth/useUserInfoQuery"; +import UserAvatar from "../user/info/UserAvatar"; +import MyIcon from "~/assets/icons/MyIcon.svg"; +const NavbarUserImage = () => { + try { + const { data } = useUserInfoQuery(); + return ( + + ); + } catch (err) { + return ; + } +}; + +export default NavbarUserImage; diff --git a/client/src/components/Navigation/NavigationBar.tsx b/client/src/components/Navigation/NavigationBar.tsx new file mode 100644 index 0000000..4ca22fe --- /dev/null +++ b/client/src/components/Navigation/NavigationBar.tsx @@ -0,0 +1,82 @@ +"use client"; +import { BottomNavigation, BottomNavigationAction, Paper } from "@mui/material"; + +import HomeIcon from "~/assets/icons/HomeIcon.svg"; +import SearchIcon from "~/assets/icons/SearchIcon.svg"; +import PostIcon from "~/assets/icons/PostIcon.svg"; +import BeverageIcon from "~/assets/icons/BeverageIcon.svg"; + +import HOME, { MY_PROFILE, NEW_POST, SEARCH, WIKI } from "@/const/clientPath"; +import Link from "next/link"; +import { usePathname } from "next/navigation"; +import NavbarUserImage from "@/components/Navigation/NavbarUserImage"; +import { useMemo } from "react"; + +const NavigationBar = () => { + const path = usePathname(); + const NavbarData = useMemo( + () => [ + { + iconComponent: , + label: "홈", + href: HOME, + }, + { + iconComponent: , + label: "검색", + href: SEARCH, + }, + { + iconComponent: , + href: NEW_POST, + }, + { + iconComponent: , + label: "술과사전", + href: WIKI, + }, + { + iconComponent: , + label: "내 정보", + href: MY_PROFILE, + }, + ], + [] + ); + return ( + + + {NavbarData.map(({ label, href, iconComponent, ...others }) => { + return ( + + ); + })} + + + ); +}; + +const WrapperStyle = { + position: "fixed", + bottom: 0, + left: 0, + right: 0, + borderRadius: 0, +}; +const BtnStyle = { + borderRadius: "12px 12px 0 0", + border: "1px solid", + borderBottom: "none", + borderColor: "gray.secondary", + boxSizing: "border-box", +}; + +export default NavigationBar; diff --git a/client/src/components/NavigationBar.tsx b/client/src/components/NavigationBar.tsx deleted file mode 100644 index 4f0cf11..0000000 --- a/client/src/components/NavigationBar.tsx +++ /dev/null @@ -1,83 +0,0 @@ -"use client"; -import { BottomNavigation, BottomNavigationAction, Paper } from "@mui/material"; - -import HomeIcon from "~/assets/icons/HomeIcon.svg"; -import SearchIcon from "~/assets/icons/SearchIcon.svg"; -import PostIcon from "~/assets/icons/PostIcon.svg"; -import BeverageIcon from "~/assets/icons/BeverageIcon.svg"; -import MyIcon from "~/assets/icons/MyIcon.svg"; -import HOME, { MY_PROFILE, NEW_POST, SEARCH, WIKI } from "@/const/clientPath"; -import Link from "next/link"; -import { usePathname } from "next/navigation"; - -const NavbarData = [ - { - iconComponent: , - label: "홈", - href: HOME, - }, - { - iconComponent: , - label: "검색", - href: SEARCH, - }, - { - iconComponent: , - href: NEW_POST, - }, - { - iconComponent: , - label: "술과사전", - href: WIKI, - }, - { - iconComponent: , - label: "내 정보", - //FIXME 실제 URL로 변경 - href: MY_PROFILE+"/1", - }, -]; - -const NavigationBar = () => { - const path = usePathname(); - return ( - - - {NavbarData.map(({ label, href, iconComponent, ...others }) => { - return ( - - ); - })} - - - ); -}; - -export default NavigationBar; diff --git a/client/src/components/post/PostCard.tsx b/client/src/components/post/PostCard.tsx index b7b7e31..bc6f82f 100644 --- a/client/src/components/post/PostCard.tsx +++ b/client/src/components/post/PostCard.tsx @@ -25,13 +25,17 @@ import useUnLikePostMutation from "@/queries/post/useUnLikePostMutation"; import "../newpost/quill.mention.css"; import { sanitize } from "isomorphic-dompurify"; import UserAvatar from "../user/info/UserAvatar"; +import Link from "next/link"; +import { USER_PAGE } from "@/const/clientPath"; +import { useUserInfoQuery } from "@/queries/auth/useUserInfoQuery"; const PostCard = ({ postAttachUrls, id, + createdBy, nickname, postContent, - updateDt, + lastModifiedDate, tagList, postNo, likeCount, @@ -46,13 +50,21 @@ const PostCard = ({ const hasImage = useMemo(() => postAttachUrls.length !== 0, [postAttachUrls]); const { mutate: likeHandler } = useLikePostMutation(); const { mutate: unLikeHandler } = useUnLikePostMutation(); + const { data: currentUser } = useUserInfoQuery(); + + const isMyPost = useMemo( + () => currentUser?.userNo === createdBy, + [currentUser] + ); return ( - + + + {/* Header */} {/* 타이틀 */} - {nickname} - {`@${id}`} + + {nickname} + + + {`@${id}`} + - {dayjs(updateDt).format("MM.DD")} + {dayjs(lastModifiedDate).format("MM.DD")} - - + + {isMyPost && } + {alcoholName && ( )} @@ -110,7 +127,11 @@ const PostCard = ({ onClick={() => openPostDetailPage(id, String(postNo))} image={postAttachUrls[0].attachUrl} alt={`${id}의 포스트`} - sx={{ borderRadius: 2, bgcolor: "background.default" }} + sx={{ + borderRadius: 2, + bgcolor: "background.default", + cursor: "pointer", + }} /> )} {/* CTA */} diff --git a/client/src/components/queryClient/CustomQueryClientProvider.tsx b/client/src/components/queryClient/CustomQueryClientProvider.tsx index 4843009..0c6faf7 100644 --- a/client/src/components/queryClient/CustomQueryClientProvider.tsx +++ b/client/src/components/queryClient/CustomQueryClientProvider.tsx @@ -13,6 +13,7 @@ export default function CustomQueryClientProvider({ new QueryClient({ defaultOptions: { queries: { + retry:false, staleTime: 60 * 1000, }, }, diff --git a/client/src/const/clientPath.ts b/client/src/const/clientPath.ts index 480b2bd..255107d 100644 --- a/client/src/const/clientPath.ts +++ b/client/src/const/clientPath.ts @@ -15,6 +15,11 @@ export const FORGOTPASSWORD = "/auth/forgot-password" as const; * 로그인 했을 경우만 접근 가능한 마이페이지 */ export const MY_PROFILE = "/user" as const; +/** + * 유저의 PK를 입력받아 해당유저의 프로필 페이지로 이동하는 URL + */ +export const USER_PAGE = (pk:string|number)=>`/user/${pk}` + /** * 술과사전 페이지 라우트 */ diff --git a/client/src/queries/auth/useUserInfoQuery.tsx b/client/src/queries/auth/useUserInfoQuery.tsx index 2122f49..9f74b1e 100644 --- a/client/src/queries/auth/useUserInfoQuery.tsx +++ b/client/src/queries/auth/useUserInfoQuery.tsx @@ -1,20 +1,21 @@ "use client"; import { MY_INFO } from "@/const/serverPath"; -import {axiosPrivate} from "@/libs/axios"; +import { axiosPrivate } from "@/libs/axios"; import { MyInfoInterface } from "@/types/auth/myInfo"; import { SigninRequirement } from "@/types/auth/signinRequirement"; -import { useSuspenseQuery } from "@tanstack/react-query"; +import getTokenFromLocalStorage from "@/utils/getTokenFromLocalStorage"; +import { useQuery } from "@tanstack/react-query"; export const useUserInfoQuery = () => - useSuspenseQuery({ + useQuery({ queryKey: userInfoQueryKeys.all, queryFn: getMyInfoByLocalStorage, }); export const getMyInfoByLocalStorage = async () => { - // const accessToken = localStorage.get("accessToken"); + const accessToken = getTokenFromLocalStorage(); const { data } = await axiosPrivate.get(MY_INFO, { - // headers: { Authorization: accessToken }, + headers: { Authorization: accessToken }, }); return data; }; diff --git a/client/src/queries/post/useDeletePostMutation.ts b/client/src/queries/post/useDeletePostMutation.ts index 41bc682..b209aef 100644 --- a/client/src/queries/post/useDeletePostMutation.ts +++ b/client/src/queries/post/useDeletePostMutation.ts @@ -1,6 +1,6 @@ import { REMOVE_POST } from "@/const/serverPath"; import { axiosPrivate } from "@/libs/axios"; -import { useMutation, useQueryClient } from "@tanstack/react-query"; +import { useMutation } from "@tanstack/react-query"; import { useInvalidatePostList } from "./useGetPostListInfiniteQuery"; export const useDeletePostMutation = () => { diff --git a/client/src/queries/post/useGetPostDetailQuery.tsx b/client/src/queries/post/useGetPostDetailQuery.tsx index 9054e2f..2133e18 100644 --- a/client/src/queries/post/useGetPostDetailQuery.tsx +++ b/client/src/queries/post/useGetPostDetailQuery.tsx @@ -1,4 +1,4 @@ -import { useSuspenseQuery } from "@tanstack/react-query"; +import { useQuery } from "@tanstack/react-query"; import axios from "@/libs/axios"; import { PostInterface } from "@/types/post/PostInterface"; @@ -6,7 +6,7 @@ const useGetPostDetailQuery = ( postId: string, options?: { initialData: PostInterface } ) => { - return useSuspenseQuery({ + return useQuery({ queryKey: ["post", postId], queryFn: () => getPostDetailQueryFn(postId), initialData: options?.initialData, diff --git a/client/src/queries/post/useGetPostListInfiniteQuery.tsx b/client/src/queries/post/useGetPostListInfiniteQuery.tsx index 38f554e..5667dbd 100644 --- a/client/src/queries/post/useGetPostListInfiniteQuery.tsx +++ b/client/src/queries/post/useGetPostListInfiniteQuery.tsx @@ -49,6 +49,7 @@ export interface GetPostListOptions { page?: number; size?: number; searchKeyword?: string; + searchUserNos?:string; } /** * 실제 서버에서 응답해주는 값 diff --git a/client/src/stories/Components/Navigation/Navbar.stories.tsx b/client/src/stories/Components/Navigation/Navbar.stories.tsx index 562477d..6bbfd2e 100644 --- a/client/src/stories/Components/Navigation/Navbar.stories.tsx +++ b/client/src/stories/Components/Navigation/Navbar.stories.tsx @@ -1,4 +1,4 @@ -import NavigationBar from "@/components/NavigationBar"; +import NavigationBar from "@/components/Navigation/NavigationBar"; import { Meta, StoryObj } from "@storybook/react"; const meta = { diff --git a/client/src/types/auth/myInfo.ts b/client/src/types/auth/myInfo.ts index 59c1962..46f356a 100644 --- a/client/src/types/auth/myInfo.ts +++ b/client/src/types/auth/myInfo.ts @@ -1,5 +1,6 @@ export interface MyInfoInterface { id: string; + userNo:string; nickname: string; profileImages: ProfileImagesType[]; introduction: string; diff --git a/client/src/types/post/PostInterface.ts b/client/src/types/post/PostInterface.ts index 6f492d4..52d33a0 100644 --- a/client/src/types/post/PostInterface.ts +++ b/client/src/types/post/PostInterface.ts @@ -9,8 +9,12 @@ export interface PostInterface { /** * 업데이트 된 날짜 */ - updateDt?: string; + lastModifiedDate?: string; /** + * 해당 게시글을 생성한유저PK + */ + createdBy:string; + /** * 수정 됬는지 여부 */ edited?: boolean; diff --git a/client/src/utils/getTokenFromCookies.ts b/client/src/utils/getTokenFromCookies.ts new file mode 100644 index 0000000..9b0c662 --- /dev/null +++ b/client/src/utils/getTokenFromCookies.ts @@ -0,0 +1,14 @@ +import { cookies } from "next/headers"; + +/** + * 쿠키에서 AccessToken을 불러오는 유틸함수 + * 서버단에서만 동작 + */ +export default async function getTokenFromCookies() { + "use server"; + if (typeof window !== "undefined") { + return; + } + const accessToken = cookies().get("accessToken")?.value; + return accessToken; +}