diff --git a/client/src/app/@Modal/(.)post/[userId]/[postId]/page.tsx b/client/src/app/@Modal/(.)post/[userId]/[postId]/page.tsx
deleted file mode 100644
index 8b092e2..0000000
--- a/client/src/app/@Modal/(.)post/[userId]/[postId]/page.tsx
+++ /dev/null
@@ -1,12 +0,0 @@
-import PostDetailPage from "@/app/post/[userId]/[postId]/page";
-import ModalWrapper from "@/components/ModalWrapper";
-
-const page = async ({ params }: { params: { postId: string } }) => {
- return (
-
-
-
- );
-};
-
-export default page;
diff --git a/client/src/app/@Modal/layout.tsx b/client/src/app/@Modal/layout.tsx
index 5aa1ad9..18e98b8 100644
--- a/client/src/app/@Modal/layout.tsx
+++ b/client/src/app/@Modal/layout.tsx
@@ -5,7 +5,7 @@ import { usePathname } from "next/navigation";
export default function Layout({ children }: any) {
const pathname = usePathname();
- const allowedPath = ["/post/", NEW_POST, SIGNIN];
+ const allowedPath = [NEW_POST, SIGNIN];
return allowedPath.some((path) => pathname.startsWith(path))
? children
diff --git a/client/src/app/post/[userId]/[postId]/page.tsx b/client/src/app/post/[userId]/[postId]/page.tsx
index 4d84e5a..6002903 100644
--- a/client/src/app/post/[userId]/[postId]/page.tsx
+++ b/client/src/app/post/[userId]/[postId]/page.tsx
@@ -1,8 +1,10 @@
"use server";
-import PostDetail from "@/components/post/PostDetail";
+import PostDetail from "@/components/post/detail/PostDetail";
import { getPostDetailQueryFn } from "@/queries/post/useGetPostDetailQuery";
import getTokenFromCookies from "@/utils/getTokenFromCookies";
+import { Paper, Container } from "@mui/material";
import { redirect } from "next/navigation";
+import CustomAppbar from "@/components/CustomAppbar";
const PostDetailPage = async ({ params }: { params: { postId: string } }) => {
const parsedPostId = params.postId;
@@ -15,7 +17,22 @@ const PostDetailPage = async ({ params }: { params: { postId: string } }) => {
} catch {
redirect("/not-found");
}
- return ;
+ return (
+ <>
+
+
+
+
+
+
+ >
+ );
};
export default PostDetailPage;
diff --git a/client/src/assets/icons/CommentIcon.svg b/client/src/assets/icons/CommentIcon.svg
index e877410..1ee8ead 100644
--- a/client/src/assets/icons/CommentIcon.svg
+++ b/client/src/assets/icons/CommentIcon.svg
@@ -5,6 +5,6 @@
+ fill="#D9D9D9" />
\ No newline at end of file
diff --git a/client/src/assets/icons/comment/SubmitCommentIcon.svg b/client/src/assets/icons/comment/SubmitCommentIcon.svg
new file mode 100644
index 0000000..2f5173c
--- /dev/null
+++ b/client/src/assets/icons/comment/SubmitCommentIcon.svg
@@ -0,0 +1,9 @@
+
diff --git a/client/src/components/ModalWrapper.tsx b/client/src/components/ModalWrapper.tsx
index d10328e..0fcb7ba 100644
--- a/client/src/components/ModalWrapper.tsx
+++ b/client/src/components/ModalWrapper.tsx
@@ -29,6 +29,7 @@ const ModalWrapper = ({ children, disableBox }: ModalInterface) => {
alignItems: "center",
justifyContent: "center",
display: "flex",
+ position:'absolute'
}}
>
{
@@ -39,6 +40,7 @@ const ModalWrapper = ({ children, disableBox }: ModalInterface) => {
p: disableBox ? 0 : 4,
maxWidth: "90%",
maxHeight: "90%",
+ overflowY:'auto'
}}
>
<>{children}>
diff --git a/client/src/components/Navigation/NavigationBar.tsx b/client/src/components/Navigation/NavigationBar.tsx
index fef2927..122b669 100644
--- a/client/src/components/Navigation/NavigationBar.tsx
+++ b/client/src/components/Navigation/NavigationBar.tsx
@@ -5,8 +5,15 @@ 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 { useGlobalNavbarVisibility } from "@/store/useGlobalNavbarVisibility";
-import HOME, { MY_PROFILE, NEW_POST, SEARCH, SIGNIN, WIKI } from "@/const/clientPath";
+import HOME, {
+ MY_PROFILE,
+ NEW_POST,
+ SEARCH,
+ SIGNIN,
+ WIKI,
+} from "@/const/clientPath";
import Link from "next/link";
import { usePathname } from "next/navigation";
import NavbarUserImage from "@/components/Navigation/NavbarUserImage";
@@ -16,6 +23,9 @@ import { useMyInfoQuery } from "@/queries/auth/useMyInfoQuery";
const NavigationBar = () => {
const path = usePathname();
const { data: userInfo } = useMyInfoQuery();
+
+ const isVisible = useGlobalNavbarVisibility(({ isVisible }) => isVisible);
+
const NavbarData = useMemo(
() => [
{
@@ -45,35 +55,32 @@ const NavigationBar = () => {
],
[userInfo]
);
- return (
-
-
- {NavbarData.map(({ label, href, iconComponent, ...others }) => {
- return (
-
- );
- })}
-
-
+ return isVisible ? (
+
+ {NavbarData.map(({ label, href, iconComponent, ...others }) => {
+ return (
+
+ );
+ })}
+
+ ) : (
+ <>>
);
};
-const WrapperStyle = {
+const BtnStyle = {
position: "fixed",
bottom: 0,
left: 0,
right: 0,
- borderRadius: 0,
-};
-const BtnStyle = {
borderRadius: "12px 12px 0 0",
border: "1px solid",
borderBottom: "none",
diff --git a/client/src/components/post/PostCard.tsx b/client/src/components/post/PostCard.tsx
index 62b9a15..6ef4f5b 100644
--- a/client/src/components/post/PostCard.tsx
+++ b/client/src/components/post/PostCard.tsx
@@ -18,7 +18,6 @@ import { useContext, useMemo } from "react";
import ShareIcon from "@/assets/icons/ShareIcon.svg";
import LikeIcon from "@/assets/icons/LikeIcon.svg";
import CommentIcon from "@/assets/icons/CommentIcon.svg";
-import QuoteIcon from "@/assets/icons/QuoteIcon.svg";
import AlcoholNameTag from "@/components/wiki/AlcoholNameTag";
import dayjs from "dayjs";
import useLikePostMutation from "@/queries/post/useLikePostMutation";
@@ -30,7 +29,7 @@ import Link from "next/link";
import { USER_PAGE } from "@/const/clientPath";
import { useMyInfoQuery } from "@/queries/auth/useMyInfoQuery";
import PostCardOptionDropdown from "./PostCardOptionDropdown";
-import { postcardContext } from "@/store/PostCardContext";
+import { postcardContext } from "@/store/post/PostCardContext";
const PostCard = ({
postAttachUrls,
@@ -47,7 +46,6 @@ const PostCard = ({
alcoholType,
commentCount,
likedByMe,
- quoteCount,
alcoholNo,
}: PostInterface) => {
const openPostDetailPage = useOpenPostDetailPage();
@@ -163,10 +161,6 @@ const PostCard = ({
{likeCount ?? 0}
-
-
- {quoteCount ?? 0}
-
공유
diff --git a/client/src/components/post/PostCardList.tsx b/client/src/components/post/PostCardList.tsx
index 0fd8999..d5f4541 100644
--- a/client/src/components/post/PostCardList.tsx
+++ b/client/src/components/post/PostCardList.tsx
@@ -6,12 +6,13 @@ import useGetPostListInfiniteQuery, {
} from "@/queries/post/useGetPostListInfiniteQuery";
import { useInView } from "react-intersection-observer";
import { useEffect } from "react";
-import { Box, CircularProgress, Stack } from "@mui/material";
+import { Stack } from "@mui/material";
import { useMemo } from "react";
import Image from "next/image";
import NoResult from "@/assets/images/noResult.png";
import getTokenFromLocalStorage from "@/utils/getTokenFromLocalStorage";
-import { postcardContext } from "@/store/PostCardContext";
+import { postcardContext } from "@/store/post/PostCardContext";
+import PostCardSkeleton from "./PostCardSkeleton";
function PostCardList(props: UseGetPostListQueryInterface) {
const {
@@ -28,7 +29,7 @@ function PostCardList(props: UseGetPostListQueryInterface) {
const { searchKeyword, searchUserNos } = props;
- const { ref, inView } = useInView({ threshold: 0.9 });
+ const { ref, inView } = useInView();
useEffect(() => {
if (hasNextPage && inView) fetchNextPage();
}, [inView, hasNextPage]);
@@ -55,9 +56,7 @@ function PostCardList(props: UseGetPostListQueryInterface) {
)}
{/* 로딩창 */}
{isFetchingNextPage || isLoading ? (
-
-
-
+
) : (
// 인터섹션옵저버
diff --git a/client/src/components/post/PostCardOptionDropdown.tsx b/client/src/components/post/PostCardOptionDropdown.tsx
index 756b5cb..53b39ce 100644
--- a/client/src/components/post/PostCardOptionDropdown.tsx
+++ b/client/src/components/post/PostCardOptionDropdown.tsx
@@ -4,17 +4,17 @@ import { ButtonBase, Menu, MenuItem } from "@mui/material";
import { useDeletePostMutation } from "@/queries/post/useDeletePostMutation";
type PostCardOptionDropdownProps = {
- postId:number
+ postId: number;
};
-const PostCardOptionDropdown = ({postId}: PostCardOptionDropdownProps) => {
+const PostCardOptionDropdown = ({ postId }: PostCardOptionDropdownProps) => {
const [anchorEl, setAnchorEl] = useState(null);
const open = Boolean(anchorEl);
const handleClick = (event: React.MouseEvent) => {
setAnchorEl(event.currentTarget);
};
-const {mutate:deletePost}=useDeletePostMutation()
+ const { mutate: deletePost } = useDeletePostMutation();
const handleClose = () => {
setAnchorEl(null);
@@ -25,11 +25,15 @@ const {mutate:deletePost}=useDeletePostMutation()
>
diff --git a/client/src/components/post/PostCardSkeleton.tsx b/client/src/components/post/PostCardSkeleton.tsx
new file mode 100644
index 0000000..f6f031a
--- /dev/null
+++ b/client/src/components/post/PostCardSkeleton.tsx
@@ -0,0 +1,35 @@
+import { Box, Card, Skeleton, Stack } from "@mui/material";
+
+const PostCardSkeleton = () => {
+ return (
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ );
+};
+
+export default PostCardSkeleton;
diff --git a/client/src/components/post/PostComment.tsx b/client/src/components/post/PostComment.tsx
deleted file mode 100644
index 52b4d3e..0000000
--- a/client/src/components/post/PostComment.tsx
+++ /dev/null
@@ -1,45 +0,0 @@
-import { USER_PAGE } from "@/const/clientPath";
-import { Stack, Avatar, Typography } from "@mui/material";
-import dayjs from "dayjs";
-import Link from "next/link";
-
-type Props = {
- content: string;
- nickname: string;
- userId: string;
- userPk: string;
- createdAt: string;
-};
-
-const PostComment = ({
- content,
- nickname,
- userId,
- createdAt,
- userPk,
-}: Props) => {
- return (
-
-
-
-
-
- {nickname}
-
-
- {`@${userId}`}
-
-
- {dayjs(createdAt).format("MM.DD")}
-
-
- {content}
-
-
- );
-};
-
-export default PostComment;
diff --git a/client/src/components/post/PostCommentList.tsx b/client/src/components/post/PostCommentList.tsx
deleted file mode 100644
index 75f73dd..0000000
--- a/client/src/components/post/PostCommentList.tsx
+++ /dev/null
@@ -1,40 +0,0 @@
-import PostComment from "./PostComment";
-import { Card, Stack } from "@mui/material";
-
-type Props = {
- postNo: string;
-};
-
-const PostCommentList = ({ postNo }: Props) => {
- const comments = Array.from(new Array(4)).map((_e, i) => ({
- commentNo: i,
- comment:
- "Lorem ipsum dolor sit amet consectetur adipisicing elit. Repellendus laudantium enim veritatis minus excepturi. Quidem iusto, velit facilis corporis at suscipit minima sed, cumque nostrum fugiat ad natus? Voluptatum, odio?",
- createdAt: new Date(),
- nickname: "유저이름",
- userId: "유저유저아이디",
- }));
- return (
- comments &&
- comments.length > 0 && (
-
-
- {comments.map(
- ({ commentNo, comment, createdAt, nickname, userId }, i) => (
-
- )
- )}
-
-
- )
- );
-};
-
-export default PostCommentList;
diff --git a/client/src/components/post/PostDetail.tsx b/client/src/components/post/PostDetail.tsx
deleted file mode 100644
index 637f760..0000000
--- a/client/src/components/post/PostDetail.tsx
+++ /dev/null
@@ -1,32 +0,0 @@
-"use client";
-
-import useGetPostDetailQuery from "@/queries/post/useGetPostDetailQuery";
-import PostCard from "./PostCard";
-import { PostInterface } from "@/types/post/PostInterface";
-import { CircularProgress } from "@mui/material";
-import PostCommentList from "@/components/post/PostCommentList";
-import getTokenFromLocalStorage from "@/utils/getTokenFromLocalStorage";
-interface PostDetailInterface {
- postNo: string;
- initialData: PostInterface;
-}
-const PostDetail = ({ postNo, initialData }: PostDetailInterface) => {
- const { data } = useGetPostDetailQuery(postNo, {
- initialData,
- headers: { Authorization: getTokenFromLocalStorage() },
- });
- //FIXME 포스트의 좋아요갯수가 업데이트 되지않음
- return (
- <>
- {data ? (
- <>
-
-
- >
- ) : (
-
- )}
- >
- );
-};
-export default PostDetail;
diff --git a/client/src/components/post/detail/PostComment.tsx b/client/src/components/post/detail/PostComment.tsx
new file mode 100644
index 0000000..3e21efc
--- /dev/null
+++ b/client/src/components/post/detail/PostComment.tsx
@@ -0,0 +1,61 @@
+import UserAvatar from "@/components/user/info/UserAvatar";
+import { USER_PAGE } from "@/const/clientPath";
+import { useMyInfoQuery } from "@/queries/auth/useMyInfoQuery";
+import { Stack, Avatar, Typography } from "@mui/material";
+import dayjs from "dayjs";
+import Link from "next/link";
+import PostCommentDropdown from "./PostCommentDropdown";
+
+type Props = {
+ content: string;
+ nickname: string;
+ userId: string;
+ userPk: string;
+ profileImg?: string;
+ createdAt: string;
+};
+
+const PostComment = ({
+ content,
+ nickname,
+ userId,
+ createdAt,
+ profileImg,
+ userPk,
+}: Props) => {
+ const { data: myData } = useMyInfoQuery();
+
+ const isMyComment = userPk === String(myData?.userNo);
+
+ return (
+
+
+
+
+
+
+ {nickname}
+
+
+ {`@${userId}`}
+
+
+ {dayjs(createdAt).format("MM.DD")}
+
+
+ {isMyComment && }
+
+ {content}
+
+
+ );
+};
+
+export default PostComment;
diff --git a/client/src/components/post/detail/PostCommentDropdown.tsx b/client/src/components/post/detail/PostCommentDropdown.tsx
new file mode 100644
index 0000000..734c0c4
--- /dev/null
+++ b/client/src/components/post/detail/PostCommentDropdown.tsx
@@ -0,0 +1,38 @@
+import { ButtonBase, Menu, MenuItem } from "@mui/material";
+import { MoreVertOutlined } from "@mui/icons-material";
+import { useState } from "react";
+
+const PostCommentDropdown = () => {
+ const [anchorEl, setAnchorEl] = useState(null);
+ const open = Boolean(anchorEl);
+
+ const handleClick = (event: React.MouseEvent) => {
+ setAnchorEl(event.currentTarget);
+ };
+
+ const handleClose = () => {
+ setAnchorEl(null);
+ };
+
+ return (
+ <>
+
+
+
+
+ >
+ );
+};
+
+export default PostCommentDropdown;
diff --git a/client/src/components/post/detail/PostCommentInput.tsx b/client/src/components/post/detail/PostCommentInput.tsx
new file mode 100644
index 0000000..328eafe
--- /dev/null
+++ b/client/src/components/post/detail/PostCommentInput.tsx
@@ -0,0 +1,83 @@
+"use client";
+import { InputAdornment, Paper, TextField } from "@mui/material";
+import { useCallback, useContext, useState } from "react";
+import SubmitCommentIcon from "@/assets/icons/comment/SubmitCommentIcon.svg";
+import { useGlobalNavbarVisibility } from "@/store/useGlobalNavbarVisibility";
+import PostDetailPageContext from "@/store/post/PostDetailPageContext";
+import useNewPostCommentMutation from "@/queries/post/comment/useNewPostCommentMutation";
+
+const PostCommentInput = () => {
+ const setIsShowingNavbar = useGlobalNavbarVisibility(
+ ({ setIsVisible }) => setIsVisible
+ );
+ const { data: currentData } = useContext(PostDetailPageContext);
+ const [isEditing, setIsEditing] = useState(false);
+ const [inputValue, setInputValue] = useState("");
+
+ const { mutateAsync: submitForm } = useNewPostCommentMutation(
+ currentData?.postNo ? String(currentData?.postNo) : undefined
+ );
+ const submitHandler = useCallback(
+ (content: string) => {
+ submitForm(content).then(() => {
+ setInputValue("");
+ });
+ },
+ [submitForm, setInputValue]
+ );
+
+ return (
+
+ {
+ setIsShowingNavbar(false);
+ setIsEditing(true);
+ }}
+ onBlur={() => {
+ setIsShowingNavbar(true);
+ setIsEditing(false);
+ }}
+ size="small"
+ autoComplete="off"
+ placeholder="회원님의 생각을 올려보세요"
+ multiline
+ value={inputValue}
+ onChange={(e) => setInputValue(e.target.value)}
+ rows={isEditing ? 5 : 1}
+ InputProps={{
+ endAdornment: (
+ {
+ e.stopPropagation();
+ submitHandler(inputValue)
+ }}
+ sx={{
+ color: inputValue.length > 0 ? "primary.main" : "text.disabled",
+ }}
+ >
+
+
+ ),
+ }}
+ sx={{ backgroundColor: "background.paper" }}
+ />
+
+ );
+};
+
+export default PostCommentInput;
diff --git a/client/src/components/post/detail/PostCommentList.tsx b/client/src/components/post/detail/PostCommentList.tsx
new file mode 100644
index 0000000..e9c71f1
--- /dev/null
+++ b/client/src/components/post/detail/PostCommentList.tsx
@@ -0,0 +1,46 @@
+import useGetCommentQuery from "@/queries/post/comment/useGetCommentQuery";
+import PostComment from "./PostComment";
+import { Card, Stack } from "@mui/material";
+
+type Props = {
+ postNo: string;
+};
+
+const PostCommentList = ({ postNo }: Props) => {
+ const { data: comments } = useGetCommentQuery({ postNo });
+
+ return comments.list.length > 0 ? (
+
+
+ {comments.list.map(
+ (
+ {
+ commentNo,
+ commentContent,
+ createdDate,
+ createdBy,
+ nickname,
+ userId,
+ profileImgUrls,
+ },
+ i
+ ) => (
+
+ )
+ )}
+
+
+ ) : (
+ <>>
+ );
+};
+
+export default PostCommentList;
diff --git a/client/src/components/post/detail/PostCommentSkeleton.tsx b/client/src/components/post/detail/PostCommentSkeleton.tsx
new file mode 100644
index 0000000..fbea0bc
--- /dev/null
+++ b/client/src/components/post/detail/PostCommentSkeleton.tsx
@@ -0,0 +1,22 @@
+import { Skeleton, Stack, Card } from "@mui/material";
+
+const PostCommentSkeleton = () => {
+ return (
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ );
+};
+
+export default PostCommentSkeleton;
diff --git a/client/src/components/post/detail/PostDetail.tsx b/client/src/components/post/detail/PostDetail.tsx
new file mode 100644
index 0000000..ab143e7
--- /dev/null
+++ b/client/src/components/post/detail/PostDetail.tsx
@@ -0,0 +1,34 @@
+"use client";
+
+import useGetPostDetailQuery from "@/queries/post/useGetPostDetailQuery";
+import PostCard from "@/components/post/PostCard";
+import { PostInterface } from "@/types/post/PostInterface";
+import PostCommentList from "@/components/post/detail/PostCommentList";
+import getTokenFromLocalStorage from "@/utils/getTokenFromLocalStorage";
+import { Suspense } from "react";
+import PostCommentSkeleton from "@/components/post/detail/PostCommentSkeleton";
+import PostDetailPageContext from "@/store/post/PostDetailPageContext";
+import PostCommentInput from "./PostCommentInput";
+
+export interface PostDetailInterface {
+ postNo: string;
+ initialData: PostInterface;
+}
+
+const PostDetail = ({ postNo, initialData }: PostDetailInterface) => {
+ const { data } = useGetPostDetailQuery(postNo, {
+ initialData,
+ headers: { Authorization: getTokenFromLocalStorage() },
+ });
+
+ return (
+
+
+ }>
+
+
+
+
+ );
+};
+export default PostDetail;
diff --git a/client/src/const/serverPath.ts b/client/src/const/serverPath.ts
index fe1135f..15aa942 100644
--- a/client/src/const/serverPath.ts
+++ b/client/src/const/serverPath.ts
@@ -30,6 +30,10 @@ export const LOGOUT_BFF = "/api/auth/logout-internal" as const;
* 게시물리스트를 받아오거나, 작성하는 Path
*/
export const POST_LIST = "/posts" as const;
+/**
+ * 게시물 pk 를 입력받아 댓글을 조회,생성 하는 URL
+ */
+export const POST_COMMENT = (pk:string) => `${POST_LIST}/${pk}/comments`
/**
* 게시물리스트를 받아오거나, 작성하는 Path 버전2 (Breaking Change)
@@ -39,7 +43,11 @@ export const POST_LIST_V2 = "/posts/v2" as const;
* ID(pk) 를 입력받아 해당 포스트를 지우는 URL
*/
export const REMOVE_POST = (pk: number) => `${POST_LIST}/${pk}` as const;
-
+/**
+ * 포스트의 PK를 입력받아 해당 PK의 게시글의 좋아요를 요청
+ * @param id 게시글의 PK
+ */
+export const POST_LIKE_URL = (id: string) => `/posts/like/${id}` as const;
/**
*
* @param type : 리소스의 타입 POST|PROFILE|ALCOHOL
@@ -63,11 +71,7 @@ export const REMOVE_FILE = (attachNo: string) => `/attach/${attachNo}` as const;
*/
export const GET_ALCOHOL_LIST = "/alcohols" as const;
-/**
- * 포스트의 PK를 입력받아 해당 PK의 게시글의 좋아요를 요청
- * @param id 게시글의 PK
- */
-export const POST_LIKE_URL = (id: string) => `/posts/like/${id}` as const;
+
/**
* 포스트의 PK를 입력받아 해당 PK의 게시글의 좋아요 취소를 요청
diff --git a/client/src/hooks/useOpenPostDetailPage.tsx b/client/src/hooks/useOpenPostDetailPage.tsx
index 5aea94c..fbb2b77 100644
--- a/client/src/hooks/useOpenPostDetailPage.tsx
+++ b/client/src/hooks/useOpenPostDetailPage.tsx
@@ -1,5 +1,5 @@
import { POST_DETAIL } from "@/const/clientPath";
-import { useRouter } from "next/navigation";
+import { usePathname, useRouter } from "next/navigation";
import { useCallback } from "react";
/**
@@ -8,8 +8,14 @@ import { useCallback } from "react";
*/
export const useOpenPostDetailPage = () => {
const router = useRouter();
+ const path = usePathname();
+
const openPostDetailPage = useCallback((userId: string, id: string) => {
- router.push(POST_DETAIL(String(userId), String(id)));
+
+ if (path !== POST_DETAIL(String(userId), String(id))) {
+ router.push(POST_DETAIL(String(userId), String(id)));
+ }
}, []);
+
return openPostDetailPage;
};
diff --git a/client/src/queries/post/comment/useGetCommentQuery.ts b/client/src/queries/post/comment/useGetCommentQuery.ts
new file mode 100644
index 0000000..a1d8704
--- /dev/null
+++ b/client/src/queries/post/comment/useGetCommentQuery.ts
@@ -0,0 +1,29 @@
+import { POST_COMMENT } from "@/const/serverPath";
+import axios from "@/libs/axios";
+import PostCommentListInterface from "@/types/post/PostCommentInterface";
+import { useSuspenseQuery } from "@tanstack/react-query";
+
+interface CommentQueryInterface {
+ postNo: string;
+}
+
+const useGetCommentQuery = ({ postNo }: CommentQueryInterface) => {
+ return useSuspenseQuery({
+ queryKey: commentQueryKey.byId(postNo),
+ queryFn: async () => await getCommentListQueryFn(postNo),
+ });
+};
+
+export const getCommentListQueryFn = async (
+ id: CommentQueryInterface["postNo"]
+) => {
+ const { data } = await axios.get(POST_COMMENT(id));
+ return data;
+};
+
+export const commentQueryKey = {
+ all: ["comment"] as const,
+ byId: (id?: string) => ["comment", { id }] as const,
+};
+
+export default useGetCommentQuery;
diff --git a/client/src/queries/post/comment/useNewPostCommentMutation.ts b/client/src/queries/post/comment/useNewPostCommentMutation.ts
new file mode 100644
index 0000000..789bfe9
--- /dev/null
+++ b/client/src/queries/post/comment/useNewPostCommentMutation.ts
@@ -0,0 +1,71 @@
+import { POST_COMMENT } from "@/const/serverPath";
+import useAxiosPrivate from "@/hooks/useAxiosPrivate";
+import { useMutation, useQueryClient } from "@tanstack/react-query";
+import { commentQueryKey } from "./useGetCommentQuery";
+import PostCommentListInterface from "@/types/post/PostCommentInterface";
+import { useErrorHandler } from "@/utils/errorHandler";
+import { useMyInfoQuery } from "@/queries/auth/useMyInfoQuery";
+
+const useNewPostCommentMutation = (id?: string) => {
+ const queryClient = useQueryClient();
+ const errorHandler = useErrorHandler();
+ const { data: myInfo } = useMyInfoQuery();
+
+ return useMutation({
+ mutationFn: async (content: string) => {
+ if (!id) {
+ return Promise.reject("id가 제공되지않음");
+ } else return postComment(id, content);
+ },
+ onMutate: (content) => {
+ queryClient.cancelQueries({ queryKey: commentQueryKey.byId(id) });
+
+ const querySnapShot = queryClient.getQueryData(
+ commentQueryKey.byId(id)
+ );
+
+ queryClient.setQueryData(
+ commentQueryKey.byId(id),
+ (prev) => {
+ return {
+ list: [
+ {
+ commentNo: Number.MAX_SAFE_INTEGER,
+ commentContent: content,
+ createdDate: String(new Date()),
+ lastModifiedDate: String(new Date()),
+ createdBy: myInfo?.nickname,
+ userId: myInfo?.id,
+ nickname: myInfo?.nickname,
+ profileImgUrls: myInfo?.profileImages,
+ },
+ ...(prev?.list ?? []),
+ ] as PostCommentListInterface["list"],
+ totalCount: (prev?.totalCount ?? 0) + 1,
+ };
+ }
+ );
+ return { querySnapShot };
+ },
+ onError: (err, queryFnParams, context) => {
+ errorHandler(err);
+ queryClient.setQueryData(
+ commentQueryKey.byId(queryFnParams),
+ context?.querySnapShot
+ );
+ },
+ onSuccess: () => {
+ queryClient.invalidateQueries({ queryKey: commentQueryKey.byId(id) });
+ },
+ });
+};
+export const postComment = async (postNo: string, content: string) => {
+ const axiosPrivate = useAxiosPrivate();
+ const { data } = await axiosPrivate.post<{ commentNo: string }>(
+ POST_COMMENT(postNo),
+ { commentContent: content }
+ );
+ return data;
+};
+
+export default useNewPostCommentMutation;
diff --git a/client/src/queries/post/useGetPostDetailQuery.tsx b/client/src/queries/post/useGetPostDetailQuery.tsx
index 8916f46..6296303 100644
--- a/client/src/queries/post/useGetPostDetailQuery.tsx
+++ b/client/src/queries/post/useGetPostDetailQuery.tsx
@@ -1,4 +1,4 @@
-import { useQuery } from "@tanstack/react-query";
+import { useSuspenseQuery } from "@tanstack/react-query";
import axios from "@/libs/axios";
import { PostInterface } from "@/types/post/PostInterface";
import { AxiosRequestConfig } from "axios";
@@ -10,7 +10,7 @@ interface PostdetailOption {
}
const useGetPostDetailQuery = (postId: string, options?: PostdetailOption) => {
- return useQuery({
+ return useSuspenseQuery({
queryKey: postDetailQueryKey.byId(postId),
queryFn: () => getPostDetailQueryFn(postId, options?.headers),
initialData: options?.initialData,
@@ -24,8 +24,7 @@ export const getPostDetailQueryFn = async (
const { data } = await axios.get(`/posts/${postId}`, {
baseURL: process.env.NEXT_PUBLIC_BASE_URL,
headers: {
- Authorization:
- options?.Authorization || getTokenFromLocalStorage(),
+ Authorization: options?.Authorization || getTokenFromLocalStorage(),
},
});
return data;
diff --git a/client/src/queries/post/useLikePostMutation.tsx b/client/src/queries/post/useLikePostMutation.tsx
index f39908d..2fee56f 100644
--- a/client/src/queries/post/useLikePostMutation.tsx
+++ b/client/src/queries/post/useLikePostMutation.tsx
@@ -13,7 +13,7 @@ import {
import getTokenFromLocalStorage from "@/utils/getTokenFromLocalStorage";
import { POST_LIKE_URL } from "@/const/serverPath";
import { useErrorHandler } from "@/utils/errorHandler";
-import { PostcardContextInterface } from "@/store/PostCardContext";
+import { PostcardContextInterface } from "@/store/post/PostCardContext";
import { useOptimisticUpdatePostList } from "@/queries/post/updator/useOptimisticUpdatePostList";
import { useOptimisticUpdatePostDetail } from "./updator/useOptimisticUpdatePostDetail";
import { postDetailQueryKey } from "./useGetPostDetailQuery";
diff --git a/client/src/queries/post/useUnLikePostMutation.tsx b/client/src/queries/post/useUnLikePostMutation.tsx
index abf74a8..661d13f 100644
--- a/client/src/queries/post/useUnLikePostMutation.tsx
+++ b/client/src/queries/post/useUnLikePostMutation.tsx
@@ -13,7 +13,7 @@ import {
import getTokenFromLocalStorage from "@/utils/getTokenFromLocalStorage";
import { POST_UN_LIKE_URL } from "@/const/serverPath";
import { useErrorHandler } from "@/utils/errorHandler";
-import { PostcardContextInterface } from "@/store/PostCardContext";
+import { PostcardContextInterface } from "@/store/post/PostCardContext";
import { useOptimisticUpdatePostList } from "@/queries/post/updator/useOptimisticUpdatePostList";
import { useOptimisticUpdatePostDetail } from "./updator/useOptimisticUpdatePostDetail";
import { postDetailQueryKey } from "./useGetPostDetailQuery";
diff --git a/client/src/store/PostCardContext.ts b/client/src/store/post/PostCardContext.ts
similarity index 100%
rename from client/src/store/PostCardContext.ts
rename to client/src/store/post/PostCardContext.ts
diff --git a/client/src/store/post/PostDetailPageContext.ts b/client/src/store/post/PostDetailPageContext.ts
new file mode 100644
index 0000000..2072d0f
--- /dev/null
+++ b/client/src/store/post/PostDetailPageContext.ts
@@ -0,0 +1,12 @@
+import { PostInterface } from "@/types/post/PostInterface";
+import { createContext } from "react";
+
+interface PostDetailPageContextInterface {
+ data?: PostInterface;
+}
+
+const PostDetailPageContext = createContext({
+ data: undefined,
+});
+
+export default PostDetailPageContext;
diff --git a/client/src/store/useGlobalNavbarVisibility.ts b/client/src/store/useGlobalNavbarVisibility.ts
new file mode 100644
index 0000000..070ea60
--- /dev/null
+++ b/client/src/store/useGlobalNavbarVisibility.ts
@@ -0,0 +1,15 @@
+import { create } from "zustand";
+
+interface GlobalNavbarVisibility {
+ isVisible: boolean;
+ setIsVisible: (val: boolean) => void;
+}
+/**
+ * 네비게이션바 (바텀네비게이션)을 표시할지 여부
+ */
+export const useGlobalNavbarVisibility = create(
+ (set) => ({
+ isVisible: true,
+ setIsVisible: (val) => set(() => ({ isVisible: val })),
+ })
+);
diff --git a/client/src/types/post/PostCommentInterface.ts b/client/src/types/post/PostCommentInterface.ts
new file mode 100644
index 0000000..5f0091e
--- /dev/null
+++ b/client/src/types/post/PostCommentInterface.ts
@@ -0,0 +1,19 @@
+import { ProfileImagesType } from "../user/userInfoInterface";
+
+interface PostCommentListInterface {
+ list: PostCommentInterface[];
+ totalCount: number;
+}
+
+export interface PostCommentInterface {
+ commentNo: number;
+ commentContent: string;
+ createdDate: string;
+ lastModifiedDate: string;
+ createdBy: number;
+ userId: string;
+ nickname: string;
+ profileImgUrls: ProfileImagesType[];
+}
+
+export default PostCommentListInterface;