brunch

You can make anything
by writing

C.S.Lewis

by 내가 사는 세상 Dec 13. 2023

Redux (Toolkit) - 2. 비동기 작업처리

#asyncThunk #extraReducers

 Redux에서 동기 작업을 처리할 때 reducer를 사용한다고 했다. 이번엔 비동기 작업처리이다. 이는 동기 작업을 처리할 때와 코드가 매우 비슷하다. 세팅하는 방법은 똑같고, 메인 로직인 '어쩌구Slice.js'에서의 코드만 살짝 다를 뿐이다.


목차

STEP1. 기본 설정 

  STEP1-1. 기초적인 설정 #기반 공사 // store.js

  STEP1-2. 추가적인 설정 #추가 공사 #구체적인 기능들 // postsSlice.js

STEP2. 프로젝트에 Redux Toolkit을 쓸 수 있는 분위기 만들기 #환경 설정 // index.js

STEP3. 실제로 사용하기 #at 원하는 컴포넌트 // MyComponent.js




STEP1. 기본 설정


STEP1-1. 기초적인 설정 #기반 공사



 우리는 state를 편하게 관리하고자 store를 만들고 그 속에 넣는다. 그리고 store는 특정 기능 단위로 구분지어진다. 구분지어진 각각을 '어쩌구slice'라는 부른다. 예를들어 계정 기능(authSlice.js), 계산기 기능(calculatorSlice.js) 포스팅 기능(postSlice.js) 등이 있을 것이다. 이번 글에선 '포스팅 조회'를 예제로 살펴볼 것이다. 일단 포스팅 slice를 store에 등록해주자.


// store.js

import { configureStore } from '@reduxjs/toolkit';

import postsReducer from './postsSlice';


const store = configureStore({

  reducer: {

    posts: postsReducer,

  },

});


export default store;




 STEP1-2. 추가적인 설정 #추가 공사 #구체적인 기능들


 이젠 구체적으로 비동기 작업(포스팅을 조회)을하는 로직을 postsSlice.js에 적어두자. 타 서버에서 포스팅을 가져오는 작업을 할 때의 구체적인 행동지침을 적는 것이다. 원하는 서버에서 데이터를 가져오는 작업은 네트워크 상황에 따라 얼마나 걸릴지 모르므로 비동기적으로 처리(Redux Toolkit의 asyncThunk 사용)하려는 목적이다. 물론 여기 postsSlice.js에 적힌 해당 지침은 곧바로 실행되는게 아니라, STEP3에서 원하는 컴포넌트에서 사용(useDispatch : dispatch())될 때 실행된다.


 비동기 작업은 extraReducers라는 곳에 등록한다. 해당 비동기 처리를 하면 다음의 다양한 과정을 거치(상태를 가지)게 된다. 각 단계에서 어떤 일을 해야할지 개발자가 코드로 작성할 수 있다.

pending : 작업을 진행 중

fulfilled : 작업을 성공하였을 때 : action.payload에 작업의 결과물(가져온 포스팅 내용)이 담겨있다

rejected : 작업을 실패하였을 때




// postsSlice.js

import { createSlice, createAsyncThunk } from '@reduxjs/toolkit';

import axios from 'axios';


// 비동기 작업을 처리하는 createAsyncThunk 생성

export const fetchPosts = createAsyncThunk('posts/fetchPosts', async () => {

    const response = await axios.get('https://jsonplaceholder.typicode.com/posts');

    return response.data;

});


// Redux Toolkit 슬라이스 생성

const postsSlice = createSlice({

    name: 'posts',  // slice명 설정


    initialState: {    // 초기 state(상태값) 설정

      items: [],

      status: 'idle',

      error: null,

    },


    reducers: {}, // 동기적 처리를 하는 곳 cf) https://brunch.co.kr/@i-intheworld/603

  

    extraReducers: (builder) => {

        builder

            .addCase(fetchPosts.pending, (state) => {

            // 1. 비동기 작업이 시작될 때의 액션

                state.status = 'loading';

            })

            .addCase(fetchPosts.fulfilled, (state, action) => {

              // 2. 비동기 작업이 성공적으로 완료된 경우의 액션

                state.status = 'succeeded';

                state.items = action.payload;  // 타 서버에서 가져온 내용을 통신소 DB에 저장

            })

            .addCase(fetchPosts.rejected, (state, action) => {

              // 3. 비동기 작업이 실패한 경우의 액션

                state.status = 'failed';

                state.error = action.error.message;

              });

        },

});


export default postsSlice.reducer;




STEP2. 프로젝트에 Redux Toolkit을 쓸 수 있는 분위기 만들기 #환경 설정


 이제 asyncThunk를 활용한 비동기 작업의 구체적인 내용을 postsSlice.js 적고, 이를 store.js에 잘 등록해 주었다. 지금까지의 과정은 state를 관리해주는 통신소 기지국 하나를 세운 것에 비유할 수 있다. 이제 통신소를 가동시켜보자. 이제 누구나 통신소와 소통할 수 있게 되었다. store를 리액트 프로젝트에서 전역적으로 사용(언제든지 원하면 useSelect를 통해 통신소가 가진 값을 읽(사용하)거나하거나  useDispatch를 통해 store의 값을 쓸(변경) 수도 있게 설정을 하는 것이다.


// index.js

import { Provider } from 'react-redux';

import { store } from './app/store';

ReactDom.render(

        <Provider store={store}>

            <MyCompoent />

        </Provider>

);




STEP3. 실제로 사용하기 #at 원하는 컴포넌트


 이제 통신소가 가동되고 있다. 이제 통신소와 양방향으로 상호작용이 가능해졌다.

useSelector를 통해 통신소에 있는 내용을 가져와서 사용할 수 있고,

useDispatch를 통해 postSlice.js에 적어둔 행동지침을 실행해서, 통신소에 특정 데이터를 업데이트도 할 수 있게 된 것이다. 



// MyComponent.js

import React, { useEffect } from 'react';

import { useSelector, useDispatch  } from 'react-redux';

import { fetchPosts } from './postsSlice';


const MyComponent = () => {

  const { items, status, error } = useSelector((state) => state.posts); // 통신소의 state 값 가져와서 사용

  const dispatch = useDispatch();


  useEffect(() => {

    dispatch(fetchPosts());  // 통신소의 state 값 업데이트

  }, [dispatch]);


// useEffect( async () => {

//     unwrap()이 Promise를 반환하기 때문에, 비동기 작업 속에서 순차적(동기적)으로도 처리가능

//       await dispatch(fetchPosts()).unwrap()

//  }, [dispatch]);


  if (status === 'loading') {

    return <div>Loading...</div>;

  }


  if (status === 'failed') {

    return <div>Error: {error}</div>;

  }


  return (

    <div>

      <h1>Posts</h1>

      <ul>

        {items.map((post) => (

          <li key={post.id}>{post.title}</li>

        ))}

      </ul>

    </div>

  );

};


export default MyComponent;




 지금까지의 Redux Toolkit에서의 state를 관리해주는 store관련 내용을 한장의 그림을 표현하면 다음과 같다. 





참고자료


공식문서 : https://redux-toolkit.js.org/api/createAsyncThunk

코딩애플 : React 리액트 기초부터 쇼핑몰 프로젝트까지!

생활코딩 : redux toolkit - thunk 를 이용해서 비동기 작업을 처리하는 방법


매거진의 이전글 CSS - 자주 실수 하는 것들
브런치는 최신 브라우저에 최적화 되어있습니다. IE chrome safari