diff --git a/client/src/__test__/post/PostCard.test.tsx b/client/src/__test__/post/PostCard.test.tsx
index df38304..bd0e246 100644
--- a/client/src/__test__/post/PostCard.test.tsx
+++ b/client/src/__test__/post/PostCard.test.tsx
@@ -14,6 +14,14 @@ const mockData = {
tags: [],
};
+jest.mock("next/navigation", () => ({
+ useRouter() {
+ return {
+ prefetch: () => null,
+ };
+ },
+}));
+
describe("버튼 컴포넌트 스펙", () => {
beforeEach(() => render());
it("@유저아이디 형태의 헤더가 존재하는지 체크", () => {
diff --git a/client/src/app/@Modal/(.)post/[userId]/[postId]/page.tsx b/client/src/app/@Modal/(.)post/[userId]/[postId]/page.tsx
index 9bb2c25..58560f7 100644
--- a/client/src/app/@Modal/(.)post/[userId]/[postId]/page.tsx
+++ b/client/src/app/@Modal/(.)post/[userId]/[postId]/page.tsx
@@ -15,7 +15,7 @@ const mockData = {
const page = () => {
return (
-
+
);
diff --git a/client/src/app/@Modal/layout.tsx b/client/src/app/@Modal/layout.tsx
new file mode 100644
index 0000000..36d4239
--- /dev/null
+++ b/client/src/app/@Modal/layout.tsx
@@ -0,0 +1,8 @@
+"use client";
+
+import { usePathname } from "next/navigation";
+
+export default function Layout({ children }: any) {
+ const pathname = usePathname();
+ return pathname.startsWith("/post/") ? children : null;
+}
diff --git a/client/src/app/layout.tsx b/client/src/app/layout.tsx
index b445a08..5ea2688 100644
--- a/client/src/app/layout.tsx
+++ b/client/src/app/layout.tsx
@@ -5,6 +5,7 @@ import OverrideCSS from "@/const/overrideCSS";
import { Box, GlobalStyles } from "@mui/material";
import Pretendard from "~/assets/font/Pretendard";
import NavigationBar from "~/components/NavigationBar";
+import "./globals.css";
export const metadata: Metadata = {
title: `${nameOfApp} | ${oneLineMessage}`,
diff --git a/client/src/app/search/page.tsx b/client/src/app/search/page.tsx
new file mode 100644
index 0000000..56ba6aa
--- /dev/null
+++ b/client/src/app/search/page.tsx
@@ -0,0 +1,9 @@
+const SearchPage = ({
+ searchParams,
+}: {
+ searchParams?: { [key: string]: string | string[] | undefined };
+}) => {
+ return {searchParams?.keyword}
;
+};
+
+export default SearchPage;
diff --git a/client/src/components/ModalWrapper.tsx b/client/src/components/ModalWrapper.tsx
index 3e82ab1..08a5442 100644
--- a/client/src/components/ModalWrapper.tsx
+++ b/client/src/components/ModalWrapper.tsx
@@ -32,6 +32,7 @@ const ModalWrapper = ({ children, disableBox }: ModalInterface) => {
>
{
e.stopPropagation()}
sx={{
bgcolor: disableBox ? undefined : "background.paper",
p: disableBox ? 0 : 4,
diff --git a/client/src/components/post/PostCard.tsx b/client/src/components/post/PostCard.tsx
index c22e48b..a5e0a63 100644
--- a/client/src/components/post/PostCard.tsx
+++ b/client/src/components/post/PostCard.tsx
@@ -1,5 +1,5 @@
"use client";
-import { POST_DETAIL } from "@/const/clientPath";
+
import { PostInterface } from "@/types/post/PostInterface";
import { MoreVertOutlined } from "@mui/icons-material";
@@ -13,7 +13,9 @@ import {
Typography,
ButtonBase,
} from "@mui/material";
-import Link from "next/link";
+import PostHashTagList from "./PostHashtagList";
+import { useOpenPostDetailPage } from "@/hooks/useOpenPostDetailPage";
+import { useMemo } from "react";
const PostCard = ({
image,
@@ -25,100 +27,92 @@ const PostCard = ({
tags,
id,
}: PostInterface) => {
+ const openPostDetailPage = useOpenPostDetailPage();
+ const hasImage = useMemo(() => image.length !== 0, [image]);
+
return (
-
-
-
+
+ {userImage || userId[0].toUpperCase()}
+
+
+ {/* Header */}
+
- {userImage || userId[0].toUpperCase()}
-
-
- {/* Header */}
-
- {nickname}
- {`@${userId}`}
- {createdAt}
-
-
-
-
-
+ {/* 타이틀 */}
+ {nickname}
+ {`@${userId}`}
+ {createdAt}
- {/* Contents */}
-
- {content}
- {/* Hash tags */}
- {tags?.length > 0 && (
-
- {tags.map((tag, i) => (
-
- {`#${tag}`}
-
- ))}
-
- )}
-
- {/* image */}
- {image.length !== 0 && (
-
- )}
- {/* CTA */}
-
-
- 댓글
-
-
- 좋아요
-
-
- 공유하기
-
-
+
+
+
-
-
+
+
+ {/* Contents */}
+ openPostDetailPage(userId, id)}
+ >
+ {content}
+
+
+ {/* Hash tags */}
+
+
+
+ {/* image */}
+ {hasImage && (
+ openPostDetailPage(userId, id)}
+ image={image[0]}
+ alt={`${userId}의 포스트`}
+ sx={{ borderRadius: 2, bgcolor: "background.default" }}
+ />
+ )}
+ {/* CTA */}
+
+
+ openPostDetailPage(userId, id)}>
+ 댓글
+
+
+
+ 좋아요
+
+
+ 공유하기
+
+
+
+
);
};
diff --git a/client/src/components/post/PostDetail.tsx b/client/src/components/post/PostDetail.tsx
index 87a9ae5..b722855 100644
--- a/client/src/components/post/PostDetail.tsx
+++ b/client/src/components/post/PostDetail.tsx
@@ -1,4 +1,16 @@
import { PostInterface } from "@/types/post/PostInterface";
+import {
+ Avatar,
+ ButtonBase,
+ Card,
+ CardActions,
+ CardContent,
+ CardHeader,
+ CardMedia,
+ TextField,
+ Typography,
+} from "@mui/material";
+import PostHashTagList from "./PostHashtagList";
const PostDetail = ({
image,
@@ -10,6 +22,47 @@ const PostDetail = ({
tags,
id,
}: PostInterface) => {
- return <>{userId}>;
+ return (
+
+
+ {userImage || userId[0].toUpperCase()}
+
+ }
+ title={`${userId} ${nickname}`}
+ subheader={createdAt}
+ sx={{ p: 0 }}
+ />
+
+ {content}
+
+
+ {/* 이미지 */}
+ {image.length !== 0 && (
+
+ )}
+
+
+ 좋아요
+
+
+ 공유하기
+
+
+
+
+ );
};
export default PostDetail;
diff --git a/client/src/components/post/PostHashtagList.tsx b/client/src/components/post/PostHashtagList.tsx
new file mode 100644
index 0000000..07a6fba
--- /dev/null
+++ b/client/src/components/post/PostHashtagList.tsx
@@ -0,0 +1,40 @@
+import { SEARCH_BY_KEYWORD } from "@/const/clientPath";
+import { PostInterface } from "@/types/post/PostInterface";
+import { Box, BoxProps, Typography } from "@mui/material";
+import Link from "next/link";
+
+interface TagListInterface extends BoxProps {
+ tags: PostInterface["tags"];
+}
+const PostHashTagList = ({ tags, ...others }: TagListInterface) => {
+ return (
+ <>
+ {tags?.length > 0 && (
+
+ {tags.map((tag) => (
+
+
+ {`#${tag}`}
+
+
+ ))}
+
+ )}
+ >
+ );
+};
+
+export default PostHashTagList;
diff --git a/client/src/const/clientPath.ts b/client/src/const/clientPath.ts
index eb532dd..2de5514 100644
--- a/client/src/const/clientPath.ts
+++ b/client/src/const/clientPath.ts
@@ -14,11 +14,22 @@ export const FORGOTPASSWORD = "/auth/forgot-password" as const;
/**
* 로그인 했을 경우만 접근 가능한 마이페이지
*/
-export const MY_PROFILE = "/user" as const
+export const MY_PROFILE = "/user" as const;
/**
* 술과사전 페이지 라우트
*/
-export const WIKI = "/wiki" as const
+export const WIKI = "/wiki" as const;
+/**
+ * 검색 페이지 라우트
+ */
+export const SEARCH = "/search" as const;
+/**
+ * 키워드를 인자로 받아 쿼리스트링이 추가된 검색페이지 라우트
+ * @param keyword
+ * @returns
+ */
+export const SEARCH_BY_KEYWORD = (keyword: string) =>
+ `${SEARCH}?keyword=${keyword}`;
/**
* 유저아이디와 게시글 아이디를 입력받아 /post/@userId/postId 형태의 path를 리턴
@@ -29,7 +40,7 @@ export const WIKI = "/wiki" as const
export const POST_DETAIL = (userId: string, postId: string) => {
const trimmedUserId = userId.trim();
const trimmedPostId = postId.trim();
-
+
return `/post/@${trimmedUserId}/${trimmedPostId}`;
};
diff --git a/client/src/hooks/useOpenPostDetailPage.tsx b/client/src/hooks/useOpenPostDetailPage.tsx
new file mode 100644
index 0000000..171e6f3
--- /dev/null
+++ b/client/src/hooks/useOpenPostDetailPage.tsx
@@ -0,0 +1,15 @@
+import { POST_DETAIL } from "@/const/clientPath";
+import { useRouter } from "next/navigation";
+import { useCallback } from "react";
+
+/**
+ * userId: string, id: string 의 입력값을 같는 함수를 리턴하는 hooks
+ * @returns
+ */
+export const useOpenPostDetailPage = () => {
+ const router = useRouter();
+ const openPostDetailPage = useCallback((userId: string, id: string) => {
+ router.push(POST_DETAIL(userId, id));
+ }, []);
+ return openPostDetailPage;
+};