Redux-saga

chu·2021년 3월 19일
0

전 시간에는 redux-thunk라는 redux에서 비동기 작업을 할 경우에 쓰이는 미들웨어에서 정리하고, 작업했던 TodoApp에 setTimeout으로 간단하게 진행을 했다.

redux-thunk는 정말 사용하기는 너무 간단하고, 추가적으로 배워야 할 문법 또한 없다.

하지만 기능적인 부분이 한계가 있기 때문에, 그 부분을 더 채워줄 수 있는 redux-saga에 대해서 정리해보고, TodoApp에 적용까지 해보자.

redux-saga

redux에서 비동기 작업을 처리 할 경우 쓰이는 미들웨어다.
thunk에 비해 더 추가적인 기능이 있다.

첫번째

npm i redux-saga

store.jsx

store 설정

import { createStore, applyMiddleware } from 'redux';
import { composeWithDevTools } from 'redux-devtools-extension';
import logger from 'redux-logger';
import createSagaMiddleware from 'redux-saga';

import reducer from './reducers/index';
import rootSaga from './saga/index';

const sagaMiddleware = createSagaMiddleware();
const middleware = [logger, sagaMiddleware];

const store = createStore(
  reducer,
  composeWithDevTools(applyMiddleware(...middleware)),
);

// 루트 사가 실행
// store가 생성 된 다음에 코드를 실행해야 한다.
sagaMiddleware.run(rootSaga);

export default store;

actions/index.jsx

export const ADD_LIST_REQUEST = 'ADD_LIST_REQUEST';
export const ADD_LIST_SUCCESS = 'ADD_LIST_SUCCESS';
export const ADD_LIST_FAILURE = 'ADD_LIST_FAILURE';

// 나머지 ADD_LIST_SUCCESS, ADD_LIST_FAILURE는
// saga에서 호출해주기 때문에 요청만 action을 생성한다.
export const addTodoRequest = (payload) => {
  return {
    type: ADD_LIST_REQUEST,
    payload,
  };
};

saga/index.jsx

import { takeLatest, put, delay, fork } from 'redux-saga/effects';
import { 
  ADD_LIST_REQUEST,
  ADD_LIST_SUCCESS,
  ADD_LIST_FAILURE,
} from '../actions';

// action - 아래에서 전달받은 action에 대한 정보를 가지고 있다.
function* addList(action) {
  try {
    yield delay(2000); // 비동기 흉내를 위한 시간 지연 이펙트
    yield put({
      type: ADD_LIST_SUCCESS,
      payload: action.payload,
    });
  } catch (err) { // 테스트여서 실제 에러날 일은 없다..
    yield put({
      type: ADD_LIST_FAILURE,
      data: err,
    });
  }
}

// 두번째 인자 들어간 addList에는 함수를 호출하며, action에 대한 정보를 보낸다.
function* watchAddList() {
  yield takeLatest(ADD_LIST_REQUEST, addList);
}

export default function* rootSaga() {
  yield fork(watchAddList);
}

saga effects의 다양한 기능

take 일회용 이펙트 (한번 실행 후 지워버려서 실행할 수 없다.)
all 여러 이펙트를 동시에 실행할 경우
put dispatch와 동일하게 사용
delay 시간 지연 이펙트
takeEvery 일회용이 아닌 반복문처럼 계속 사용할 수 있다.
takeLatest 요청은 전부 가지만, 마지막 호출에 대해서만 응답한다.
takeLeading 맨 처음 호출만 실행한다.
throttle 정해진 시간내에 마지막만 요청을 보낸다.

더 많지만, 이 정도만 정리를 하도록 하겠다.
사실 이렇게 적어도 다 쓰진 않는 것 같다. 필요할 때 찾아보고 사용해보자!

components/AddList.jsx

import React, { useState, useCallback } from 'react';
import { useDispatch, useSelector } from 'react-redux';
import shortId from 'shortid';

import { AddInput, AddContainer, AddButton, Loading } from '../style';
import { addTodoRequest } from '../actions/index';

const AddList = () => {
  // loading 기능을 추가해보고자 불러온다.
  const loading = useSelector((state) => state.loading);
  const [value, setValue] = useState('');
  const dispatch = useDispatch();

  console.log(loading);

  const onChangeInput = useCallback(
    (e) => {
      setValue(e.target.value);
    },
    [value],
  );

  const onAddList = useCallback(() => {
    if (value === '') {
      return alert('내용을 입력해주세요.');
    }
    dispatch(
      addTodoRequest({ // 변경된 요청 부분
        id: shortId.generate(),
        title: value,
        done: false,
      }),
    );
    setValue('');
  }, [value]);

  return (
    <AddContainer>
      {loading ? (
        <Loading>
          <p>리스트 추가 로딩 중...</p>
        </Loading>
      ) : null}
      <AddInput type="text" value={value} onChange={onChangeInput} />
      <AddButton onClick={onAddList}>+</AddButton>
    </AddContainer>
  );
};

export default AddList;

그렇다면 실행!!

  1. 추가할 리스트를 작성한 뒤, 버튼을 눌러준다.
  2. loading이 true 상태일 땐 로딩창이 노출됀다. (딜레이 2초 설정)
  3. 리스트가 추가되면, loading은 false 상태로 변경되서 사라진다.

좀더 해상도가 좋은 GIF 캡쳐 앱이 있을까... 유료를 써야하나...

실제 API로 요청을 보내고, 받게되면 saga의 현재 패턴에서 문장이 더 추가됀다.

물론 API로 보내는 문장도 추가하게 되고, 응답을 받는 부분도 추가로 try catch문에도 넣어줘야한다.

복잡하게 느껴질 수 있겠지만 saga의 패턴을 사용하다보면 복사 붙혀넣기로 진행이 될 정도로 단순하다.

어떻게 요청을 보내고, 어떻게 데이터를 받아서 UI를 만들 것 인지 이제는 심화과정을 배워보고, 다른 프로젝트로 넘어갈 필요도 있을 것 같다.

profile
한 걸음 한걸음 / 현재는 알고리즘 공부 중!

0개의 댓글