강의

리액트 심화 주차 필수 개념 정리(2)

choijming21 2024. 9. 6. 17:40
  1. [1-2] 비동기1 - 비동기, promise
  2. [1-5] json-server
  3. [1-6 ~ 1-7] axios의 GET, POST, DELETE, PATCH, fetch와의 비교
  4. [1-10] tanstack query 기본 사용법 (매우중요)
  5. [1-15] zustand 기본 사용법
  6. [1-20] 인증인가 개념 실습
  7. [1-21] tailwind

 

 

 

[1-6 ~ 1-7] axio의 GET, POST, DELETE, PATCH, fetch와 비교 

axios란?

node.js 브라우저를 위한 Promise 기반 http 클라이언트라고 소개하고 있다. 즉, http를 이용해서 서버와 통신하기 위해 사용하는 패키지이다.

 

 

 

설치

yarn add axios

 

json-server 설정

{
  "todos": [
    {
      "id": "1",
      "title": "react"
    }
  ]
}

 

axios 기본 설정 (선택 사항)

const axiosInstance = axios.create({
  baseURL: 'https://api.example.com',
  timeout: 1000,
  headers: { 'X-Custom-Header': 'foobar' }
});

 

 

 

 

GET

get은 서버의 데이터를 조회할 때 사용한다. 

 

기본 형태

// url에는 서버의 url이 들어가고, config에는 기타 여러가지 설정을 추가할 수 있습니다.
// config는 axios 공식문서에서 확인하세요.

axios.get(url[, config]) // GET(url은 매개변수, 대괄호([]) 안의 값은 선택 입력사항입니다.

 

get 사용법

	const fetchTodos = async () => {
    	const { data } = await axios.get("http://localhost:4000/todos");
    	setTodos(data);
  		};

 

코드 예시

HTTP에서는 body에 javascript 객체를 direct로 넣을 수 없지만, axios는 내부적으로 JSON.stringify를 적용하기 때문에 편하게 적용할 수 있다.

import React, { useEffect, useState } from "react";
import axios from "axios"; // axios import 합니다.

const App = () => {
	const [todos, setTodos] = useState(null);

	const fetchTodos = async () => {
    	const { data } = await axios.get("http://localhost:4000/todos");
    	setTodos(data);
  		};
     
    useEffect(() => {
     	fetchTodos();
 		}, []);


  	console.log(todos);
  	
    
    return <div>App</div>;
};

export default App;

 

 

 

 

 

POST

post는 보통 서버에 데이터를 추가할 때 사용한다. 

 

기본 형태

axios.post(url[, data[, config]])   // POST

 

post 사용법

 const onSubmitHandler = async(todo) => {
    await axios.post("http://localhost:4000/todos", todo);
    
    setTodos([...todos, todo]);
  };

 

코드 예시

// src/App.jsx

import React, { useEffect, useState } from "react";
import axios from "axios"; // axios import 합니다.

const App = () => {
  // 새롭게 생성하는 todo를 관리하는 state
  const [todo, setTodo] = useState({
    title: "",
  });

  const [todos, setTodos] = useState(null);

  const fetchTodos = async () => {
    const { data } = await axios.get("http://localhost:4000/todos");
    setTodos(data);
  };

	// (원리를 꼭 이해합시다!)
	// HTTP에서는 body에 javascript 객체를 direct로 넣을 수 없어요!
	// axios는 내부적으로 JSON.stringify를 적용하기 때문에 이처럼 편리하게 사용하는 것 뿐입니다.
  const onSubmitHandler = async(todo) => {
    await axios.post("http://localhost:4000/todos", todo);
    
    setTodos([...todos, todo]);
  };
  
  // 만일 fetch를 사용했다면, 이렇게 JSON.stringify를 '직접' 해주어야 해요.
  // await fetch("http://localhost:4000/todos", {
  //   method: "POST",
  //   headers: {
  //     "Content-Type": "application/json",
  //   },
  //   body: JSON.stringify(todo),
  // });

  useEffect(() => {
    fetchTodos();
  }, []);

  return (
    <>
      <form
        onSubmit={(e) => {
					// 👇 submit했을 때 브라우저의 새로고침을 방지합니다. 
          e.preventDefault();
          onSubmitHandler(todo);
        }}
      >
        <input
          type="text"
          onChange={(ev) => {
            const { value } = ev.target;
            setTodo({
              ...todo,
              title: value,
            });
          }}
        />
        <button>추가하기</button>
      </form>
      <div>
        {todos?.map((todo) => (
          <div key={todo.id}>{todo.title}</div>
        ))}
      </div>
    </>
  );
};

export default App;

 

 

 

 

 

 

DELETE

delete는 저장되어 있는 데이터를 삭제하고자 요청을 보낼 때 사용한다.

 

기본 형태

axios.delete(url[, config])  // DELETE

 

delete 사용법

  const onClickDeleteButtonHandler = (todoId) => {
    axios.delete(`http://localhost:4000/todos/${todoId}`);
  };

 

 

