❗️ 해당 게시물은 팀 프로젝트 팀원 배포용 문서 이며 해당 프로젝트를 기반으로 서술되었습니다.
// * =======================
// * 리덕스 사가 간단 사용 가이드
// * =======================
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 업데이트가 이뤄지게 됩니다.
*/