diff --git a/client/src/__test__/post/PostCard.test.tsx b/client/src/__test__/post/PostCard.test.tsx
new file mode 100644
index 0000000..413bcea
--- /dev/null
+++ b/client/src/__test__/post/PostCard.test.tsx
@@ -0,0 +1,47 @@
+import "@testing-library/jest-dom";
+import { render, screen } from "@testing-library/react";
+import PostCard from "@/components/post/PostCard";
+
+const mockData = {
+ id: "123458",
+ createdAt: "Mon Nov 06 2023 00:13:07",
+ nickname: "testNick",
+ userId: "userID",
+ userImage: "https://source.unsplash.com/random?wallpapers",
+ content:
+ "Lorem ipsum dolor, sit amet consectetur adipisicing elit. Eos ullam aut minus aliquam quis officia, non dolore omnis, magnam totam tenetur ad harum? Mollitia omnis odit atque blanditiis exercitationem! Voluptatum.",
+ image: ["https://source.unsplash.com/random?wallpapers"],
+};
+
+describe("버튼 컴포넌트 스펙", () => {
+ beforeEach(() => render());
+ it("@유저아이디 형태의 헤더가 존재하는지 체크", () => {
+ expect(screen.getByTestId("mui-header")).toHaveTextContent(
+ `@${mockData.userId}`
+ );
+ });
+ it("유저 닉네임이 존재하는지 여부 체크", () => {
+ expect(screen.getByTestId("mui-header")).toHaveTextContent(
+ mockData.nickname
+ );
+ });
+ it("공유하기 버튼이 존재하는지 여부", () => {
+ expect(screen.getByTestId("shareBtn")).not.toBeNull();
+ });
+ it("좋아요 버튼이 존재하는지 여부", () => {
+ expect(screen.getByTestId("likeBtn")).not.toBeNull();
+ });
+});
+
+describe("버튼 컴포넌트 조건부렌더링 테스트", () => {
+ it("포스트에 이미지가 없을경우 이미지가 표시되지 않는지 여부", () => {
+ render();
+ const imgNode = screen.queryByTestId("postImg");
+ expect(imgNode).not.toBeInTheDocument();
+ });
+ it("유저 이미지가 없을경우 이미지대신 1글자의 더미문자 표시여부", () => {
+ render();
+ const imgNode = screen.queryByTestId("avatar");
+ expect(imgNode).toHaveTextContent(/./);
+ });
+});
diff --git a/client/src/app/layout.tsx b/client/src/app/layout.tsx
index aa40cf4..8e1f6ea 100644
--- a/client/src/app/layout.tsx
+++ b/client/src/app/layout.tsx
@@ -2,7 +2,7 @@ import type { Metadata, Viewport } from "next";
import ThemeRegistry from "@/components/theme/ThemeRegistry";
import { nameOfApp, oneLineMessage } from "@/const/brand";
import OverrideCSS from "@/const/overrideCSS";
-import { GlobalStyles } from "@mui/material";
+import { Box, GlobalStyles } from "@mui/material";
import Pretendard from "~/assets/font/Pretendard";
import NavigationBar from "~/components/NavigationBar";
@@ -25,7 +25,14 @@ export default function RootLayout({
- {children}
+
+ {children}
+
diff --git a/client/src/app/page.tsx b/client/src/app/page.tsx
index 5506174..d75255b 100644
--- a/client/src/app/page.tsx
+++ b/client/src/app/page.tsx
@@ -1,9 +1,10 @@
-import { Button, Paper } from "@mui/material";
+import PostCardList from "@/components/post/PostCardList";
+import { Container } from "@mui/material";
export default function Home() {
return (
-
-
-
+
+
+
);
}
diff --git a/client/src/components/NavigationBar.tsx b/client/src/components/NavigationBar.tsx
index c5c3489..03524fa 100644
--- a/client/src/components/NavigationBar.tsx
+++ b/client/src/components/NavigationBar.tsx
@@ -1,10 +1,5 @@
-import {
- AppBar,
- ButtonBase,
- ButtonBaseProps,
- Toolbar,
- Typography,
-} from "@mui/material";
+// FIXME "use client";
+import { BottomNavigation, BottomNavigationAction, Paper } from "@mui/material";
import HomeIcon from "~/assets/icons/HomeIcon.svg";
import SearchIcon from "~/assets/icons/SearchIcon.svg";
@@ -12,6 +7,7 @@ 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, WIKI } from "@/const/clientPath";
+import Link from "next/link";
const NavbarData = [
{
@@ -40,44 +36,30 @@ const NavbarData = [
];
const NavigationBar = () => {
+ // FIXME const path = usePathname();
return (
-
-
+
+
{NavbarData.map((buttonData) => {
return (
-
- {buttonData.label}
-
+ // FIXME value={buttonData.href}
+ // FIXME label={buttonData.label}
+ />
);
})}
-
-
+
+
);
};
export default NavigationBar;
-
-interface NavbarIconButtonInterface extends Omit {
- children: string;
- iconComponent: React.ReactNode;
- href?: string;
-}
-
-export const NavbarIconButton = ({
- children,
- iconComponent,
- ...others
-}: NavbarIconButtonInterface) => {
- return (
-
- {iconComponent}
-
- {children}
-
-
- );
-};
diff --git a/client/src/components/post/PostCard.tsx b/client/src/components/post/PostCard.tsx
new file mode 100644
index 0000000..c5e382b
--- /dev/null
+++ b/client/src/components/post/PostCard.tsx
@@ -0,0 +1,78 @@
+"use client";
+import { PostInterface } from "@/types/post/PostInterface";
+import {
+ FavoriteOutlined,
+ MoreVertOutlined,
+ ShareOutlined,
+} from "@mui/icons-material";
+import {
+ Avatar,
+ Card,
+ CardActions,
+ CardContent,
+ CardHeader,
+ CardMedia,
+ IconButton,
+ Typography,
+} from "@mui/material";
+
+const PostCard = ({
+ image,
+ createdAt,
+ userId,
+ nickname,
+ content,
+ userImage,
+}: PostInterface) => {
+ return (
+
+ {/* Header */}
+
+ {userImage || userId[0].toUpperCase()}
+
+ }
+ action={
+
+
+
+ }
+ title={`@${userId}`}
+ subheader={nickname}
+ />
+ {/* image */}
+ {image.length !== 0 && (
+
+ )}
+ {/* Contents */}
+
+
+ {content}
+
+
+ {/* CTA */}
+
+
+
+
+
+
+
+
+
+ );
+};
+
+export default PostCard;
diff --git a/client/src/components/post/PostCardList.tsx b/client/src/components/post/PostCardList.tsx
new file mode 100644
index 0000000..cc07a82
--- /dev/null
+++ b/client/src/components/post/PostCardList.tsx
@@ -0,0 +1,32 @@
+import { Paper } from "@mui/material";
+import PostCard from "@/components/post/PostCard";
+import { PostInterface } from "@/types/post/PostInterface";
+
+const PostCardList = () => {
+ const dummyPost: PostInterface[] = Array.from(new Array(5)).map((data, i) => {
+ return {
+ id: String(i),
+ createdAt: "Mon Nov 06 2023 00:13:07",
+ nickname: "testNick",
+ userId: "userID",
+ userImage:
+ i % 2 !== 0
+ ? undefined
+ : "https://source.unsplash.com/random?wallpapers",
+ content:
+ "Lorem ipsum dolor, sit amet consectetur adipisicing elit. Eos ullam aut minus aliquam quis officia, non dolore omnis, magnam totam tenetur ad harum? Mollitia omnis odit atque blanditiis exercitationem! Voluptatum.",
+ image:
+ i % 2 === 0 ? [] : ["https://source.unsplash.com/random?wallpapers"],
+ };
+ });
+
+ return (
+ <>
+ {dummyPost.map((post) => (
+
+ ))}
+ >
+ );
+};
+
+export default PostCardList;
diff --git a/client/src/stories/Components/Post/PostCard.stories.tsx b/client/src/stories/Components/Post/PostCard.stories.tsx
new file mode 100644
index 0000000..92a1860
--- /dev/null
+++ b/client/src/stories/Components/Post/PostCard.stories.tsx
@@ -0,0 +1,50 @@
+import PostCard from "@/components/post/PostCard";
+import { Container, Paper } from "@mui/material";
+import { Meta, StoryObj } from "@storybook/react";
+
+const mockData = {
+ id: "123458",
+ createdAt: "Mon Nov 06 2023 00:13:07",
+ nickname: "testNick",
+ userId: "userID",
+ userImage: "",
+ content:
+ "Lorem ipsum dolor, sit amet consectetur adipisicing elit. Eos ullam aut minus aliquam quis officia, non dolore omnis, magnam totam tenetur ad harum? Mollitia omnis odit atque blanditiis exercitationem! Voluptatum.",
+ image: ["https://source.unsplash.com/random?wallpapers"],
+};
+
+const meta = {
+ title: "Components/Post/PostCard",
+ component: PostCard,
+ tags: ["autodocs"],
+ decorators: [
+ (Story) => {
+ return (
+
+
+
+ );
+ },
+ ],
+} satisfies Meta;
+
+export default meta;
+
+type Story = StoryObj;
+export const Default: Story = {
+ args: {
+ ...mockData,
+ },
+};
+export const withoutImage: Story = {
+ args: {
+ ...mockData,
+ image: [],
+ },
+};
+export const withoutUserImage: Story = {
+ args: {
+ ...mockData,
+ userImage: undefined,
+ },
+};
diff --git a/client/src/types/post/PostInterface.ts b/client/src/types/post/PostInterface.ts
new file mode 100644
index 0000000..ebc6c38
--- /dev/null
+++ b/client/src/types/post/PostInterface.ts
@@ -0,0 +1,33 @@
+/**
+ * 서버로부터 응답받은 포스트 간략정보
+ */
+export interface PostInterface {
+ /**
+ * 글 PK
+ */
+ id: string;
+ /**
+ * 글 최초 생성일
+ */
+ createdAt: string;
+ /**
+ * 유저가 설정한 닉네임
+ */
+ nickname: string;
+ /**
+ * 유저의 ID (로그인용 Email 과는 다름)
+ */
+ userId: string;
+ /**
+ * 유저 프로필 (Optional, 없을 경우 유저 닉네임 첫글자로 대체)
+ */
+ userImage?: string;
+ /**
+ * 포스트 내용
+ */
+ content: string;
+ /**
+ * 이미지 Href 배열
+ */
+ image: [] | string[];
+}