[React] RTK Query

youngseo·2022년 8월 22일
0

REACT

목록 보기
48/52
post-thumbnail

RTK Query

RTK Query공식문서에 의하면 RTK Qurey는 다음과 같은 기능을 수행합니다.

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

동기

redux toolkit 에는 createAsyncThunk 를 사용해서, thunk 를 만들어서 async 요청을 관리할 수 있도록 하는 기능이 있습니다. 하지만 createAsyncThunk 의 경우 async 요청의 응답을 따로 createSlice 를 활용해서 관리해줘야 해서, 코드가 늘어날 수 밖에 없습니다.

이를 보완해 만들어진 것이 바로 RTK query로 createSlice 와 createAsyncThunk 의 기능을 동시에 사용하면서, react-query 와 유사하게 백엔드에서 넘어온 데이터를 관리할 수 있습니다.

특히 RTK Query 는 캐싱 관련 기능도 지원하기 때문에, 불필요한 요청을 줄일 수도 있으며 post요청 후 자동으로 get하는 기능을 구현할 수 있기에 보다 적은 코드로 필요한 로직을 작성할 수 있습니다.

뿐만 아니라, 프론트 데이터와 api 데이터 관리하는 로직 자체를 분리시킬 수도 있습니다.

백엔드와의 요청을 통해서 받아오는 데이터 → RTK Query 로 관리
프론트엔드만의 데이터 → createSlice 로 관리

사용 방법

$ npm i @reduxjs/toolkit react-redux

api 는 createApi 라는 함수를 사용해서 만들 수 있습니다. 해당 함수 안에 baseUrl과 관련한 요청을 모두 작성하는 방식입니다.

src > api > postApi.js

import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query/react';
 
export const api이름 = createApi({
  reducerPath: '리듀서이름',
  baseQuery: fetchBaseQuery({
    baseUrl: process.env.REACT_APP_API_URL,
  }),
  endpoints: (builder) => ({ 
    요청이름: builder.query({
      query: (주소에 넘길 값) => 'api 주소값/(주소에 넘길 값)',
    }),
  }),
});
 
export const {
  use요청이름Query
} = postApi;

endpoints 에 보내고자 하는 요청 이름을 명시하면, 자동으로 use요청이름Query 를 사용할 수 있게 됩니다.

요청을 명시할 때 builder.query 라고 작성하게 되면, get 요청을 보낼 수 있게 되고, builder.mutation 이라고 작성하면, post 혹은 put 요청 등을 보낼 수 있게 됩니다.

이렇게 생성된 api슬라이스를 컴포넌트에서 호출을 해주면 자동으로 isLoading, isError, data 를 반환해줍니다.

const { data: posts, isLoading, isError } = useGetPostsQuery();

RTK Qurey의 API

  • createApi(): RTK Query 기능의 코어.
    • 데이터를 패치하고 변환하는 설정을 포함해서 엔드포인트들에서 어떻게 데이터를 패치하는지 정의할 수 있습니다.
    • 대부분의 케이스에서는 베이스 URL당 하나의 API 슬라이스를 사용해야 합니다.
  • fetchBaseQuery() 간단한 요청을 위한 fetch의 래퍼.
    • 대부분의 사용자에게 createApi의 baseQuery로 권장합니다.

사용 예시

1. api슬라이스 생성

src> api > postApi.js

import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query/react';
 
export const postApi = createApi({
  reducerPath: 'postApi',
  baseQuery: fetchBaseQuery({
    baseUrl: process.env.REACT_APP_API_URL,
  }),
  endpoints: (builder) => ({
    getPosts: builder.query({
      query: () => 'posts',
    }),
  }),
});
 
export const {
  useGetPostsQuery
} = postApi;

2. 스토어 연결

store>index.js

import { configureStore } from '@reduxjs/toolkit';
import { postApi } from '../api/postApi';
import { setupListeners } from '@reduxjs/toolkit/query';
 
export const store = configureStore({
  reducer: {
    [postApi.reducerPath]: postApi.reducer,
  },
  middleware: (getDefaultMiddleware) =>
    getDefaultMiddleware().concat(postApi.middleware),
});

setupListeners(store.dispatch);

RTK Qurey의 API

  • setupListeners(): 서비스 내에서 사용자의 행동이나 요소의 변화 등을 지켜보다가, 요청을 보낼 수 있도록 listener 를 준비해놓기 위한 함수입니다.

3. 컴포넌트에서 사용

components/Posts.jsx

import React from 'react';
import { useGetPostsQuery } from '../api/postApi';
 
