[React/JS/ํŒ€ํ”„์ ] ์ฆ๊ฒจ์ฐพ๊ธฐ ๊ธฐ๋Šฅ ๊ตฌํ˜„ํ•˜๊ธฐ

2024. 2. 29. 01:36ยท๐Ÿชด React
SMALL

0228

 

์ฆ๊ฒจ์ฐพ๊ธฐ (or ์ข‹์•„์š”) ๊ธฐ๋Šฅ์€ ๊ตฌํ˜„ํ•ด๋ณด๊ณ  ์‹ถ์—ˆ์ง€๋งŒ ํ•œ๋ฒˆ๋„ ์‹œ๋„ํ•ด๋ณธ ์ ์ด ์—†์—ˆ๋‹ค. ๋‹ค๋ฅธ ๋ถ„๋“ค์ด ํ•˜์‹  ๊ฑธ ๋ณด๋ฉด์„œ ์šฐ์™€ ์–ด๋–ป๊ฒŒ ํ•œ๊ฑฐ์ง€, ์ƒ๊ฐ๋งŒ ํ–ˆ๋˜ ๊ฑฐ ๊ฐ™๋‹ค. ๊ทผ๋ฐ ์ด๋ฒˆ ๊ธฐํšŒ์— ๊ตฌํ˜„ํ•ด๋ณผ ์ˆ˜ ์žˆ๊ฒŒ ๋ผ์„œ ๋„˜๋„˜ ๋ฟŒ๋“ฏํ•˜๊ณ  ์ข‹์€ ๊ฒฝํ—˜์ด์—ˆ๋‹ค !! ใ… ^ใ… 

 

์‚ฌ์‹ค ๋กœ๊ทธ์ธ/ํšŒ์›๊ฐ€์ž…์„ ๋งก์•„์ฃผ์‹  ํŒ€์›๋ถ„์ด session stroage์— ์œ ์ € uid๋ฅผ setํ•˜๊ณ , ๋‚ด๊ฐ€ ๊ทธ๊ฑธ getํ•ด์„œ ์“ฐ๋Š” ์‹์œผ๋กœ, firestore๋ฅผ ํ™œ์šฉํ•ด ๋ฐ์ดํ„ฐ ๋„ฃ๊ณ  ๊ฐ€์ ธ์˜ค๊ณ  ํ•˜๋ฉด ๋  ๊ฑฐ๋ผ๊ณ  ์ž˜ ์•Œ๋ ค์ฃผ์‹œ๊ณ , ํ•„๋“œ๊ฐ’๋„ ์–ด๋–ป๊ฒŒ ํ•  ๊ฑด์ง€ ์ž˜ ์ „๋‹ฌํ•ด์ฃผ์…”์„œ ์ •๋ง ์ˆ˜์›”ํ•˜๊ฒŒ ๋œ ๊ฒƒ ๊ฐ™๋‹ค. ์„ธ์…˜์Šคํ† ๋ฆฌ์ง€๋Š” ์ฒ˜์Œ ์จ๋ดค๋Š”๋ฐ, ๋กœ์ปฌ์Šคํ† ๋ฆฌ์ง€์™€ ์“ฐ๋Š” ๋ฐฉ๋ฒ•์ด ๋˜‘๊ฐ™์•˜๋‹ค. ๋˜ ํŒŒ์ด์–ด์Šคํ† ์–ด๋Š” ์ด์ „์— ํ™œ์šฉํ•œ ๊ฒฝํ—˜์ด ์žˆ์–ด์„œ, ๋‹ค์‹œ ๊ตฌ๊ธ€๋งํ•ด์„œ ์ž˜ ์จ๋จน์€ ๊ฑฐ ๊ฐ™๋‹ค. 

