React Hooks & Json-server

Taehye.on·2023년 5월 23일
0

코드스테이츠 44기

목록 보기
73/89
post-thumbnail

D-59

🔍 React Hooks 과제

메인페이지

게시글 작성 페이지


🔍 json-server 라이브러리

json-server 라이브러리는 직접 DB를 만들어 서버를 구축할 필요 없이 Json 파일을 이용해 REST API 서버를 구축해주는 라이브러리다.

npm i -g json-server
// 전역 설치해야 제대로 작동

그 다음 앱 내에 존재하는 data폴더로 이동한 뒤 아래 명령어 입력

cd data
json-server --watch data.json --port 3001

POSTMAN으로 GET 요청 보내기


📌 APP.js

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;

📌 BlogDetail.js

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;

📌 CreateBlog.js

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;

📌 useFetch.js

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 사용법을 복습하는 시간이 되었다 🧐

0개의 댓글