function Posts() => {
  const { data: posts, isLoading, isError } = useGetPostsQuery();

  if (isLoading) {
    return <div>로딩 중...</div>
  }
  if (isError || !posts) {
    return <div>오류 발생</div>;
  }
  return (
    <div>
        {posts.map((post) => (
            <>
            <p>{post.title}</p>
            <p>{post.body}</p>
            </>
        ))}
    </div>
  );
};
 
export default Posts;

4. app.js연결

import { Provider } from 'react-redux';
import Posts from './components/Posts';
import {store} from './store/index';

function App() {
  return (
    <Provider store={store}>
      <Posts/>
    </Provider>
  );
}

export default App;

RTK Qurey의 API

  • <ApiProvider />: Redux 스토어를 가지고 있지 않다면 Provider로 사용 가능합니다.

사용예시2

1. api슬라이스에 추가

api/postApi.js

import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query/react';
 
export const postApi = createApi({
  reducerPath: 'postApi',
  baseQuery: fetchBaseQuery({
    baseUrl: process.env.REACT_APP_API_URL,
  }),
  endpoints: (builder) => ({
    getPosts: builder.query({
      query: () => 'posts',
    }),
    getPostById: builder.query({
      query: (postId) => `posts/${postId}`,
    }),
    createPost: builder.mutation({
      query: ({ data }) => ({
        url: 'posts',
        method: 'POST',
        body: data,
      }),
    }),
  }),
});
 
export const {
  useGetPostsQuery,
  useGetPostByIdQuery,
  useCreatePostMutation,
} = postApi;

2. 컴포넌트에 사용

props를 통해 postId전달

components/Post.jsx

import React from 'react';
import { useGetPostByIdQuery } from '../api/postApi';
 
function Post({ postId }) {
  const { data: post, isLoading, isError } = useGetPostByIdQuery(postId);

  if (isLoading) {
    return <div>로딩 중...</div>
  }
  if (isError || !post) {
    return <div>Something went wrong</div>;
  }
  return (
    <div>
      <p>{post.title}</p>
      <p>{post.body}</p>
    </div>
  );
};
 
export default Post;

app.js

import { Provider } from 'react-redux';
import Post from './components/Post';
import Posts from './components/Posts';
import {store} from './store/index';

function App() {
  return (
    <Provider store={store}>
      <Posts/>
      <Post postId={1}/>
    </Provider>
  );
}

export default App;

post요청

components/PostInput.jsx

import React, { useState } from 'react';
import { useCreatePostMutation } from '../api/postApi';

function PostInput() {
  const [postInput, setPostInput] = useState({title: '', body: ''});
  const [createPost] = useCreatePostMutation();
 
  const inputChangeHandler = (e) => {
    const { name, value } = e.target
    setPostInput({...postInput, [name]:value});
  };
 
  const submitHandler = () => {
    createPost({
      data: postInput
    })
  };
 
  return (
    <div>
      <input name="title" onChange={inputChangeHandler} />
      <input name="body" onChange={inputChangeHandler} />
      <button onClick={submitHandler}>
        Save
      </button>
    </div>
  );
};
 
export default PostInput;

app.js

import { Provider } from 'react-redux';
import Post from './components/Post';
import PostInput from './components/PostInput';
import Posts from './components/Posts';
import {store} from './store/index';

function App() {
  return (
    <Provider store={store}>
      <PostInput/>
      <Posts/>
      <Post postId={1}/>
    </Provider>
  );
}

export default App;

3. Tag활용

Tag를 활용하면 각 요청별로 post후 자동으로 get요청을 하는 로직을 만들수 있습니다.

import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query/react';
 
export const postApi = createApi({
  reducerPath: 'postApi',
  tagTypes: ['Posts'],
  baseQuery: fetchBaseQuery({
    baseUrl: process.env.REACT_APP_API_URL,
  }),
  endpoints: (builder) => ({
    getPosts: builder.query({
      query: () => 'posts',
      providesTags: [{ type: 'Posts', id: 'LIST' }],
    }),
    getPostById: builder.query({
      query: (postId) => `posts/${postId}`,
      providesTags: (result, error, postId) => [{ type: 'Posts', id: postId }],
    }),
    createPost: builder.mutation({
      query: ({ data }) => ({
        url: 'posts',
        method: 'POST',
        body: data,
      }),
      invalidatesTags: (result) =>
        result
          ? [
              { type: 'Posts', id: 'LIST' },
            ]
          : [],
    }),
  }),
});
 
export const {
  useGetPostsQuery,
  useGetPostByIdQuery,
  useCreatePostMutation,
} = postApi;
  • reatePost 요청을 보내고, 거기에 대해서 result (응답) 가 있을때만 { type: 'Posts', id: 'LIST' } 라는 태그를 가진 요청인 getPosts 요청을 다시 보내게 됩니다.

0개의 댓글