[๐Ÿ’ป ์ฝ”๋“œ์Šคํ…Œ์ด์ธ  FE 44๊ธฐ] React ์‹ฌํ™” - [๊ณผ์ œ]React Hooks ์ ์šฉํ•˜๊ธฐ

JiEunยท2023๋…„ 5์›” 23์ผ
0
post-thumbnail

โœ”๏ธ ์‹œ์ž‘

๊ทธ๋™์•ˆ ๋ฐฐ์› ๋˜ Hook์„ ์ด์šฉํ•ด ๊ณผ์ œ๋ฅผ ํ‘ธ๋Š” ํ˜•์‹์˜ ๊ณผ์ œ๋ฅผ ์ง„ํ–‰ํ–ˆ๋‹ค.


โœ๏ธ ์•Œ๊ฒŒ๋œ ์ 

json-server ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ

  • json ํŒŒ์ผ์„ ์ด์šฉํ•ด REST API ์„œ๋ฒ„๋ฅผ ๊ตฌ์ถ•ํ•ด ์ฃผ๋Š” ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์ด๋‹ค.
//์„ค์น˜ ๋ฐฉ๋ฒ•
npm i -g json-server
  • ์ „์—ญ์— ์„ค์น˜ํ•ด ์ค€๋‹ค.
  • React ์•ฑ ํŒŒ์ผ ๋‚ด์— ์„ค์น˜ํ•˜๋ฉด ์ œ๋Œ€๋กœ ์ž‘๋™ํ•˜์ง€ ์•Š์„ ์ˆ˜ ์žˆ๋‹ค.
//์„œ๋ฒ„ ์‹คํ–‰
cd data
json-server --watch data.json --port 3001
  • data ํด๋”๋กœ ์ด๋™ ๋’ค
  • --port 3001๋ผ๋Š” ์˜ต์…˜์„ ๋ถ™์—ฌ์ค˜ 3001๋ฒˆ ํฌํŠธ์— ์„œ๋ฒ„๋ฅผ ์—ด์–ด์ค€๋‹ค. (์˜ต์…˜์„ ์ฃผ์ง€ ์•Š์œผ๋ฉด ์ž๋™์œผ๋กœ 3000๋ฒˆ ํฌํŠธ๋กœ ์—ด์–ด์ฃผ๊ธฐ ๋•Œ๋ฌธ)

๊ด€๋ฆฌ์ž ๋ชจ๋“œ๋กœ ์‹คํ–‰ํ•˜๊ธฐ

sudo npm install

๊ทธ๋ƒฅ npm install์„ ํ•˜๋‹ˆ ๊ถŒํ•œ์ด ์—†์–ด ์˜ค๋ฅ˜๊ฐ€ ๋‚ฌ๋‹ค.
sudo๋ฅผ ์•ž์— ๋ถ™์—ฌ ์ฃผ๋ฉด ๊ด€๋ฆฌ ๊ถŒํ•œ์„ ์ค€๋‹ค๋Š” ๋œป์ด๋‹ค.

ํ”ํžˆ ์ปดํ“จํ„ฐ ํ”„๋กœ๊ทธ๋žจ์ด ์ž˜ ์•ˆ ๋  ๋•Œ ๊ด€๋ฆฌ์ž ๋ชจ๋“œ๋กœ ์‹คํ–‰ ํ•˜๊ธฐ์™€ ๋น„์Šทํ•œ ๋งฅ๋ฝ ๊ฐ™๋‹ค.

useParams()

  • React 16.8 ๋ฒ„์ „ ์ด์ƒ์—์„œ๋งŒ ๊ฐ€๋Šฅํ•˜๋‹ค.
  • React ์ปดํฌ๋„ŒํŠธ ๋‚ด์—์„œ URL์˜ ๋™์  ํŒŒ๋ผ๋ฏธํ„ฐ ๊ฐ’์„ ๊ฐ€์ ธ ์˜ฌ ์ˆ˜ ์žˆ๋‹ค.

