From c2c90613638470bcfda954950127af7d2b6ec47b Mon Sep 17 00:00:00 2001 From: ch9968 Date: Sat, 22 Jun 2024 21:14:01 +0900 Subject: [PATCH] final commit --- week10/my-app/package-lock.json | 51 +++++ week10/my-app/package.json | 3 + week10/my-app/src/App.jsx | 87 +++++++- week10/my-app/src/api/comment.js | 37 ++++ week10/my-app/src/api/heart.js | 57 ++++++ week10/my-app/src/api/index.js | 2 + week10/my-app/src/api/post.js | 40 ++-- week10/my-app/src/pages/Detail.jsx | 290 +++++++++++++++++++++++++++ week10/my-app/src/pages/Heart.jsx | 45 +++++ week10/my-app/src/pages/List.jsx | 77 ++++++- week10/my-app/src/pages/Register.jsx | 161 ++++++++++----- week10/my-app/src/pages/Write.jsx | 55 ++++- 12 files changed, 825 insertions(+), 80 deletions(-) diff --git a/week10/my-app/package-lock.json b/week10/my-app/package-lock.json index 8b830d18e..8e02fa5a3 100644 --- a/week10/my-app/package-lock.json +++ b/week10/my-app/package-lock.json @@ -14,8 +14,11 @@ "axios": "^1.7.2", "react": "^18.3.1", "react-dom": "^18.3.1", + "react-icons": "^5.2.1", + "react-redux": "^9.1.2", "react-router-dom": "^6.23.1", "react-scripts": "5.0.1", + "redux": "^5.0.1", "styled-components": "^6.1.11", "web-vitals": "^2.1.4" } @@ -4523,6 +4526,11 @@ "resolved": "https://registry.npmjs.org/@types/trusted-types/-/trusted-types-2.0.7.tgz", "integrity": "sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==" }, + "node_modules/@types/use-sync-external-store": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/@types/use-sync-external-store/-/use-sync-external-store-0.0.3.tgz", + "integrity": "sha512-EwmlvuaxPNej9+T4v5AuBPJa2x2UOJVdjCtDHgcDqitUeOtjnJKJ+apYjVcAoBEMjKW1VVFGZLUb5+qqa09XFA==" + }, "node_modules/@types/ws": { "version": "8.5.10", "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.5.10.tgz", @@ -15166,11 +15174,41 @@ "resolved": "https://registry.npmjs.org/react-error-overlay/-/react-error-overlay-6.0.11.tgz", "integrity": "sha512-/6UZ2qgEyH2aqzYZgQPxEnz33NJ2gNsnHA2o5+o4wW9bLM/JYQitNP9xPhsXwC08hMMovfGe/8retsdDsczPRg==" }, + "node_modules/react-icons": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/react-icons/-/react-icons-5.2.1.tgz", + "integrity": "sha512-zdbW5GstTzXaVKvGSyTaBalt7HSfuK5ovrzlpyiWHAFXndXTdd/1hdDHI4xBM1Mn7YriT6aqESucFl9kEXzrdw==", + "peerDependencies": { + "react": "*" + } + }, "node_modules/react-is": { "version": "17.0.2", "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz", "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==" }, + "node_modules/react-redux": { + "version": "9.1.2", + "resolved": "https://registry.npmjs.org/react-redux/-/react-redux-9.1.2.tgz", + "integrity": "sha512-0OA4dhM1W48l3uzmv6B7TXPCGmokUU4p1M44DGN2/D9a1FjVPukVjER1PcPX97jIg6aUeLq1XJo1IpfbgULn0w==", + "dependencies": { + "@types/use-sync-external-store": "^0.0.3", + "use-sync-external-store": "^1.0.0" + }, + "peerDependencies": { + "@types/react": "^18.2.25", + "react": "^18.0", + "redux": "^5.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "redux": { + "optional": true + } + } + }, "node_modules/react-refresh": { "version": "0.11.0", "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.11.0.tgz", @@ -15336,6 +15374,11 @@ "node": ">=8" } }, + "node_modules/redux": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/redux/-/redux-5.0.1.tgz", + "integrity": "sha512-M9/ELqF6fy8FwmkpnF0S3YKOqMyoWJ4+CS5Efg2ct3oY9daQvd/Pc71FpGZsVsbl3Cpb+IIcjBDUnnyBdQbq4w==" + }, "node_modules/reflect.getprototypeof": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/reflect.getprototypeof/-/reflect.getprototypeof-1.0.6.tgz", @@ -17459,6 +17502,14 @@ "requires-port": "^1.0.0" } }, + "node_modules/use-sync-external-store": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.2.2.tgz", + "integrity": "sha512-PElTlVMwpblvbNqQ82d2n6RjStvdSoNe9FG28kNfz3WiXilJm4DdNkEzRhCZuIDwY8U08WVihhGR5iRqAwfDiw==", + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0" + } + }, "node_modules/util-deprecate": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", diff --git a/week10/my-app/package.json b/week10/my-app/package.json index 8dd3baec9..5003b3d11 100644 --- a/week10/my-app/package.json +++ b/week10/my-app/package.json @@ -9,8 +9,11 @@ "axios": "^1.7.2", "react": "^18.3.1", "react-dom": "^18.3.1", + "react-icons": "^5.2.1", + "react-redux": "^9.1.2", "react-router-dom": "^6.23.1", "react-scripts": "5.0.1", + "redux": "^5.0.1", "styled-components": "^6.1.11", "web-vitals": "^2.1.4" }, diff --git a/week10/my-app/src/App.jsx b/week10/my-app/src/App.jsx index 9d90d9b14..6aecda0e0 100644 --- a/week10/my-app/src/App.jsx +++ b/week10/my-app/src/App.jsx @@ -1,25 +1,96 @@ import RegisterPage from "./pages/Register"; import CreatePage from "./pages/Write"; +import ListPage from "./pages/List"; +import DetailPage from "./pages/Detail"; import { Routes, Route, NavLink } from "react-router-dom"; import styled from "styled-components"; +import { useState, useEffect } from "react"; +import { GetPostsApi } from "./api/post"; +import { GetUserHeartApi } from "./api/heart"; +import { Navigate } from "react-router-dom"; + function App() { + const [posts, setPosts] = useState(null); + const [heartPosts, setHeartPosts] = useState(); + + async function getPosts() { + try { + const data = await GetPostsApi(); + setPosts(data); + } catch (err) { + console.log(err.message); + } + } + + async function getHeartPosts() { + console.log("test"); + try { + const data = await GetUserHeartApi(); + setHeartPosts(data); + } catch (err) { + console.log(err.message); + } + } + + useEffect(() => { + getPosts(); + getHeartPosts(); + }, []); + return (
- 글 목록 - 글쓰기 - 로그인/회원가입 - 좋아요 누른 글 + 글 목록 + 글쓰기 + 로그인/회원가입 + + 좋아요 누른 글 + - {/* } /> */} - {/* } /> */} + } /> + } + /> + } + /> } /> } /> - {/* } /> */} + } + />
); } + export default App; -const Navbar = styled.div``; + +const Navbar = styled.div` + display: flex; + justify-content: space-around; + align-items: center; + background-color: #333; + padding: 10px 20px; +`; + +const StyledNavLink = styled(NavLink)` + color: white; + text-decoration: none; + font-size: 18px; + padding: 10px 20px; + + &.active { + background-color: #555; + border-radius: 4px; + } + + &:hover { + background-color: #444; + border-radius: 4px; + } +`; diff --git a/week10/my-app/src/api/comment.js b/week10/my-app/src/api/comment.js index e69de29bb..817307496 100644 --- a/week10/my-app/src/api/comment.js +++ b/week10/my-app/src/api/comment.js @@ -0,0 +1,37 @@ +import client from "."; +import axios from "axios"; + +export const GetCommentApi = async (postId) => { + try { + if (postId) { + const res = await client.get( + `${process.env.REACT_APP_SERVER_URL}/posts/${postId}/comments` + ); + console.log(res.data); + return res.data; + } + } catch (err) { + console.log(err); + } +}; + +export const PostCommentApi = async (postId, content) => { + try { + await client.post(`${process.env.REACT_APP_SERVER_URL}/comments`, { + postId, + content, + }); + } catch (err) { + console.log(err); + } +}; + +export const DeleteCommentApi = async (commentId) => { + try { + await client.delete( + `${process.env.REACT_APP_SERVER_URL}/comments/${commentId}` + ); + } catch (err) { + console.log(err); + } +}; diff --git a/week10/my-app/src/api/heart.js b/week10/my-app/src/api/heart.js index e69de29bb..5a91d1536 100644 --- a/week10/my-app/src/api/heart.js +++ b/week10/my-app/src/api/heart.js @@ -0,0 +1,57 @@ +import client from "."; +import axios from "axios"; + +export const GetHeartApi = async (postId) => { + try { + if (postId) { + const res = await client.get(`/posts/${postId}/hearts`); + // console.log(res); + return res.data; + } + } catch (err) { + console.log(err); + } +}; + +export const PostHeartApi = async (postId) => { + try { + const res = await client.post(`/posts/${postId}/hearts`); + // console.log(res); + } catch (err) { + console.log(err.message); + } +}; + +export const DeleteHeartApi = async (postId) => { + try { + await client.delete(`/posts/${postId}/hearts`); + } catch (err) { + console.log(err); + } +}; + +export const GetUserHeartApi = async () => { + try { + const res = await client.get(`/posts/hearts`); + return res.data; + } catch (err) { + console.log(err); + } +}; + +export const FindIfLiked = async (postId) => { + var liked = false; + try { + const list = await GetUserHeartApi(); + if (list.length > 0) { + list.forEach(function (post) { + if (post.postId.toString() == postId) { + liked = true; + } + }); + } + } catch (err) { + console.log(err.message); + } + return liked; +}; diff --git a/week10/my-app/src/api/index.js b/week10/my-app/src/api/index.js index 168c64f5f..21947d2ad 100644 --- a/week10/my-app/src/api/index.js +++ b/week10/my-app/src/api/index.js @@ -1,4 +1,5 @@ import axios from "axios"; + const client = axios.create(); client.defaults.baseURL = process.env.REACT_APP_SERVER_URL; client.defaults.withCredentials = true; @@ -9,4 +10,5 @@ console.log( "현재 axios instance 헤더 토큰", client.defaults.headers.common["Authorization"] ); + export default client; diff --git a/week10/my-app/src/api/post.js b/week10/my-app/src/api/post.js index 2046b09b3..e067b5591 100644 --- a/week10/my-app/src/api/post.js +++ b/week10/my-app/src/api/post.js @@ -1,4 +1,5 @@ import client from "."; +import axios from "axios"; export const CreateNewPostApi = async (request, image) => { try { @@ -13,24 +14,39 @@ export const CreateNewPostApi = async (request, image) => { "Content-Type": "multipart/form-data", }, }); - console.log(res); + // console.log(res); } catch (err) { console.log(err); } }; -export const GetPostApi = async () => { +export const GetPostsApi = async () => { try { - const res = await client.get("/posts", { - postId, - nickname, - title, - content, - image, - createDate, - modifiedDate, - }); - console.log(res); + const res = await client.get("/posts"); + // console.log(res.data); + return res.data; + } catch (err) { + console.log(err); + } +}; + +export const DeletePostApi = async (postId) => { + try { + await client.delete(`${process.env.REACT_APP_SERVER_URL}/posts/${postId}`); + } catch (err) { + console.log(err); + } +}; + +export const GetPostDetailApi = async (postId) => { + try { + if (postId) { + const res = await client.get( + `${process.env.REACT_APP_SERVER_URL}/posts/${postId}` + ); + // console.log(res.data); + return res.data; + } } catch (err) { console.log(err); } diff --git a/week10/my-app/src/pages/Detail.jsx b/week10/my-app/src/pages/Detail.jsx index e69de29bb..481d96bbc 100644 --- a/week10/my-app/src/pages/Detail.jsx +++ b/week10/my-app/src/pages/Detail.jsx @@ -0,0 +1,290 @@ +import { useEffect, useState } from "react"; +import { useNavigate, useParams } from "react-router-dom"; +import { GetPostDetailApi, DeletePostApi } from "../api/post"; +import { + DeleteHeartApi, + GetHeartApi, + PostHeartApi, + FindIfLiked, +} from "../api/heart"; +import { FaHeart } from "react-icons/fa"; +import { + PostCommentApi, + GetCommentApi, + DeleteCommentApi, +} from "../api/comment"; +import styled from "styled-components"; + +const PostContainer = styled.div` + padding: 20px; + border: 1px solid #ddd; + border-radius: 8px; + margin: 20px; +`; + +const PostHeader = styled.div` + display: flex; + align-items: center; + justify-content: space-between; +`; + +const PostTitle = styled.h2` + margin: 0; +`; + +const HeartIcon = styled(FaHeart)` + cursor: pointer; + color: ${(props) => (props.postLiked ? "red" : "gray")}; + font-size: 24px; +`; + +const HeartCount = styled.span` + font-size: 18px; + margin-left: 8px; +`; + +const PostInfo = styled.div` + margin-top: 20px; +`; + +const PostAuthor = styled.p` + font-weight: bold; +`; + +const PostContent = styled.p` + margin: 10px 0; +`; + +const PostImage = styled.img` + max-width: 100%; + height: auto; + margin-top: 10px; +`; + +const PostDates = styled.div` + display: flex; + justify-content: space-between; + margin-top: 10px; +`; + +const Date = styled.span` + font-size: 12px; + color: #888; +`; + +const CommentSection = styled.div` + margin-top: 20px; +`; + +const Comment = styled.div` + display: flex; + align-items: center; + justify-content: space-between; + border-bottom: 1px solid #ddd; + padding: 10px 0; +`; + +const CommentText = styled.p` + margin-left: 20px; +`; + +const DeleteButton = styled.button` + background: none; + border: none; + color: red; + cursor: pointer; +`; + +const NoComments = styled.p` + color: #888; + margin-left: 20px; +`; + +const CommentInput = styled.input` + margin-left: 20px; + width: calc(100% - 100px); + padding: 8px; + margin-right: 10px; + border: 1px solid #ddd; + border-radius: 4px; +`; + +const SubmitButton = styled.button` + margin-left: 20px; + margin-top: 20px; + padding: 8px 16px; + background-color: #007bff; + color: white; + border: none; + border-radius: 4px; + cursor: pointer; + &:hover { + background-color: #0056b3; + } +`; + +const DetailPage = ({ posts, setPosts }) => { + const navigate = useNavigate(); + const { postId } = useParams(); + const [postDetail, setPostDetail] = useState(null); + const [comments, setComments] = useState([]); + const [newComment, setNewComment] = useState(""); + const [heart, setHeart] = useState(0); + const [postLiked, setPostLiked] = useState(); + + async function getPostDetail() { + try { + setPostDetail(null); + const data = await GetPostDetailApi(postId); + setPostDetail(data); + } catch (err) { + console.log(err); + } + } + + async function deletePost(postId) { + try { + await DeletePostApi(postId); + setPosts(posts.filter((post) => post.postId !== postId)); + navigate("/posts"); + } catch (err) { + console.log(err); + } + } + + async function fetchComments(postId) { + try { + const data = await GetCommentApi(postId); + setComments(data); + } catch (err) { + console.log(err); + } + } + + async function handleCommentSubmit() { + try { + await PostCommentApi(postId, newComment); + setNewComment(""); + fetchComments(postId); + } catch (err) { + console.log(err); + } + } + + async function handleDeleteComment(commentId) { + try { + await DeleteCommentApi(commentId); + setComments( + comments.filter((comment) => comment.commentId !== commentId) + ); + } catch (err) { + console.log(err); + } + } + + async function GetLikePost() { + /*좋아요 반환*/ + try { + const data = await GetHeartApi(postId); + setHeart(data); + } catch (err) { + console.log(err); + } + } + + async function PostLikePost() { + /*좋아요 누르기*/ + if (!postLiked) { + try { + await PostHeartApi(postId); + GetLikePost(); + setPostLiked(true); + getPostDetail(); + } catch (err) { + console.log(err); + } + } else { + await DeleteLikePost(postId); + setPostLiked(false); + GetLikePost(); + getPostDetail(); + } + } + + async function DeleteLikePost(postId) { + /*좋아요 취소*/ + try { + await DeleteHeartApi(postId); + getPostDetail(); + } catch (err) { + console.log(err); + } + } + + useEffect(() => { + GetLikePost(); + try { + FindIfLiked(postId).then((liked) => { + setPostLiked(liked); + }); + } catch (err) { + console.log(err); + } + }, []); + + useEffect(() => { + getPostDetail(); + fetchComments(postId); + GetLikePost(); + }, [postId]); + + if (!postDetail) return

Loading...

; + + return ( + <> + + + {postDetail.title} + + {heart} + + + {postDetail.nickname} + {postDetail.content} + {postDetail.image && ( + + )} + + {postDetail.createdDate} + {postDetail.modifiedDate} + + + + + {comments.length > 0 ? ( + comments.map((comment) => ( + + {comment.content} + handleDeleteComment(comment.commentId)} + > + 삭제 + + + )) + ) : ( + 댓글이 없습니다. + )} + setNewComment(e.target.value)} + /> + 댓글 작성 + + + ); +}; + +export default DetailPage; diff --git a/week10/my-app/src/pages/Heart.jsx b/week10/my-app/src/pages/Heart.jsx index e69de29bb..5c0f318d2 100644 --- a/week10/my-app/src/pages/Heart.jsx +++ b/week10/my-app/src/pages/Heart.jsx @@ -0,0 +1,45 @@ +// import { useEffect, useState } from "react"; +// import { useNavigate, useParams } from "react-router-dom"; + +// const PostListItemContainer = styled.div` +// display: flex; +// flex-direction: column; +// gap: 1em; +// `; + +// const PostListItem = styled.div` +// border: 1px solid #ccc; +// padding: 1em; +// border-radius: 5px; +// cursor: pointer; +// `; + +// const PostImage = styled.img` +// max-width: 100%; +// height: auto; +// `; + +// const HeartPage = ({posts, setPosts}) => { +// const navigate = useNavigate(); + +// if (!posts) return