코드 예시

// src/App.jsx

import React, { useEffect, useState } from "react";
import axios from "axios"; 

const App = () => {
  const [todo, setTodo] = useState({
    title: "",
  });

  const [todos, setTodos] = useState(null);

  const fetchTodos = async () => {
    const { data } = await axios.get("http://localhost:4000/todos");
    setTodos(data); 
  };

  const onSubmitHandler = (todo) => {
    axios.post("http://localhost:4000/todos", todo);
  };

	// 새롭게 추가한 삭제 버튼 이벤트 핸들러 
  const onClickDeleteButtonHandler = (todoId) => {
    axios.delete(`http://localhost:4000/todos/${todoId}`);
  };

  useEffect(() => {
    fetchTodos();
  }, []);

  return (
    <>
      <form
        onSubmit={(e) => {
          e.preventDefault();
          onSubmitHandler(todo);
        }}
      >
        <input
          type="text"
          onChange={(ev) => {
            const { value } = ev.target;
            setTodo({
              ...todo,
              title: value,
            });
          }}
        />
        <button>추가하기</button>
      </form>
      <div>
        {todos?.map((todo) => (
          <div key={todo.id}>
            {todo.title}
            <button
              type="button"
              onClick={() => onClickDeleteButtonHandler(todo.id)}
            >
              삭제하기
            </button>
          </div>
        ))}
      </div>
    </>
  );
};

export default App;

 

 

 

 

 

 

PATCH

patch는 보통 어떤 데이터에서 수정하고자 서버에 요청을 보낼 때 사용하는 메서드이다.

 

기본 형태

axios.patch(url[, data[, config]])  // PATCH

 

patch 사용법

  const onClickEditButtonHandler = (todoId, edit) => {
    axios.patch(`http://localhost:4000/todos/${todoId}`, edit);
  };

 

코드 예시

// src/App.jsx

import React, { useEffect, useState } from "react";
import axios from "axios";

const App = () => {
  const [todo, setTodo] = useState({
    title: "",
  });
  const [todos, setTodos] = useState(null);

  // patch에서 사용할 id, 수정값의 state를 추가
  const [targetId, setTargetId] = useState(null);
  const [editTodo, setEditTodo] = useState({
    title: "",
  });

  const fetchTodos = async () => {
    const { data } = await axios.get("http://localhost:4000/todos");
    setTodos(data);
  };

  const onSubmitHandler = (todo) => {
    axios.post("http://localhost:4000/todos", todo);
  };

  const onClickDeleteButtonHandler = (todoId) => {
    axios.delete(`http://localhost:4000/todos/${todoId}`);
  };

  // 수정버튼 이벤트 핸들러 추가 👇
  const onClickEditButtonHandler = (todoId, edit) => {
    axios.patch(`http://localhost:4000/todos/${todoId}`, edit);
  };

  useEffect(() => {
    fetchTodos();
  }, []);

  return (
    <>
      <form
        onSubmit={(e) => {
          e.preventDefault();
          onSubmitHandler(todo);
        }}
      >
        {/* 👇 수정기능에 필요한 id, 수정값 input2개와 수정하기 버튼을 추가 */}
        <div>
          <input
            type="text"
            placeholder="수정하고싶은 Todo ID"
            onChange={(ev) => {
              setTargetId(ev.target.value);
            }}
          />
          <input
            type="text"
            placeholder="수정값 입력"
            onChange={(ev) => {
              setEditTodo({
                ...editTodo,
                title: ev.target.value,
              });
            }}
          />
          <button
						// type='button' 을 추가해야 form의 영향에서 벗어남
            type="button"
            onClick={() => onClickEditButtonHandler(targetId, editTodo)}
          >
            수정하기
          </button>
        </div>
        <input
          type="text"
          onChange={(ev) => {
            const { value } = ev.target;
            setTodo({
              ...todo,
              title: value,
            });
          }}
        />
        <button>추가하기</button>
      </form>
      <div>
        {todos?.map((todo) => (
          <div key={todo.id}>
						{/* todo의 아이디를 화면에 표시 */}
            {todo.id} :{todo.title}
            <button
              type="button"
              onClick={() => onClickDeleteButtonHandler(todo.id)}
            >
              삭제하기
            </button>
          </div>
        ))}
      </div>
    </>
  );
};

export default App;

 

 

 

 

 

 

 

 

[1-10] tanstack query 기본 사용법 (매우중요) ⭐⭐⭐

 

tanstack query 란?

서버 상태를 관리하기 위한 라이브러리로, 데이터를 패칭하고 캐싱, 동기화, 무효화 등의 기능을 제공한다. 이를 사용하면 이전에 비해 훨씬 비동기 로직을 간편하게 작성하고 유지보수성을 높일 수 있다.👍

 

useQuery, useMutation, invalidateQueries 세가지는 꼭 알고 가자!!!!!!

 

 