http://localhost:3000/blogs/1์—์„œ
1์€ ๋™์  ํŒŒ๋ผ๋ฏธํ„ฐ์ด๋‹ค.

์‚ฌ์šฉ ์ „ npm install react-router-dom ์„ค์น˜๋ฅผ ๋จผ์ €ํ•ด ์ฃผ์–ด์•ผ ํ•œ๋‹ค.
์„ค์น˜ ํ›„

import { useParams } from "react-router-dom";
.
.
.
const id = useParams();
console.log(id) // { id : 1}

useParams()๋Š” ๊ฐ์ฒด์ธ String ํƒ€์ž…์„ ๋ฐ˜ํ™˜ํ•œ๋‹ค.
๊ทธ๋ž˜์„œ ๊ตฌ์กฐ ๋ถ„ํ•ด ํ• ๋‹น์œผ๋กœ ์ „๋‹ฌํ•  ์ˆ˜ ์žˆ๊ณ  ์ด๋ฅผ fetch๋ฅผ ์ด์šฉํ•ด ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค.

// ๊ตฌ์กฐ๋ถ„ํ•ด ํ• ๋‹น
const { id: blogId } = useParams()
// useFetch๋Š” Custom Hook์ด๋‹ค.
// ๋’ค์— ํŒŒ๋ผ๋ฏธํ„ฐ๋ฅผ ํ…œํ”Œ๋ฆฟ ๋ฆฌํ„ฐ๋Ÿด์„ ์ด์šฉํ•ด ๋ณ€๊ฒฝํ•ด ์ค„ ์ˆ˜ ์žˆ๋‹ค.
const [blog, isPending, error] = useFetch(`http://localhost:3001/blogs/${blogId}`)

useNavigate()

  • React Router v6์—์„œ ๋„์ž…๋˜์—ˆ๋‹ค.
  • ํŽ˜์ด์ง€ ์ „ํ™˜๊ด€ URL ๋ณ€๊ฒฝ์„ ์ฒ˜๋ฆฌํ•˜๋Š” ํ•จ์ˆ˜๋ฅผ ๋ฐ˜ํ™˜ํ•œ๋‹ค.
  • React ์ปดํฌ๋„ŒํŠธ์—์„œ ํ”„๋กœ๊ทธ๋ž˜๋ฐ ๋ฐฉ์‹์œผ๋กœ ํŽ˜์ด์ง€๋ฅผ ์ด๋™ํ•  ์ˆ˜ ์žˆ๋‹ค.

useParams()์™€ ๋งˆ์ฐฌ๊ฐ€์ง€๋กœ npm install react-router-dom ์„ค์น˜๋ฅผ ๋จผ์ €ํ•ด ์ฃผ์–ด์•ผ ํ•œ๋‹ค.

import { useNavigate } from "react-router-dom";
.
.
.
const navigate = useNavigate();

// ํ•ด๋‹น ๋ฒ„ํŠผ ํด๋ฆญ ์‹œ home์œผ๋กœ ์ด๋™ํ•œ๋‹ค.
const handleDeleteClick = () => {
  // ์ธ์ž๋กœ ์ด๋™ํ•  ๊ฒฝ๋กœ๋ฅผ ์ž‘์„ฑํ•ด ์ค€๋‹ค.
    navigate('/')
  }

navigate(1), navigate(-1)๋ฅผ ์ด์šฉํ•ด ๋‹ค์Œ ๊ฒฝ๋กœ, ์ด์ „ ๊ฒฝ๋กœ๋กœ ์ด๋™ํ•  ์ˆ˜ ์žˆ๋‹ค.(์ƒ๋Œ€ ๊ฒ…๋กœ)

  • navigate(1) : ํ˜„์žฌ ๊ฒฝ๋กœ์—์„œ ๋‹ค์Œ ๊ฒฝ๋กœ
  • navigate(-1) : ํ˜„์žฌ ๊ฒฝ๋กœ์—์„œ ์ด์ „ ๊ฒฝ๋กœ

