From 586c5b19a322f0479772e8ec3a6579a850969876 Mon Sep 17 00:00:00 2001 From: ijh1298 Date: Mon, 3 Jun 2024 14:42:17 +0900 Subject: [PATCH] feat: blog template --- package-lock.json | 41 +++++++++++++++++++++++++- package.json | 3 +- src/App.jsx | 9 +++++- src/components/Card.jsx | 41 ++++++++++++++------------ src/components/Card.module.css | 9 ++++-- src/components/NavBar.jsx | 33 +++++++++++---------- src/components/NavBar.module.css | 2 +- src/components/Post.jsx | 23 +++++++++++++++ src/components/Post.module.css | 44 ++++++++++++++++++++++++++++ src/globals.css | 2 +- src/main.jsx | 7 +++-- src/pages/PostPage.jsx | 50 ++++++++++++++++++++++++++++++++ 12 files changed, 220 insertions(+), 44 deletions(-) create mode 100644 src/components/Post.jsx create mode 100644 src/components/Post.module.css create mode 100644 src/pages/PostPage.jsx diff --git a/package-lock.json b/package-lock.json index 93a56a7..f865f85 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,7 +9,8 @@ "version": "0.0.0", "dependencies": { "react": "^18.2.0", - "react-dom": "^18.2.0" + "react-dom": "^18.2.0", + "react-router-dom": "^6.23.1" }, "devDependencies": { "@types/react": "^18.2.14", @@ -901,6 +902,14 @@ "node": ">= 8" } }, + "node_modules/@remix-run/router": { + "version": "1.16.1", + "resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.16.1.tgz", + "integrity": "sha512-es2g3dq6Nb07iFxGk5GuHN20RwBZOsuDQN7izWIisUcv9r+d2C5jQxqmgkdebXgReWfiyUabcki6Fg77mSNrig==", + "engines": { + "node": ">=14.0.0" + } + }, "node_modules/@types/babel__core": { "version": "7.20.5", "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz", @@ -3306,6 +3315,36 @@ "node": ">=0.10.0" } }, + "node_modules/react-router": { + "version": "6.23.1", + "resolved": "https://registry.npmjs.org/react-router/-/react-router-6.23.1.tgz", + "integrity": "sha512-fzcOaRF69uvqbbM7OhvQyBTFDVrrGlsFdS3AL+1KfIBtGETibHzi3FkoTRyiDJnWNc2VxrfvR+657ROHjaNjqQ==", + "dependencies": { + "@remix-run/router": "1.16.1" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "react": ">=16.8" + } + }, + "node_modules/react-router-dom": { + "version": "6.23.1", + "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-6.23.1.tgz", + "integrity": "sha512-utP+K+aSTtEdbWpC+4gxhdlPFwuEfDKq8ZrPFU65bbRJY+l706qjR7yaidBpo3MSeA/fzwbXWbKBI6ftOnP3OQ==", + "dependencies": { + "@remix-run/router": "1.16.1", + "react-router": "6.23.1" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "react": ">=16.8", + "react-dom": ">=16.8" + } + }, "node_modules/reflect.getprototypeof": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/reflect.getprototypeof/-/reflect.getprototypeof-1.0.6.tgz", diff --git a/package.json b/package.json index aab6f17..21e3dac 100644 --- a/package.json +++ b/package.json @@ -11,7 +11,8 @@ }, "dependencies": { "react": "^18.2.0", - "react-dom": "^18.2.0" + "react-dom": "^18.2.0", + "react-router-dom": "^6.23.1" }, "devDependencies": { "@types/react": "^18.2.14", diff --git a/src/App.jsx b/src/App.jsx index b7f9f7b..66749f5 100644 --- a/src/App.jsx +++ b/src/App.jsx @@ -1,11 +1,18 @@ +import { Route, Routes } from "react-router-dom"; import { NavBar } from "./components/NavBar"; import HomePage from "./pages/HomePage"; +import PostPage from "./pages/PostPage"; +import NewPostPage from "./pages/NewPostPage"; export default function App() { return ( <> - + + } /> + } /> + } /> + ); } diff --git a/src/components/Card.jsx b/src/components/Card.jsx index 471de3b..00ec0f2 100644 --- a/src/components/Card.jsx +++ b/src/components/Card.jsx @@ -1,25 +1,30 @@ +import { useNavigate } from "react-router-dom"; import styles from "./Card.module.css"; -export const Card = ({ title, author, likes, createdAt }) => { - return ( -
-

- {title} - ❤️ {likes} -

+export const Card = ({ id, title, author, likes, createdAt }) => { + const navigate = useNavigate(); + return ( +
navigate(`/posts/${id}`)} + > +

+ {title} + ❤️{likes} +

-

글쓴이 : {author}

-

작성일 : {createdAt}

-
- ); +

글쓴이 : {author}

+

작성일 : {createdAt}