주요 기능

  • 데이터 캐싱: 동일한 데이터를 여러번 요청하지 않도록 캐싱하여 성능을 향상시킴
  • 자동 리페칭: 데이터가 변경되었을 때 자동으로 리페칭(다시 데이터 가져오기)하여 최신 상태를 유지
  • 쿼리 무효화: 특정 이벤트가 발생했을 때 쿼리를 무효화하고 데이터를 다시 가져올 수 있음(개발자가 더 이상 이 데이터는 유효하지 않아! 선언하는 것)

 

사용 방법

 

설치

yarn add @tanstack/react-query

 

적용할 범위(ex: 전역)에 Provider를 이용하여 적용

    => App.jsx 또는 main.jsx(index.jsx)에 세팅하는 것을 권장!!

// main.jsx

import ReactDOM from "react-dom/client";
import App from "./App.jsx";
import "./index.css";
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";

const queryClient = new QueryClient();

ReactDOM.createRoot(document.getElementById("root")).render(
  <QueryClientProvider client={queryClient}>
    <App />
  </QueryClientProvider>
);

 

 

 

 

 

1. useQuery의 개념

데이터를 가져오기 위해 사용되는 Tanstack Quert의 대표적인 훅이다. 쿼리 키비동기 함수(패칭 함수)를 인자로 받아 데이터를 가져오고, 로딩 상태, 오류 상태, 그리고 데이터를 반환한다.

import { useQuery } from "@tanstack/react-query";
import axios from "axios";

const App = () => {
  const fetchTodos = async () => {
    const response = await axios.get("http://localhost:4000/todos");
    return response.data;
  };

  const {
    data: todos,
    isPending,
    isError,
  } = useQuery({
    queryKey: ["todos"],
    queryFn: fetchTodos,
  });

  if (isPending) {
    return <div>로딩중입니다...</div>;
  }

  if (isError) {
    return <div>데이터 조회 중 오류가 발생했습니다.</div>;
  }

  return (
    <div>
      <h3>TanStack Query</h3>
      <ul>
        {todos.map((todo) => {
          return (
            <li
              key={todo.id}
              style={{
                display: "flex",
                alignItems: "center",
                gap: "10px",
                backgroundColor: "aliceblue",
              }}
            >
              <h4>{todo.title}</h4>
              <p>{todo.isDone ? "Done" : "Not Done"}</p>
            </li>
          );
        })}
      </ul>
    </div>
  );
};

export default App;

 

 

 

 

2. useMutation

데이터를 생성, 수정, 삭제하는 등의 작업에 사용되는 훅이다. CUD에 대한 비동기 작업을 쉽게 수행하고, 성공 또는 실패 시에 추가적인 작업을 실행할 수 있기 때문에 useQuery와 함께 가장 대표적인 훅이라고 할 수 있다.

import { useMutation, useQuery } from "@tanstack/react-query";
import axios from "axios";
import { useState } from "react";

const App = () => {
  const [todoItem, setTodoItem] = useState("");

  const fetchTodos = async () => {
    const response = await axios.get("http://localhost:4000/todos");
    return response.data;
  };

  const addTodo = async (newTodo) => {
    await axios.post("http://localhost:4000/todos", newTodo);
  };

  const {
    data: todos,
    isPending,
    isError,
  } = useQuery({
    queryKey: ["todos"],
    queryFn: fetchTodos,
  });

  const { mutate } = useMutation({
    mutationFn: addTodo,
  });

  if (isPending) {
    return <div>로딩중입니다...</div>;
  }

  if (isError) {
    return <div>데이터 조회 중 오류가 발생했습니다.</div>;
  }

  return (
    <div>
      <h3>TanStack Query</h3>
      <form
        onSubmit={(e) => {
          e.preventDefault();

          const newTodoObj = { title: todoItem, isDone: false };

          // useMutation 로직 필요
          mutate(newTodoObj);
        }}
      >
        <input
          type="text"
          value={todoItem}
          onChange={(e) => setTodoItem(e.target.value)}
        />
        <button>추가</button>
      </form>
      <ul>
        {todos.map((todo) => {
          return (
            <li
              key={todo.id}
              style={{
                display: "flex",
                alignItems: "center",
                gap: "10px",
                backgroundColor: "aliceblue",
              }}
            >
              <h4>{todo.title}</h4>
              <p>{todo.isDone ? "Done" : "Not Done"}</p>
            </li>
          );
        })}
      </ul>
    </div>
  );
};

export default App;

 

 

 

 

3. invalidateQueries 

특정 쿼리를 무효화하여 데이터를 다시 패칭하게 하는 함수이다. useMutation과 함께 사용하여 데이터가 변경된 후 관련 쿼리를 다시 가져오도록 한다. 이를 통해 최신 상태로 유지될 수 있게 도와준다.

  const { mutate } = useMutation({
    mutationFn: addTodo,
    onSuccess: () => {
      // alert("데이터 삽입이 성공했습니다.");
      queryClient.invalidateQueries(["todos"]);
    },
  });

'강의' 카테고리의 다른 글

리액트 심화 주차 필수 개념 정리(1)  (0) 2024.09.06