메인페이지
게시글 작성 페이지
json-server 라이브러리는 직접 DB를 만들어 서버를 구축할 필요 없이 Json 파일을 이용해 REST API 서버를 구축해주는 라이브러리다.
npm i -g json-server
// 전역 설치해야 제대로 작동
그 다음 앱 내에 존재하는 data폴더로 이동한 뒤 아래 명령어 입력
cd data
json-server --watch data.json --port 3001
POSTMAN으로 GET 요청 보내기
import {BrowserRouter, Routes, Route} from "react-router-dom";
import {useEffect, useState, Suspense, lazy} from "react";
import useFetch from "./util/useFetch";
const Home = lazy(() => import("./Home"));
const CreateBlog = lazy(() => import("./blogComponent/CreateBlog"));
const BlogDetails = lazy(() => import("./blogComponent/BlogDetail"));
const NotFound = lazy(() => import("./component/NotFound"));
const Navbar = lazy(() => import("./component/Navbar"));
const Footer = lazy(() => import("./component/Footer"));
const Loading = lazy(() => import("./component/Loading"));
/* react.lazy()와 suspense를 사용해 App 컴포넌트를 리팩토링 해보세요. */
function App() {
/* get 메소드를 통해 데이터를 받아오는 useEffect hook은 컴포넌트 내 여기저기 존재하고 있습니다. */
/* 해당 hook은 반복이 되는 부분이 있으므로 어떻게 custom hook으로 만들 수 있을지 고민해 봅시다. */
/* util 폴더 내에 존재하는 useFetch에 custom hook을 작성해 주세요. */
const [blogs, isPending, error] = useFetch("http://localhost:3001/blogs/");
return (
<BrowserRouter>
{error && <div>{error}</div>}
<Suspense fallback={<Loading />}>
<div className="app">
<Navbar />
<div className="content">
<Routes>
<Route exact path="/" element={<Home blogs={blogs} isPending={isPending} />} />
<Route path="/create" element={<CreateBlog />} />
<Route path="/blogs/:id" element={<BlogDetails />} />
<Route path="/blogs/:id" element={<NotFound />} />
</Routes>
</div>
<Footer />
</div>
</Suspense>
</BrowserRouter>
);
}
export default App;
import {useEffect, useState} from "react";
import {Navigate, useNavigate, useParams} from "react-router-dom";
const BlogDetails = () => {
const [blog, setBlogs] = useState(null);
const [isPending, setIsPending] = useState(true);
const [error, setError] = useState(null);
const [isLike, setIsLike] = useState(true);
const {id} = useParams();
const navigate = useNavigate();
/* 현재는 개별 블로그 내용으로 진입해도 내용이 보이지 않습니다. */
/* useParams와 id를 이용하여 개별 블로그의 내용이 보일 수 있게 해봅시다. */
useEffect(() => {
setTimeout(() => {
fetch("http://localhost:3001/blogs/" + `${id}`)
.then((res) => {
if (!res.ok) {
throw Error("could not fetch the data for that resource");
}
return res.json();
})
.then((data) => {
setIsPending(false);
setBlogs(data);
setError(null);
})
.catch((err) => {
setError(err.message);
});
}, 1000);
}, []);
const handleDeleteClick = () => {
/* delete 버튼을 누르면 다시 home으로 리다이렉트 되어야 합니다. */
/* useNavigate()를 이용하여 handleDeleteClick 로직을 작성해주세요. */
navigate("../Home");
console.log("delete!");
};
const handleLikeClick = () => {
/* 하트를 누르면 home에서 새로고침을 했을 때 숫자가 올라가야 합니다. */
/* isLike와 blog.likes를 이용하여 handleLikeClick의 로직을 작성해주세요. */
setIsLike(!isLike);
const patchData = {likes: blog.likes + 1};
fetch("http://localhost:3001/blogs/" + `${id}`, {
method: "PATCH",
headers: {"Content-type": "Application/json"},
body: JSON.stringify(patchData),
})
.then((res) => {
if (!res.ok) {
throw Error("could not fetch the data for that resource");
}
return res.json();
})
.then((data) => {
setIsPending(false);
setBlogs(data);
setError(null);
})
.catch((err) => {
setIsPending(false);
setError(err.message);
});
console.log("like!");
};
return (
<div className="blog-details">
{isPending && <div>Loading...</div>}
{error && <div>{error}</div>}
{blog && (
<article>
<h2>{blog.title}</h2>
<p>Written by {blog.author}</p>
<div>{blog.body}</div>
<button onClick={handleLikeClick}>
{isLike ? "❤️" : "🤍"}
{/* isLike에 의해 조건부 렌더링으로 빨간 하트(❤️)와 하얀 하트(🤍)가 번갈아 보여야 합니다. */}
</button>
<button onClick={handleDeleteClick}>delete</button>
</article>
)}
</div>
);
};
export default BlogDetails;
import {useState} from "react";
import {useNavigate} from "react-router-dom";
const CreateBlog = () => {
const [title, setTitle] = useState("");
const [body, setBody] = useState("");
const [author, setAuthor] = useState("김코딩");
const navigate = useNavigate();
const handleSubmit = (e) => {
e.preventDefault();
/* 등록 버튼을 누르면 게시물이 등록이 되며 home으로 리다이렉트 되어야 합니다. */
/* 작성한 내용과 useNavigate를 이용하여 handleSubmit의 로직을 작성해보세요. */
console.log(e.type);
const newData = {title, body, author, likes: 0};
fetch("http://localhost:3001/blogs/", {
method: "POST",
headers: {"Content-type": "Application/json"},
body: JSON.stringify(newData),
}).then((res) => {
if (!res.ok) {
throw Error("could not fetch the data for that resource");
}
return res.json();
});
navigate("../");
};
return (
<div className="create">
<h2>Add a New Blog</h2>
<form onSubmit={handleSubmit}>
<label>제목</label>
<input type="text" required value={title} onChange={(e) => setTitle(e.target.value)} placeholder="제목을 입력해주세요." />
<label>내용</label>
<textarea required value={body} onChange={(e) => setBody(e.target.value)} placeholder="내용을 입력해주세요."></textarea>
<label>작성자</label>
<select value={author} onChange={(e) => setAuthor(e.target.value)}>
<option value="kimcoding">김코딩</option>
<option value="parkhacker">박해커</option>
</select>
<button>등록</button>
</form>
</div>
);
};
export default CreateBlog;
import {useState, useEffect} from "react";
const useFetch = (url) => {
const [blog, setBlog] = useState(null);
const [isPending, setIsPending] = useState(true);
const [error, setError] = useState(null);
useEffect(() => {
setTimeout(() => {
fetch(url)
.then((res) => {
if (!res.ok) {
throw Error("could not fetch the data for that resource");
}
return res.json();
})
.then((data) => {
setIsPending(false);
setBlog(data);
setError(null);
})
.catch((err) => {
setIsPending(false);
setError(err.message);
});
}, 1000);
}, []);
/* useFetch 안의 중심 로직을 작성해주세요. */
return [blog, isPending, error]; /* return 문을 작성해주세요. */
};
export default useFetch;
아직 UseFetch를 제대로 사용하지 못했고 Advanced를 진행 못했다. 페어분과 같이 고민하면서 전에 블로깅한 fetch API 사용법을 복습하는 시간이 되었다 🧐