[ Team Project ] Redux-Saga 간단 사용 가이드

June hyoung Park·2020년 12월 18일
0

React

목록 보기
18/19
post-thumbnail

❗️ 해당 게시물은 팀 프로젝트 팀원 배포용 문서 이며 해당 프로젝트를 기반으로 서술되었습니다.

리덕스 사가 간단 사용 가이드 📚

// * =======================
// * 리덕스 사가 간단 사용 가이드
// * =======================

import { call, put, takeEvery } from 'redux-saga/effects';
import * as FIIL_ME_IN from '../api/FIIL_ME_IN';
import { createRequestActionTypes } from '../api/createRequestSaga';
// 필요한 모듈을 불러와줍니다. 


const [
  SOME_TYPE,
  SUCCESS,
  FAILURE,
] = createRequestActionTypes('FIIL_ME_IN/FIIL_ME_IN');
/* 
먼저 사용할 액션 타입을 정해주세요. 리덕스 내에서 State를 업데이트할때는 항상
액션 타입을 지정해줘야합니다. 타입 작명은 자유이며, 총 세가지의 타입을 정의해야합니다.

SOME_TYPE : 기본적인 타입의 이름입니다.
SOME_SUCCESS : 타입이 성공적으로 수행됬을때 실행되는 타입입니다.
SOME_FAILUE : 타입 수행이 실패했을때 실행되는 타입입니다.
*/


/* EXAMPLE/////////////
const [
  LOAD_CARD,  
  SUCCESS,
  FAILURE,
] = createRequestActionTypes('userCard/LOAD_CARD');

위 예시의 LOAD_CARD는 Lobby단에서 다른 유저들의 정보를 받아와서 리듀서 상태를 
업데이트하는 작업을 수행하는 타입입니다.
아래의 SUCCESS와, FAILURE는 LOAD_CARD_SUCCESS와 LOAD_CARD_FAILUE와 동일합니다.
또한 createRequestActionTypes()의 인자로는 위의 예시와 같이 문자열로 넣어주시면 됩니다.

# 만일 타입이 LOGIN_USER라면, createRequestActionTypes()의 인자는 'loginUser/LOGIN_USER'가 될것입니다.

*/

// ==================================
// (1) GET 요청 시 
export const FIIL_ME_IN = () => ({
  type: SOME_TYPE,
});

// (2) POST 요청 시 
export const FIIL_ME_IN = (FIIL_ME_IN) => ({
  type: SOME_TYPE,
  payload: userData,
});


/*
이제 해당 타입이 발동될 수 있는 함수를 만들 차례입니다. 이 함수는 해당 프로젝트 기준
container에서 사용될것이며, dispatch()를 이용해서 발동시켜야합니다. 

// ===================
// 	EXAMPLE 
// ===================
 
 /* userCards.js
 
 export const loadUserList = (userData) => ({
  type: SOME_TYPE,
  payload: userData,
});

********************************

/* LobbyContainer.jsx

 const dispatch = useDispatch();
 dispatch(loadUserList());
 
 
 위 예시는 LobbyContainer에서 다른 유저를 get요청을 통해서 가져온뒤 
 해당 데이터로 리듀서 상태를 업데이트 시키는 LOAD_CARD라는 액션을 수행 시키는 예제입니다.
 
 해당 타입이 수행해야하는 api 요청이 GET이라면,
 export const FIIL_ME_IN = () => ({
  type: SOME_TYPE,
});

POST요청과 같이 특정 데이터를 인자로 전달해야하는 상황이라면 
 export const FIIL_ME_IN = (SOME_DATA) => ({
  type: SOME_TYPE,
  payload: SOME_DATA,
});

위 처럼 인자에 데이터를 넣은뒤 payload에 넣어줄 수 있습니다.
payload에 넣은 데이터는 어떻게 사용할 수 있는지는 아래에서 추가적으로 서술하겠습니다.
*/


export function* SOME_SAGA(action) {
  try {
    const loadRusult = yield call(SOME_API.SOME_API.ASYNC, action.payload);

    yield put({
      type: SUCCESS,
      payload: loadRusult,
    });
  } catch (e) {
    yield put({
      type: FAILURE,
      payload: e,
    });
  }
}

