React심화 Redux Toolkit/json-server/비동기통신/Thunk

윱니·2023년 11월 28일

1. Redux Toolkit

1. 리덕스툴킷
쉽게 말하면 리덕스를 개량한 것.
코드를 더 적게, 그리고 리덕스를 더 편하게 쓰기 위한 기능들을 흡수해서 만든 것.

2. 툴킷 설치하기

yarn add react-redux @reduxjs/toolkit

=> 일반 리덕스는 Action Value, Action Creator를 별도로 생성해줘야 했다면 리덕스툴킷은 Action Value, Action Creator, Reducer가 하나로 합쳐졌다.

createSlice 사용!!

//createSlice API 뼈대

const counterSlice = createSlice({
	name: '', // 이 모듈의 이름
	initialState : {}, // 이 모듈의 초기상태 값
	reducers : {}, // 이 모듈의 Reducer 로직
})

2. json-server

1. json-server 정의
아주 간단한 DB와 API서버를 생성해주는 패키지 (임시로 사용할 mock data 생성)

2. json-server 설치하기

yarn add json-server

3. json-server 실행하기

yarn json-server --watch db.json --port 4000

3. 비동기 통신 - axios,fetch

1. Axios
node.js와 브라우저를 위한 Promise 기반 http 클라이언트
=> http를 이용해서 서버와 통신하기 위해 사용하는 패키지

  • 설치

    yarn add axios

2. GET
axios.get => 서버의 데이터를 조회할 때 사용

  • json-server에 있는 todos를 axios를 이용해서 fetching하고 useState를 통해서 관리하는 로직
// src/App.js

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

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

	// axios를 통해서 get 요청을 하는 함수를 생성
	// 비동기처리를 해야하므로 async/await 구문을 통해서 처리
  const fetchTodos = async () => {
    const { data } = await axios.get("http://localhost:4000/todos");
    setTodos(data); // 서버로부터 fetching한 데이터를 useState의 state로 set
  };
	
	// 생성한 함수를 컴포넌트가 mount 됐을 때 실행하기 위해 useEffect를 사용
  useEffect(() => {
		// effect 구문에 생성한 함수를 넣어 실행
    fetchTodos();
  }, []);

	// data fetching이 정상적으로 되었는지 콘솔을 통해 확인합니다.
  console.log(todos); 
  return <div>App</div>;
};

export default App;

3. POST
axios.post => 서버에 데이터를 추가할 때 사용

  • GET 코드예시에서 추가
// 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:3001/todos");
    setTodos(data);
  };

  const onSubmitHandler = async(todo) => {
		//1.  이때 todos는 [{투두하나}]임
    await axios.post("http://localhost:3001/todos", todo); // 이때 서버에 있는 todos도 [{투두하나}]임
		
		// 근데 여기서 서버 요청이 끝나고 서버는 [{투두가},{두개임}]
		
	
		setTodos([...todos, todo]) 2. <-- 만약 이게 없다면, go to useEffect
		//4. 새로고침해서 진짜 현재 서버 데이터를 받아오기전에 상태를 똑같이 동기시켜줌 
		//5. 어떻게보면 유저한테 서버에서 새로 받아온것처럼 속이는것
		
  };

  useEffect(() => {
    fetchTodos(); //3. 새로고침해서 여기를 다시 실행해줘야 서버값이 새로 들어옴 e.g) [{투두가},{두개임}]
  }, []);

  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}</div>
        ))}
      </div>
    </>
  );
};

export default App;

4. DELETE
axios.delete => 저장되어 있는 데이터를 삭제하고자 요청을 보낼 때

const onClickDeleteButtonHandler = (todoId) => {
    axios.delete(`http://localhost:3001/todos/${todoId}`);
  };
  
  //버튼부분
     <button
        type="button"
        onClick={() => onClickDeleteButtonHandler(todo.id)}
            >
              삭제하기
     </button>
  

5. PATCH
axios.patch => 어떤 데이터를 수정하고자 서버에 요청을 보낼 때

4. axios 심화 - instance와 interceptor

1. instance 만들기, baseURL 설정하기
src > axios > api.js

import axios from "axios";

// axios.create의 입력값으로 들어가는 객체는 configuration 객체
const instance = axios.create({
	baseURL: "http://localhost:4000",
});

export default instance;

App.jsx

import "./App.css";
import { useEffect } from "react";
import api from "./axios/api";

function App() {
  useEffect(() => {
    api
      .get("/cafe")
      .then((res) => {
        console.log("결과 => ", res.data);
      })
      .catch((err) => {
        console.log("오류가 발생하였습니다!");
      });
  }, []);

  return <div>axios 예제입니다.</div>;
}

export default App;

2. request, response에 적용해보기
요청을 보낼 때, 그리고 서버로부터 응답을 받을 때(실패할 때) 특정한 일을 수행해야 한다면?

src > axios > api.js

import axios from "axios";

const instance = axios.create({
  baseURL: "http://localhost:4000",
});

instance.interceptors.request.use(
  function (config) {
    // 요청을 보내기 전 수행
    console.log("인터셉트 요청 성공!");
    return config;
  },
  function (error) {
    // 오류 요청을 보내기 전 수행
    console.log("인터셉트 요청 오류!");
    return Promise.reject(error);
  }
);

instance.interceptors.response.use(
  function (response) {
    console.log("인터넵트 응답 받았어요!");
    // 정상 응답
    return response;
  },

  function (error) {
    console.log("인터셉트 응답을 받지 못했어요");
    return Promise.reject(error);
  }
);

export default instance;

=>요청과 응답 중간에 가로채서 어떠한 작업을 수행!!

5. Thunk

1. Redux 미들웨어
리덕스에서 dispatch를 하면 action이 리듀서로 전달되고, 리듀서는 새로운 state를 반환한다. 미들웨어를 사용하면 이 과정에서 우리가 하고싶은 작업들을 넣어서 할 수 있다.

2. thunk

  • 리덕스 thunk란, 리덕스에서 많이 사용하고 있는 미들웨어 중 하나
  • thunk를 사용하면 우리가 dispatch를 할 때 객체가 아닌 함수를 dispatch할 수 있게 해줌. dispatch(객체) -> dispatch(함수)
  • 리덕스 툴킷에서 Thunk함수를 생성할 때는 createAsyncThunk를 이용
  • createAsyncThunk()의 첫번째 자리에는 action value, 두번째에는 함수가 들어감
  • 두번째로 들어가는 함수에서 2개의 인자를 꺼내 사용할 수 있는데, 첫번째 인자는 컴포넌트에서 보내준 payload이고, 두번째 인자는 thunk에서 제공하는 여러가지 기능.
profile
코린이 탈출을 기원하는 코린이

0개의 댓글