+
+ ); }; export const CardSkeleton = () => { - return ( -
-
-
-
-
- ); + return ( +
+
+
+
+
+ ); }; diff --git a/src/components/Card.module.css b/src/components/Card.module.css index 813c67b..d425221 100644 --- a/src/components/Card.module.css +++ b/src/components/Card.module.css @@ -1,14 +1,19 @@ .card_wrapper { - width: min(100%, 500px); + width: min(100%, 1000px); margin: 20px auto; padding: 20px; - border-radius: 15px; box-shadow: 0px 0px 50px 0px rgba(0, 0, 0, 0.2); cursor: pointer; } +.card_wrapper:hover { + box-shadow: 0px 0px 50px 0px rgba(0, 0, 0, 0.4); + background-color: #44559930; + transition: 0.25s; + cursor: pointer; +} .card_head { display: flex; diff --git a/src/components/NavBar.jsx b/src/components/NavBar.jsx index 9f6c588..bdd2b36 100644 --- a/src/components/NavBar.jsx +++ b/src/components/NavBar.jsx @@ -1,23 +1,24 @@ +import { Link } from "react-router-dom"; import styles from "./NavBar.module.css"; export const NavBar = () => { - return ( - + ); }; diff --git a/src/components/NavBar.module.css b/src/components/NavBar.module.css index bd52500..1d211f3 100644 --- a/src/components/NavBar.module.css +++ b/src/components/NavBar.module.css @@ -2,7 +2,7 @@ width: 100%; height: 60px; - background-color: #303030; + background-color: #445599; color: #fff; } diff --git a/src/components/Post.jsx b/src/components/Post.jsx new file mode 100644 index 0000000..a01da95 --- /dev/null +++ b/src/components/Post.jsx @@ -0,0 +1,23 @@ +import { useNavigate } from "react-router-dom"; +import styles from "./Post.module.css"; + +export const Post = ({ id, title, author, likes, createdAt, content }) => { + const navigate = useNavigate(); + return ( +
+

#{id}번째 게시글

+

+ {title} + ❤️ {likes} +

+
+ {author} + {createdAt} +
+
+

{content}

+
+ +
+ ); +}; diff --git a/src/components/Post.module.css b/src/components/Post.module.css new file mode 100644 index 0000000..28b0932 --- /dev/null +++ b/src/components/Post.module.css @@ -0,0 +1,44 @@ +.post_wrapper { + width: min(100%, 900px); + + margin: 20px auto; + padding: 20px; + + box-shadow: 0px 0px 50px 0px rgba(0, 0, 0, 0.2); +} +.post_auth { + display: flex; + gap: 10px; + align-items: end; + margin: 2px 0px; +} +.post_auth > span:first-child { + font-weight: bold; +} +.post_auth > span:last-child { + color: grey; + font-size: 14px; +} +.post_content { + margin: 30px 0px; + padding: 20px 0px; + border-top: 1px solid #ccc; + border-bottom: 1px solid #ccc; +} + +.post_wrapper > button { + position: relative; + left: 88%; + width: 100px; + padding: 5px 0px; + border: 1px solid #303030; + border-radius: 5px; + color: #303030; + background-color: white; +} +.post_wrapper > button:hover { + background-color: #44559940; + border: 1px solid #303030; + transition: 0.5s; + cursor: pointer; +} \ No newline at end of file diff --git a/src/globals.css b/src/globals.css index 10f4a31..e7d95e9 100644 --- a/src/globals.css +++ b/src/globals.css @@ -18,4 +18,4 @@ a:visited { } #root { -} +} \ No newline at end of file diff --git a/src/main.jsx b/src/main.jsx index fb934ac..2f55934 100644 --- a/src/main.jsx +++ b/src/main.jsx @@ -2,9 +2,10 @@ import React from "react"; import ReactDOM from "react-dom/client"; import App from "./App.jsx"; import "./globals.css"; +import { BrowserRouter } from "react-router-dom"; ReactDOM.createRoot(document.getElementById("root")).render( - - - + + + ); diff --git a/src/pages/PostPage.jsx b/src/pages/PostPage.jsx new file mode 100644 index 0000000..11c389a --- /dev/null +++ b/src/pages/PostPage.jsx @@ -0,0 +1,50 @@ +import { useEffect, useState } from "react"; +import { Post } from "../components/Post"; +import { useParams } from "react-router-dom"; + +export default function PostPage() { + const [isPending, setIsPending] = useState(false); + const [post, setPost] = useState([]); + const params = useParams(); + + useEffect(() => { + setIsPending(true); + fetch(`http://localhost:8080/posts/${params.id}`) + .then((response) => { + return response.json(); + }) + .then((post) => { + setPost(post); + }) + .finally(() => { + setIsPending(false); + }); + }, []); + return ( + <> + {isPending ? ( + <> +

로딩중 ...

+ + ) : ( + + )} + + ); +}