Loading...

; +// return ( +// +// {posts.map((post) => ( +// navigate(`/posts/${post.postId}`)} +// > +//

제목: {post.title}

+//

닉네임: {post.nickname}

+//

내용: {post.content}

+// +//

{post.createdDate}

+//

{post.modifiedDate}

+//
+// ))} +//
+// ); +// }; + +// export default HeartPage; diff --git a/week10/my-app/src/pages/List.jsx b/week10/my-app/src/pages/List.jsx index be5dd7003..abe09aa60 100644 --- a/week10/my-app/src/pages/List.jsx +++ b/week10/my-app/src/pages/List.jsx @@ -1,4 +1,77 @@ -import { GetPostApi } from "../api/post"; -const ListPage = () => { +import { useNavigate } from "react-router-dom"; +import styled from "styled-components"; + +const PostListItemContainer = styled.div` + display: flex; + flex-direction: column; + gap: 1em; + padding: 20px; +`; +const formatDate = (datetime) => { + const date = new Date(datetime); + return date.toLocaleDateString(); +}; + +const PostListItem = styled.div` + border: 1px solid #ccc; + padding: 1em; + border-radius: 5px; + cursor: pointer; + transition: background-color 0.3s ease; + &:hover { + background-color: #f0f0f0; + } +`; + +const PostImage = styled.img` + max-width: 00px; + height: auto; + border-radius: 5px; +`; + +const PostTitle = styled.p` + font-size: 1.2em; + font-weight: bold; + margin: 0.5em 0; +`; + +const PostNickname = styled.p` + color: #555; + margin: 0.5em 0; +`; + +const PostContent = styled.p` + color: #333; + margin: 0.5em 0; +`; + +const PostDate = styled.p` + font-size: 0.8em; + color: #888; + margin: 0.5em 0; +`; + +const ListPage = ({ posts, setPosts }) => { const navigate = useNavigate(); + + if (!posts) return

