[React/Dave Gray] React Redux Toolkit #6 Query Tutorial and RTK Query CRUD Example App

최예린·2022년 10월 15일
0

React

목록 보기
14/19
post-thumbnail

RTK Query

개요

RTK Query는 강력한 data fetching, caching 툴입니다. 웹 애플리케이션에서 데이터를 가져오는 단순한 상황을 간단하게 만들어서 data fetching과 caching 로직을 스스로 작성할 필요가 없도록 만들어졌습니다.

동기

  1. 스피너 UI의 로딩 상태관리
  2. 동일한 데이터에 대한 중복 요청 방지
  3. UI가 느리지 않도록 업데이트 최적화
  4. UI와 상호작용할 때 캐시 상태 관리

json server 설치하기

npm i json-server -g

json-server --watch data/db.json --port 3500

서버를 실행시키면 데이터를 보기시작하며 읽기 업데이트 및 삭제 기능을 지원합니다.

data/db.json

{
  "todos": [
    {
      "userId": 1,
      "id": 2,
      "title": "quis ut nam facilis et officia qui",
      "completed": true
    },
    {
      "userId": 1,
      "id": 6,
      "title": "qui ullam ratione quibusdam voluptatem quia omnis",
      "completed": false
    },
    {
      "userId": 1,
      "id": 8,
      "title": "quo adipisci enim quam ut ab",
      "completed": true
    },
    {
      ...

features/todos/TodoList.js 추가하기

사전에 미리 주어지는 TodoList 코드입니다.

App.js

import TodoList from "./features/todos/TodoList";

function App() {
  return <TodoList />
}

export default App;

TodoList를 반환합니다.

features/api/apiSlice.js 생성하기

import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query/react'

export const apiSlice = createApi({
    reducerPath: 'api', //default, 생략가능
    baseQuery: fetchBaseQuery({ baseUrl: 'http://localhost:3500' }),
    tagTypes: ['Todos'],
    endpoints: (builder) => ({
      	// 사용자 정의 Hook
        getTodos: builder.query({
            query: () => '/todos',
            transformResponse: res => res.sort((a, b) => b.id - a.id), // (1)
            providesTags: ['Todos'] // (2)
        }),
        addTodo: builder.mutation({ // (3)
            query: (todo) => ({
                url: '/todos',
                method: 'POST',
                body: todo
            }),
            invalidatesTags: ['Todos'] // (2)
        }),
        updateTodo: builder.mutation({ 
            query: (todo) => ({
                url: `/todos/${todo.id}`,
                method: 'PATCH',
                body: todo
            }),
            invalidatesTags: ['Todos']
        }),
        deleteTodo: builder.mutation({
            query: ({ id }) => ({ // (4)
                url: `/todos/${id}`,
                method: 'DELETE',
                body: id
            }),
            invalidatesTags: ['Todos']
        }),
    })
})

export const {
    useGetTodosQuery,
    useAddTodoMutation,
    useUpdateTodoMutation,
    useDeleteTodoMutation
} = apiSlice
  1. transformResponse: res => res.sort((a, b) => b.id - a.id),
    a - b 오름차순 정렬
    b - a 내림차순 정렬
    데이터를 추가하면 id는 1씩 증가하면서 부여되기때문에 최근에 추가한 데이터가
    제일 상단에 오도록 하려면 내림차순 정렬해야합니다.

  2. providesTags: ['Todos'],invalidatesTags: ['Todos']
    getTodos에 Todos 태그를 주고, invalidatesTags하면 getTodos를 무효화해줍니다. 이렇게 하면 캐시에서 다시 가져오거나 제거할 캐시된 데이터가 결정됩니다.
    이로써 얻을 수 있는 효과는 해당 쿼리가 실행되면 providesTags가 자동으로 패치되도록 해줍니다.

  1. : builder.mutation()
    데이터를 요청or쿼리하는게 아니라 실제로 데이터를 변경한다는 의미
  2. query: ({ id }) => ({
    삭제할 때는 id만 필요하기때문에 todo가 아니라 id만 있으면 된다.

index.js

import React from 'react';
import ReactDOM from 'react-dom/client';
import './index.css';
import App from './App';

import { ApiProvider } from "@reduxjs/toolkit/query/react";
import { apiSlice } from "./features/api/apiSlice";

ReactDOM.createRoot(document.getElementById('root'))
  .render(
    <React.StrictMode>
      <ApiProvider api={apiSlice}>
        <App />
      </ApiProvider>
    </React.StrictMode>
  );

ApiProvider
apiSlice

이 두가지를 import하고 App을 감싸줍니다.

features/todos/TodoList.js

import {
    useGetTodosQuery,
    useUpdateTodoMutation,
    useDeleteTodoMutation,
    useAddTodoMutation
} from "../api/apiSlice"
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
import { faTrash, faUpload } from '@fortawesome/free-solid-svg-icons'
import { useState } from "react"

const TodoList = () => {
    const [newTodo, setNewTodo] = useState('')

    const {
        data: todos,
        isLoading,
        isSuccess,
        isError,
        error
    } = useGetTodosQuery()
    const [addTodo] = useAddTodoMutation()
    const [updateTodo] = useUpdateTodoMutation()
    const [deleteTodo] = useDeleteTodoMutation()

    const handleSubmit = (e) => {
        e.preventDefault();
        addTodo({ userId: 1, title: newTodo, completed: false })
        setNewTodo('')
    }

    const newItemSection =
        <form onSubmit={handleSubmit}>
            <label htmlFor="new-todo">Enter a new todo item</label>
            <div className="new-todo">
                <input
                    type="text"
                    id="new-todo"
                    value={newTodo}
                    onChange={(e) => setNewTodo(e.target.value)}
                    placeholder="Enter new todo"
                />
            </div>
            <button className="submit">
                <FontAwesomeIcon icon={faUpload} />
            </button>
        </form>


    let content;
    if (isLoading) {
        content = <p>Loading...</p>
    } else if (isSuccess) {
        content = todos.map(todo => { //JSON.stringify(todos)
            return (
                <article key={todo.id}>
                    <div className="todo">
                        <input
                            type="checkbox"
                            checked={todo.completed}
                            id={todo.id}
                            onChange={() => updateTodo({ ...todo, completed: !todo.completed })}
                        />
                        <label htmlFor={todo.id}>{todo.title}</label>
                    </div>
                    <button className="trash" onClick={() => deleteTodo({ id: todo.id })}>
                        <FontAwesomeIcon icon={faTrash} />
                    </button>
                </article>
            )
        })
    } else if (isError) {
        content = <p>{error}</p>
    }

    return (
        <main>
            <h1>Todo List</h1>
            {newItemSection}
            {content}
        </main>
    )
}
export default TodoList

실행결과


todo를 입력하고 버튼을 누르면 리스트 상단에 가장 최근에 추가한 밥먹기가 추가됩니다.

http://localhost:3500/todos 에 가보면 todos에 밥먹기가 새로 추가된 걸 볼 수 있습니다.


Todo List에 밥먹기를 추가(POST)/완료처리(PATCH)/삭제(DELETE)하는 과정을 cmd창에서 보면 이렇습니다. 각각 요청을 보낸 뒤 GET으로 바로 변경한 내용을 반영해서 화면을 띄워줍니다.

profile
경북대학교 글로벌소프트웨어융합전공/미디어아트연계전공

0개의 댓글