๊ณผ์ œ

//BlogDetail.js
const { id: blogId } = useParams()
  const [blog, isPending, error] = useFetch(`http://localhost:3001/blogs/${blogId}`)
  const [isLike, setIsLike] = useState(false);
  const navigate = useNavigate()

  const handleDeleteClick = () => {
    fetch(`http://localhost:3001/blogs/${blogId}`, {
      method: 'DELETE'
    })
    .then(() => {
      navigate('/')
      window.location.reload();
    })
    .catch((err) => console.log(err))
    console.log('delete!');
  }

  const handleLikeClick = () => {
    let payload = {
      ...blog,
      likes: !isLike ? blog.likes + 1 : blog.likes - 1
    }
    fetch(`http://localhost:3001/blogs/${blogId}`, {
      method: 'PATCH',
      headers: {'Content-Type':'application/json'},
      body: JSON.stringify(payload)
    })
    .then(() => {
      setIsLike((prev) => !prev)
    })
    .catch(err => console.log(err))
  }
  .
  .
  .
   <button onClick={handleLikeClick}>
    {isLike ? "โค๏ธ" : "๐Ÿค"}
   </button>
//CreateBlog.js
const [title, setTitle] = useState('');
  const [body, setBody] = useState('');
  const [author, setAuthor] = useState('๊น€์ฝ”๋”ฉ');
  const navigate = useNavigate()

  const handleSubmit = (e) => {
    e.preventDefault();
    const payload = {
      title,
      body,
      author,
      likes: 0,
    };

    fetch(`http://localhost:3001/blogs`, {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify(payload),
    })
    .then(() => {
      navigate('/')
      window.location.reload();
    })
    .catch((err) => console.log(err))
    
    console.log(e.type);
  }
  • then์„ ์‚ฌ์šฉํ•ด์„œ ๋น„๋™๊ธฐ ์ฒ˜๋ฆฌ๋ฅผ ํ–ˆ๋‹ค.
  • window.location.reload()๋ฅผ ํ•ด์„œ ์ž๋™์œผ๋กœ ์ƒˆ๋กœ๊ณ ์นจ์ด ๋˜๋„๋ก ํ–ˆ๋‹ค.

๐Ÿ“ ๋งˆ์น˜๋ฉฐ

๊ณผ์ œ๋ฅผ ์ „๋ถ€ ์™„๋ฃŒํ•˜์ง€ ๋ชปํ•ด ์ž‘์„ฑํ•˜์ง€๋Š” ๋ชปํ–ˆ์ง€๋งŒ
์ด๋ฒˆ ๊ณผ์ œ๋ฅผ ํ†ตํ•ด ๋ชฐ๋ž๋˜ ๋ถ€๋ถ„๋„ ์•Œ๊ฒŒ ๋˜์—ˆ๊ณ 
์„œ๋ฒ„์— ์š”์ฒญ ๋ณด๋‚ด๋Š” ๋ถ€๋ถ„์€ ์•ˆ์ผํ•˜๊ฒŒ ์ƒ๊ฐํ–ˆ๋˜๊ฑฐ ๊ฐ™๋‹ค.

์ด๋ฒˆ ๊ธฐํšŒ์— ๊ทธ ๋ถ€๋ถ„์„ ๋ณด์ถฉํ•˜๊ฒŒ ๋œ ๊ณ„๊ธฐ๊ฐ€ ๋œ ๊ฑฐ ๊ฐ™๋‹ค.

profile
๐Ÿ’ป ํ”„๋ก ํŠธ์—”๋“œ๋ฅผ ๋ชฉํ‘œ๋กœ ์„ฑ์žฅ ์ค‘! (์•Œ์•„๋ดค๋˜ ๋‚ด์šฉ ๋“ฑ์„ ์ •๋ฆฌํ•˜๊ธฐ)

0๊ฐœ์˜ ๋Œ“๊ธ€