Loading...

; + + return ( + + {posts.map((post) => ( + navigate(`/posts/${post.postId}`)} + > + 제목: {post.title} + 닉네임: {post.nickname} + 내용: {post.content} + {post.image && } + {formatDate(post.createdDate)} + + ))} + + ); }; + +export default ListPage; diff --git a/week10/my-app/src/pages/Register.jsx b/week10/my-app/src/pages/Register.jsx index 4e773a723..b757c8c6f 100644 --- a/week10/my-app/src/pages/Register.jsx +++ b/week10/my-app/src/pages/Register.jsx @@ -1,7 +1,8 @@ import { useState } from "react"; import { useNavigate } from "react-router-dom"; -import { postRegister } from "../api/user"; -import { postLogin } from "../api/user"; +import { postRegister, postLogin } from "../api/user"; +import styled from "styled-components"; + const RegisterPage = () => { const navigate = useNavigate(); const [signup, setSignup] = useState({ id: "", pw: "", nickname: "" }); @@ -11,6 +12,7 @@ const RegisterPage = () => { const { name, value } = e.target; setSignup({ ...signup, [name]: value }); }; + const Signup = async (e) => { e.preventDefault(); const { id, pw, nickname } = signup; @@ -22,6 +24,7 @@ const RegisterPage = () => { alert(res.response.data.message); } }; + const onChangeSignin = (e) => { const { name, value } = e.target; setSignin({ ...signin, [name]: value }); @@ -30,63 +33,121 @@ const RegisterPage = () => { const Signin = async (e) => { e.preventDefault(); const { id, pw } = signin; - console.log(signin); const res = await postLogin(id, pw); if (res.status === 201) { alert(`로그인 성공!${res.data.username}님 환영합니다.`); navigate("/"); window.location.reload(); } else if (res.response.status === 400) { - alert(`아이디와 비밀번호가 일치하지 않습니다.`); + alert("아이디와 비밀번호가 일치하지 않습니다."); } }; + return ( - <> -

로그인/회원가입 페이지

-

회원가입

-
- 아이디 - - 비밀번호 - - 닉네임 - - -
-

로그인

-
- 아이디 - - 비밀번호 - - -
- + + 로그인/회원가입 페이지 +
+ 회원가입 +
+ + + + + + + +
+
+
+ 로그인 +
+ + + + + +
+
+
); }; + export default RegisterPage; + +const Container = styled.div` + display: flex; + flex-direction: column; + gap: 2em; + padding: 20px; + max-width: 400px; + margin: auto; +`; + +const Title = styled.h1` + text-align: center; +`; + +const Section = styled.div` + display: flex; + flex-direction: column; + gap: 1em; +`; + +const Subtitle = styled.h2` + text-align: center; +`; + +const Form = styled.form` + display: flex; + flex-direction: column; + gap: 0.5em; +`; + +const Label = styled.label` + font-weight: bold; +`; + +const Input = styled.input` + padding: 10px; + border: 1px solid #ddd; + border-radius: 4px; + font-size: 16px; +`; + +const Button = styled.button` + padding: 10px 20px; + background-color: #007bff; + color: white; + border: none; + border-radius: 4px; + font-size: 16px; + cursor: pointer; + &:hover { + background-color: #0056b3; + } +`; diff --git a/week10/my-app/src/pages/Write.jsx b/week10/my-app/src/pages/Write.jsx index 47f63122b..924620e52 100644 --- a/week10/my-app/src/pages/Write.jsx +++ b/week10/my-app/src/pages/Write.jsx @@ -1,5 +1,6 @@ import { CreateNewPostApi } from "../api/post"; import { useState } from "react"; +import styled from "styled-components"; const CreatePage = () => { const [title, setTitle] = useState(""); @@ -9,6 +10,7 @@ const CreatePage = () => { const onUploadImage = (e) => { setFile(e.target.files[0]); }; + const onUploadImageButtonClick = () => { const request = { title: title, @@ -16,26 +18,63 @@ const CreatePage = () => { }; CreateNewPostApi(request, file); }; + return ( -
- + setTitle(e.target.value)} /> - setContent(e.target.value)} /> - - -
+ + 업로드 + ); }; export default CreatePage; + +const CreatePostContainer = styled.div` + margin: 100px auto; + display: flex; + flex-direction: column; + gap: 1em; + padding: 20px; + border: 1px solid #ccc; + border-radius: 8px; + max-width: 400px; +`; + +const Input = styled.input` + padding: 10px; + border: 1px solid #ddd; + border-radius: 4px; + + font-size: 16px; +`; + +const FileInput = styled.input` + padding: 10px; + border: 1px solid #ddd; + border-radius: 4px; +`; + +const UploadButton = styled.button` + padding: 10px 20px; + background-color: #007bff; + color: white; + border: none; + border-radius: 4px; + font-size: 16px; + cursor: pointer; + &:hover { + background-color: #0056b3; + } +`;