(๊ทผ๋ฐ ๋ฐฐ์—ด์— ์›์†Œ ์ถ”๊ฐ€, ์‚ญ์ œ ํ•˜๋Š” ๊ฑด arrayUnion arrayRemove ๋ผ๋Š” ๋ฉ”์„œ๋“œ๋ฅผ ๋”ฐ๋กœ import ํ•ด์„œ ์จ์•ผ๋˜๋Š” ๊ฑด ์ฒจ ์•Œ์•˜๋‹ค.. ๊ทธ๊ฒƒ๋„ ๋ชจ๋ฅด๊ณ  [... ์—ฐ์‚ฐ์ž ์จ์„œ ์‹œ๋„ํ•ด๋ณด๋‹ค๊ฐ€ ์™œ ์•ˆ๋˜์ง€, ํ–ˆ์Œ..!) 

 

๋˜, react query ๋„ ์ฒ˜์Œ์œผ๋กœ ์จ๋ณด๊ณ , useEffect ๋„ ์ด๋ฒˆ๊ธฐํšŒ์— ์ƒˆ์‚ผ ๋‹ค์‹œ ๊ณต๋ถ€ํ•˜๋Š” ๊ธฐํšŒ๊ฐ€ ๋๋‹ค. (์ด ํ›…์„ ์ž˜ ์จ๋จน๋Š”๊ฒŒ ์ค‘์š”ํ•œ๊ฑฐ๊ฐ™๋‹ค. ์˜์กด์„ฑ๋ฐฐ์—ด๋„ ์ค‘์š”..) ๊ทธ ๊ณผ์ •์—์„œ ์—๋Ÿฌ๋„ ๋งŽ์ด ๋งˆ์ฃผํ–ˆ์ง€๋งŒ, ๊ตฌ๊ธ€๋ง๋„ ํ•˜๊ณ  ๋งŽ์ด ๋ฐฐ์› ๋‹ค

 

๊ทธ๋ฆฌ๊ณ  ํ•ญ์ƒ undefined ์— ๋Œ€ํ•œ ์ฒ˜๋ฆฌ๋ฅผ ํ•ด์ฃผ๋Š” ๊ฒŒ ์ค‘์š”ํ•˜๋‹ค๋Š” ๊ฑธ ๊นจ๋‹ซ๊ณ  ์žˆ๊ธฐ๋„ ํ•˜๋‹ค. ๋งŽ์€ ์—๋Ÿฌ๋“ค์ด ์—ฌ๊ธฐ์„œ ์ƒ๊ฒจ๋‚˜๋Š” ๊ฑฐ ๊ฐ™๋‹ค. ๋ฌผ๋ก  undefined - ๊ฐ’์ด ์•ˆ๋“ค์–ด์˜ค๊ฑฐ๋‚˜ ํ•˜๋ฉด ์—๋Ÿฌ๊ฐ€ ๋‚˜๋Š” ๊ฒŒ ๋‹น์—ฐํ•˜๊ธด ํ•œ๋ฐ, ๊ทธ๋Ÿฐ ์˜๋ฏธ? ๋ง๊ณ ๋„, undefined ๋กœ ๊ฐ’์ด ๋“ค์–ด์˜ค๋Š” ๊ฒฝ์šฐ์— ๋Œ€ํ•œ ์ฒ˜๋ฆฌ!! ๊ฐ€ ์ •๋ง ์ค‘์š”ํ•˜๋‹ค๋Š” ๋А๋‚Œ์ด๋‹ค. 


..  const [userUid, setUserUid] = useState('');
  const isLogin = useSelector((state) => state.loginReducer);
..
  useEffect(() => {
    if (!data) return;

    if (sessionStorage.getItem('uid')) {
      setUserUid(sessionStorage.getItem('uid'));
    }
...
  }, [data, sortBy, isLogin]);
  
  ..
  return ( 
  
                    {userUid ? (
                    <ListFavoriteButton userUid={userUid} channelId={channel.channelId} />
                  ) : (
                    <NonFavStar src={nonFavImg} width={30} onClick={handleNonUserFavClick} />
                  )}
                  
               ..

 

์œ„์˜ cardList.jsx ์—์„œ  ( .. ์€ ์ƒ๋žต๋œ ๋ถ€๋ถ„)

์ „์—ญ์ƒํƒœ๊ด€๋ฆฌ๋œ isLogin ์€ useEffect ์˜์กด์„ฑ ๋ฐฐ์—ด์— ๋„ฃ์–ด์คฌ๋‹ค.

์„ธ์…˜์Šคํ† ๋ฆฌ์ง€์—์„œ uid๋ฅผ ๋ฐ›์•„์™€ useState set์„ ํ•ด์คฌ๋‹ค. 

๊ทผ๋ฐ ์ด uid๋ฅผ ์ „์—ญ ์ƒํƒœ ๊ด€๋ฆฌํ•ด์ฃผ๋Š”๊ฒŒ ์ข‹์„ ๊ฑฐ ๊ฐ™๋‹ค๊ณ  ์ƒ๊ฐํ•˜๊ธด ํ–ˆ๋Š”๋ฐ (์—ฌ๋ŸฌํŽ˜์ด์ง€์—์„œ ์‚ฌ์šฉํ•˜๋ฏ€๋กœ)

์‚ฌ์‹ค ๊ทธ๋ƒฅ ์„ธ์…˜์Šคํ† ๋ฆฌ์ง€์—์„œ ๋ฐ”๋กœ ๊บผ๋‚ด๋ฉด ๋ผ์„œ ๋ณ„ ๋ฌธ์ œ๋Š” ์—†๋Š” ๊ฑฐ ๊ฐ™๋‹ค.

 

๊ทธ๋ฆฌ๊ณ  ์ปดํฌ๋„ŒํŠธ ๋ฆฌํ„ด๋ฌธ ์•ˆ์—์„œ๋Š”, ๋ฐ›์•„์˜จ userUid์˜ ์กด์žฌ์—ฌ๋ถ€์— ๋”ฐ๋ผ, ์žˆ์œผ๋ฉด  ์ฆ๊ฒจ์ฐพ๊ธฐ(์ƒ‰์น ๋œ ๋ณ„์ด๋ฏธ์ง€/๋นˆ๋ณ„ ํ† ๊ธ€ ๋กœ ๋‚˜ํƒ€๋‚˜๋Š”) ์ปดํฌ๋„ŒํŠธ๋กœ, ๊ทธ๊ฒŒ ์•„๋‹ˆ๋ฉด (๋น„๋กœ๊ทธ์ธ -) empty ๋ณ„ ์ด๋ฏธ์ง€๋ฅผ ๋„์šฐ๋„๋ก ํ–ˆ๋‹ค. 

 


 

์ฆ๊ฒจ์ฐพ๊ธฐ ์ปดํฌ๋„ŒํŠธ ๋Š” ์•„๋ž˜์—!

๋กœ๊ทธ์ธ์ƒํƒœ์ธ ๊ฒฝ์šฐ์— ๋œจ๊ฒŒ ํ•จ

// ListFavoriteButton.jsx
import React, { useEffect, useState } from 'react';
import nonFavImg from '../../assets/emptyStar.png';
import favImg from '../../assets/coloredStar.png';
import styled from 'styled-components';
import { useQuery } from '@tanstack/react-query';
import { addFavoriteChannel, fetchIsFavorite, removeFavoriteChannel } from '../../api/favorites';
import Loading from '../layout/Loading';
import Error from '../../pages/Error';

export const ListFavoriteButton = ({ userUid, channelId }) => {
  const [favorite, setFavorite] = useState(false);

  // ๊ธฐ์กด ์ฆ๊ฒจ์ฐพ๊ธฐ ๋ฐ์ดํ„ฐ ๊ฐ€์ ธ์™€์„œ ๋ณ„ํ‘œ ๋œจ๊ฒŒํ•˜๊ธฐ => RQ
  const {
    data: favoriteChannels,
    isLoading,
    error
  } = useQuery({
    queryKey: ['favoriteChannels', userUid, favorite],
    queryFn: () => fetchIsFavorite(userUid)
  });

  useEffect(() => {
    favoriteChannels?.includes(channelId) ? setFavorite(true) : setFavorite(false);
  }, [favoriteChannels, favorite]);

  const toggleFavoriteClick = async () => {
    if (!favorite) {
      // ์ถ”๊ฐ€
      try {
        await addFavoriteChannel(userUid, channelId);
        setFavorite(true);
      } catch (error) {
        alert('์ฆ๊ฒจ์ฐพ๊ธฐ ์ถ”๊ฐ€๊ฐ€ ์ œ๋Œ€๋กœ ๋˜์ง€ ์•Š์•˜์–ด์š”. ๋‹ค์‹œ ์‹œ๋„ํ•ด์ฃผ์„ธ์š” !');
      }
    } else {
      // ์‚ญ์ œ(ํ•ด์ œ)
      try {
        await removeFavoriteChannel(userUid, channelId);
        setFavorite(false);
      } catch (error) {
        alert('์ฆ๊ฒจ์ฐพ๊ธฐ ์‚ญ์ œ๊ฐ€ ์ œ๋Œ€๋กœ ๋˜์ง€ ์•Š์•˜์–ด์š”. ๋‹ค์‹œ ์‹œ๋„ํ•ด์ฃผ์„ธ์š” !');
      }
    }
  };

  if (isLoading) return <Loading />;
  if (error) return <Error />;
  return (
    <div>
      {/* ์ฆ๊ฒจ์ฐพ๊ธฐ (๋ณ„) ๋ˆŒ๋ €์„ ๋•Œ (toggle - ์ฆ๊ฒจ์ฐพ๊ธฐ ์ถ”๊ฐ€/ํ•ด์ œ) */}
      <NonFavStar src={favorite ? favImg : nonFavImg} width={30} onClick={toggleFavoriteClick} />
    </div>
  );
};

const NonFavStar = styled.img`
  cursor: pointer;
`;

์ฆ๊ฒจ์ฐพ๊ธฐ ์ƒํƒœ๋Š” useState๋ฅผ ์‚ฌ์šฉํ–ˆ๋‹ค. ์‚ฌ์‹ค ์ด๊ฒŒ ์—ฌ๋Ÿฌ ํŽ˜์ด์ง€์—์„œ ์‚ฌ์šฉ๋  ๊ฑฐ๋ผ์„œ, ์ „์—ญ์ƒํƒœ๊ด€๋ฆฌ๋ฅผ ํ•ด์•ผํ•˜๋‚˜ ์ƒ๊ฐํ–ˆ๋Š”๋ฐ.. ๋‚ด๊ฐ€ ์ƒ๊ฐํ•œ๊ฒŒ ์ž˜๋ชป๋œ๊ฑด์ง„ ๋ชจ๋ฅด๊ฒ ์ง€๋งŒ.. ใ… ใ…  ์ „์—ญ ์ƒํƒœ๊ด€๋ฆฌ๋ฅผ ํ•ด๋ฒ„๋ฆฌ๋ฉด, ์ด๊ฒŒ ๋‹ค ํ†ต์šฉ?? ๋ผ์„œ, ํ•œ ๋ชฉ๋ก๋‹น ์ฆ์ฐพ์ด ์•„๋‹ˆ๋ผ ํ•˜๋‚˜๋ฅผ ๋ˆ„๋ฅด๋ฉด ๋‹ค๊ฐ™์ด ํ† ๊ธ€์ด ๋˜์–ด๋ฒ„๋ฆฌ๋Š” ์‹์ด๋ผ์„œ..   ์—ฌํŠผ ๊ฐ๊ฐ์˜ ์ƒํƒœ๊ฐ€ ์žˆ์–ด์•ผ ํ•œ๋‹ค๊ณ  ์ƒ๊ฐํ–ˆ๊ณ  ๊ทธ๋ž˜์„œ ํ•œ ์ปดํฌ๋„ŒํŠธ์— state๋ฅผ ํ•˜๋‚˜์”ฉ ์ฃผ๊ธฐ๋กœ ํ–ˆ๋‹ค. 

 

๋˜ ๋ฆฌ์•กํŠธ์ฟผ๋ฆฌ๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๊ณผ์ •์—์„œ , ์ฒ˜์Œ์—” ์ˆ˜์›”ํ•ด์„œ ์‰ฝ๋‹ค๊ณ  ์ƒ๊ฐํ–ˆ์ง€๋งŒ  ๋‹ค๋ฅธ๊ฒฝ์šฐ๋“ค์„ ๋งˆ์ฃผํ•˜๋ฉด์„œ ์—๋Ÿฌ๊ฐ€ ํŒกํŒกใ… ^ใ… 

์ˆ˜์ •ํ•˜๋ฉด์„œ ์• ๋จน์—ˆ์ง€๋งŒ ๋งŽ์ด ๋ฐฐ์šฐ๊ธฐ๋„ ํ–ˆ๋‹ค.

 

 

์ € ์œ„์—์„œ ์“ฐ์ธ ํŒŒ์ด์–ด์Šคํ† ์–ด DB ๊ด€๋ จ ํ•จ์ˆ˜๋“ค์€ ๋”ฐ๋กœ favorite.js์— ์žˆ๋‹ค ์•„๋ž˜์ฐธ๊ณ 

import { arrayRemove, arrayUnion, doc, getDoc, updateDoc } from 'firebase/firestore/lite';
import db from './config';

// ์ฆ๊ฒจ์ฐพ๊ธฐ ๋ชฉ๋ก DB์—์„œ ๊ฐ€์ ธ์˜ค๊ธฐ
export const fetchIsFavorite = async (userUid) => {
  if (!userUid) return '';
  try {
    const userRef = doc(db, 'users', userUid);
    const userSnap = await getDoc(userRef);
    if (userSnap.exists()) {
      return userSnap.data().favChannels;
    }
    return '';
  } catch (error) {
    console.log(`cannot fetch user's favorites from firestore : `, error);
  }
};

// ์ฆ๊ฒจ์ฐพ๊ธฐ ์ถ”๊ฐ€
export const addFavoriteChannel = async (userUid, channelId) => {
  try {
    await updateDoc(doc(db, 'users', userUid), {
      favChannels: arrayUnion(channelId)
    });
  } catch (error) {
    console.error('cannot add favorite to firestore: ', error);
  }
};

// ์ฆ๊ฒจ์ฐพ๊ธฐ ์‚ญ์ œ
export const removeFavoriteChannel = async (userUid, channelId) => {
  try {
    await updateDoc(doc(db, 'users', userUid), {
      favChannels: arrayRemove(channelId)
    });
  } catch (error) {
    console.error('cannot remove favorite from firestore: ', error);
  }
};

 

 

 

๊ทธ๋ฆฌ๊ณ  ์ƒ์„ธํŽ˜์ด์ง€์—์„œ๋„ ์ฆ์ฐพ์ด ์œ ์ง€๋˜๋„๋ก ํ•˜๊ณ , ๋˜‘๊ฐ™์ด ๋ฒ„ํŠผ์„ ๊ตฌํ˜„ํ–ˆ๋‹ค. 

ํŠนํžˆ ์ด๋•Œ๋ฆฌ์•กํŠธ ์ฟผ๋ฆฌ๋ฅผ ์“ฐ๊ณ  useEffect ๋ฅผ ์“ฐ๋ฉฐ ์—๋Ÿฌ๊ฐ€ ๋งŽ์•˜๋‹ค ..

 

๊ทผ๋ฐ ์•„๋งˆ๋„ (์•„๋ž˜์™€๊ฐ™์ด) ์œ ์ €๋กœ๊ทธ์ธ ์ƒํƒœ์— ๋Œ€ํ•œ ์ฒ˜๋ฆฌ๋ฅผ ์•ˆํ•ด์คฌ๋˜๊ฒŒ ์—๋Ÿฌ์˜ ํ™”๊ทผ์ด์—ˆ๋˜ ๊ฑฐ ๊ฐ™๋‹ค

  const [userUid, setUserUid] = useState('');
  const isLogin = useSelector((state) => state.loginReducer);

  useEffect(() => {
    if (sessionStorage.getItem('uid')) {
      setUserUid(sessionStorage.getItem('uid'));
    }
  }, [isLogin]);

 

SMALL

'๐Ÿชด React' ์นดํ…Œ๊ณ ๋ฆฌ์˜ ๋‹ค๋ฅธ ๊ธ€

[React] TanStack/React-Query ๋ฆฌ์•กํŠธ์ฟผ๋ฆฌ : ํ™”๋ฉด UI์™€ DB๋ฅผ ์‰ฝ๊ฒŒ ์—ฐ๋™ํ•˜๊ธฐ์œ„ํ•œ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ  (0) 2024.03.19
[React] ์—๋Ÿฌ : Element type is invalid: ... forgot to export your component from the file it's defined in, or you might have mixed up default and named imports. (+์Šคํƒ€์ผ์ปดํฌ๋„ŒํŠธ ๋„ค์ด๋ฐ ์‹ค์ˆ˜)  (0) 2024.02.29
[React/JS/ํŒ€ํ”„์ ] api๋กœ ๋ฐ›์€ ์ •๋ณด useState ๋ฐฐ์—ด์— ๋‹ด์•„ map ๋Œ๋ฆฌ๋ฉฐ ์ถœ๋ ฅํ•˜๊ธฐ  (0) 2024.02.27
[React/ํŒ€ํ”„์ ] Youtube API ์‚ฌ์šฉํ•˜๊ธฐ  (0) 2024.02.26
[React] React-Query(aka. Tanstack-Query)  (0) 2024.02.23
'๐Ÿชด React' ์นดํ…Œ๊ณ ๋ฆฌ์˜ ๋‹ค๋ฅธ ๊ธ€
  • [React] TanStack/React-Query ๋ฆฌ์•กํŠธ์ฟผ๋ฆฌ : ํ™”๋ฉด UI์™€ DB๋ฅผ ์‰ฝ๊ฒŒ ์—ฐ๋™ํ•˜๊ธฐ์œ„ํ•œ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ
  • [React] ์—๋Ÿฌ : Element type is invalid: ... forgot to export your component from the file it's defined in, or you might have mixed up default and named imports. (+์Šคํƒ€์ผ์ปดํฌ๋„ŒํŠธ ๋„ค์ด๋ฐ ์‹ค์ˆ˜)
  • [React/JS/ํŒ€ํ”„์ ] api๋กœ ๋ฐ›์€ ์ •๋ณด useState ๋ฐฐ์—ด์— ๋‹ด์•„ map ๋Œ๋ฆฌ๋ฉฐ ์ถœ๋ ฅํ•˜๊ธฐ
  • [React/ํŒ€ํ”„์ ] Youtube API ์‚ฌ์šฉํ•˜๊ธฐ
๋ จ๋””
๋ จ๋””
FE - ๊ฐœ๋ฐœ ๊ณต๋ถ€์˜ ๊ธฐ๋ก
  • ๋ จ๋””
    Ryeon's Devstory
    ๋ จ๋””
  • ์ „์ฒด
    ์˜ค๋Š˜
    ์–ด์ œ
    • ๋ถ„๋ฅ˜ ์ „์ฒด๋ณด๊ธฐ (69)
      • ๐Ÿชด JavaScript & TypeScript (11)
        • ๐Ÿงฉ Algorithm (6)
      • ๐Ÿชด React (12)
      • ๐Ÿชด Next.js (8)
      • ๐Ÿชด HTML & CSS (2)
      • ๐Ÿชด DB & BaaS (5)
      • ๐Ÿชด Git &Github (0)
      • โœ๏ธ ํ”„๋กœ์ ํŠธ ๊ณผ์ • & ํšŒ๊ณ  (18)
      • โœ๏ธ Today what I Learned _T.. (11)
      • ๐Ÿ•Š๏ธ IT ์•„ํ‹ฐํด (0)
      • etc. (2)
  • ๋ธ”๋กœ๊ทธ ๋ฉ”๋‰ด

    • ํ™ˆ
    • ํƒœ๊ทธ
    • ๋ฐฉ๋ช…๋ก
  • ๋งํฌ

    • My Github ๐Ÿ˜บ
    • ๋„ค์ด๋ฒ„ ๋ธ”๋กœ๊ทธ ๐ŸŽต๐ŸŽง (์ผ์ƒ,์Œ์•… ๋“ฑ)
  • ๊ณต์ง€์‚ฌํ•ญ

  • ์ธ๊ธฐ ๊ธ€

  • ํƒœ๊ทธ

    ์ปดํฌ๋„ŒํŠธ๋ถ„๋ฆฌ
    ํ‹ฐ์Šคํ† ๋ฆฌ๋ธ”๋กœ๊ทธ์Šคํ‚จ
    Supabase
    axios
    props
    ๋ Œ๋”๋งํŒจํ„ด
    Array.filter
    route handler
    ํ‹ฐ์Šคํ† ๋ฆฌ์ฑŒ๋ฆฐ์ง€
    ์—๋Ÿฌ
    fetch
    BAAS
    reacthook
    ์›น๊ฐœ๋ฐœ ๊ธฐ์ดˆ
    tailwind
    ํƒ€์ž…์Šคํฌ๋ฆฝํŠธ
    react
    ๋ฐฐ์—ด ๋ฉ”์„œ๋“œ
    State
    ์˜ค๋ธ”์™„
    Vite
    NVM
    useEffect
    ์˜์กด์„ฑ๋ฐฐ์—ด
    reactquery
    ๋ฆฌ์•กํŠธ์ฟผ๋ฆฌ
    Next.js
    json-server
    useMutation
    ๋น„๋™๊ธฐํ†ต์‹ 
  • ์ตœ๊ทผ ๋Œ“๊ธ€

  • ์ตœ๊ทผ ๊ธ€

  • hELLOยท Designed By์ •์ƒ์šฐ.v4.10.0
๋ จ๋””
[React/JS/ํŒ€ํ”„์ ] ์ฆ๊ฒจ์ฐพ๊ธฐ ๊ธฐ๋Šฅ ๊ตฌํ˜„ํ•˜๊ธฐ
์ƒ๋‹จ์œผ๋กœ

ํ‹ฐ์Šคํ† ๋ฆฌํˆด๋ฐ”