export function* SOME_WATCHER_SAGA() {
  yield takeEvery(SOME_TYPE, SOME_SAGA);
}

/*
기본적으로 하나의 사가 모듈에는 두개의 사가 함수가 필요합니다. 바로 액션 수행을 감시하는 SOME_WATCHER_SAGA와
SOME_WATCHER_SAGA가 액션을 감지했을때 콜백으로 수행될 SOME_SAGA 함수 입니다.

먼저 SOME_WATCHER_SAGA 함수를 봐주시길 바랍니다. 이 함수는 감시자로서, 상단에서 지정했던 액션 타입의 수행 여부를 
감시합니다. 예제를 한번 살펴볼까요?

// ===================
// 	EXAMPLE 
// ===================

export function* loadUserCardsSaga(action) {
  try {
    const loadRusult = yield call(loadCardApi.loadCardAsync, action.payload);

    yield put({
      type: SUCCESS,
      payload: loadRusult,
    });
  } catch (e) {
    yield put({
      type: FAILURE,
      payload: e,
    });
  }
}

export function* userCardSaga() {
  yield takeEvery(LOAD_CARD, loadUserCardsSaga);
}


먼저 우린 userCardSaga라는 감시자 역활을 하는 사가 함수를 주목할 필요가 있습니다. 해당 함수는 LOAD_CARD라는 액션 타입의 
수행여부를 감시하며, 만일 수행됬다면, 두번째 인자에 있는 loadUserCardsSaga란 콜백 함수를 수행합니다. 

이제 거의다 끝났습니다! 이제 실질적인 리덕스 사가의 강점이 부각되는 파트입니다. 위의 예제 코드로 계속 설명드리겠습니다.

// const loadRusult = yield call(loadCardApi.loadCardAsync, action.payload); 
이부분 부터 짚고 넘어가죠. 아마 async await 구문을 사용해본적이 있는 분이라면 'yield call'이란 구문을 좀더 쉽게
이해하실 수 있으실겁니다. 

여기의 call은 마치 await과 비슷한 역활을 합니다. 즉 첫번째 인자의 함수의 수행이 끝난 후에야
loadRusult란 변수에 할당을 하겠다는거죠. (해당 프로젝트를 기준으로 첫번째 함수 즉, api요청 함수는 api디렉토리에 
위치해있습니다.) 그리고 두번째 인자는 방금전 post요청 시 우리가 payload로 넣어준 값이 들어가게됩니다.
만일 get요청 이었다면 두번째 인자는 필요하지않겠죠?

아무튼 loadCardApi.loadCardAsync란 api요청을 보내는 함수가 잘 수행되어 loadRusult에 정상적으로 값이 할당되었다면,

이제 'yield put'이란 구문이 보일겁니다. 여기서 'put'은 액션 타입을 수행시키는 dispatch와 흡사합니다. 즉 성공했다면
SUCCESS 타입을, 실패했다면 FAILURE타입을 수행시키는거죠:)

*/

export default function SOME_REDUCER(state = {}, action) {
  switch (action.type) {
    case SOME_TYPE:
      return {
        ...state,
      };
    case SUCCESS:
      return {
        ...state,
        SOME_KEY: action.payload,
      };
    case FAILURE:
      return {
        ...state,
        SOME_ERROR: SOME_VALUE,
      };

    default:
      return state;
  }
}

/*
자 이제 위에서 만들어준 모든 타입들의 중앙 처리 장소인 대망의 'reducer'입니다. 여기서 우리가 주목해야 할 부분은 
첫번째 인자로 전달되는 'state'와 아래의 'switch / case'구문 입니다. 먼저 'reducer'의 'state'는 따로 임의의 
값들로 구성된 객체로 선언한뒤 인자로 전달할 수 있지만, 편의를 위해서 인자 자체에서 빈 객체로 선언해둔 상태입니다. 
그렇기에 해당 리듀서의 초기값은 빈 객체이며, 이제 사가함수에서 성공 / 실패 여부에따라 
SUCCESS 및 FAILURE로 나뉘게되고, 그에 맞는 state 업데이트가 이뤄지게 됩니다. 

*/
profile
Take me home~~~~

0개의 댓글