😈 Redux-Sagaλ₯Ό λ™μž‘μ‹œν‚€λŠ” 기본적인 방법

μΊ”λ”” ν¬λŸ¬μ‰¬ 사가 말고 λ¦¬λ•μŠ€ 사가

hankyeolk

λΈ”λ‘œκ·Έ λͺ©μ°¨

λ¦¬λ•μŠ€ 미듀웨어

redux-sagaλŠ” λ¦¬λ•μŠ€ μƒνƒœκ³„λ₯Ό μ§€νƒ±ν•˜κ³  μžˆλŠ” λ‹€μ–‘ν•œ 미듀웨어 쀑 ν•«ν•œ 미듀웨어닀. λ―Έλ“€μ›¨μ–΄λŠ”(middleware Express.js둜 μ„œλ²„λ₯Ό ꡬ좕할 λ•Œ μ‚¬μš©λ˜λŠ” μ—¬λŸ¬ 미듀웨어와 μ‚¬μš©μ„±κ³Ό λŠλ‚Œμ΄ 거의 λΉ„μŠ·ν•˜λ‹€. nextλΌλŠ” λ©”μ„œλ“œλ‘œ λ‹€μŒ λ™μž‘μ„ λΆ€λ₯΄λŠ” 것도 λ™μΌν•˜λ‹€.(보톡 λ¦¬λ•μŠ€μ—μ„œλŠ” λ¦¬λ“€μ„œλ₯Ό ν˜ΈμΆœν•œλ‹€.) μ΅œμ’…μ μΈ λ™μž‘ 처리 이전에 νŠΉμˆ˜ν•œ λ™μž‘μ„ λ§Œλ“€μ–΄λ‚΄λŠ” 역할을 ν•˜λŠ” 점도 λΉ„μŠ·ν•˜λ‹€. λ¦¬λ•μŠ€μ—μ„œ μ΅œμ’…μ μΈ μ²˜λ¦¬λŠ” μƒνƒœκ°’μ— λŒ€ν•œ μ–΄λ–€ λ³€ν™”λ₯Ό μ˜λ―Έν•œλ‹€. νŠΉμˆ˜ν•œ λ™μž‘μ€ 비동기 μ²˜λ¦¬λ“±μ„ λ“€ 수 μžˆλ‹€.

redux-thunk도 λŒ€ν‘œμ μ΄κ³  κ°„λ‹¨ν•œ λ¦¬λ•μŠ€ 미듀웨어닀. κ°€λ²Όμš΄ λΉ„λ™κΈ°μ²˜λ¦¬ λͺ©μ μœΌλ‘œ 많이 μ‚¬μš©λœλ‹€. λͺ¨λ“ˆ 자체둜 λΆˆλŸ¬μ™€μ„œ μ‚¬μš©ν•  μˆ˜λ„ μžˆμ§€λ§Œ 미듀웨어 κ·Έ 자체둜 μ‘μš©ν•˜κΈ° 쒋은 μ½”λ“œλ‘œ κ΅¬μ„±λ˜μ–΄μžˆλ‹€. ν•˜μ§€λ§Œ api둜 μ—¬λŸ¬ 비동기적 λ™μž‘μ„ μ²˜λ¦¬ν•˜λŠ” κ²ƒμ—λŠ” 쑰금 무리가 μžˆμ–΄μ„œ μ œλ„ˆλ ˆμ΄ν„° 문법 기반의 redux-sagaκ°€ 많이 μ£Όλͺ©λ°›κ³  μžˆλ‹€. 그리고 였늘 닀뀄볼 λ‚΄μš©λ„ λ¦¬λ•μŠ€ 사가닀.


λ¦¬λ•μŠ€ 사가와 μ œλ„ˆλ ˆμ΄ν„°

λ¦¬λ•μŠ€ μ‚¬κ°€λŠ” μžλ°”μŠ€ν¬λ¦½νŠΈμ˜ μ œλ„ˆλ ˆμ΄ν„° 문법을 μ΄μš©ν•œλ‹€. function* ν˜•νƒœμ˜ νŠΉμˆ˜ν•œ ν•¨μˆ˜λ‘œ μƒμ„±λœ μ œλ„ˆλ ˆμ΄ν„° κ°μ²΄λŠ” { value, done } 속성을 가지고 μžˆλŠ” 정말 νŠΉμˆ˜ν•œ 객체닀. μ œλ„ˆλ ˆμ΄ν„° ν•¨μˆ˜ λ‚΄λΆ€μ—μ„œ yield라고 ν•˜λŠ” ν‚€μ›Œλ“œλ‘œ λ‹€μŒ κ°’, λ™μž‘μ„ μ œμ–΄ν•œλ‹€. κ·Έλž˜μ„œ while(true)와 같이 ν”„λ‘œκ·Έλž¨μ„ ν„°νŠΈλ¦¬λŠ” λ¬΄ν•œλ£¨ν”„λ„ μ œλ„ˆλ ˆμ΄ν„° ν•¨μˆ˜ λ‚΄λΆ€μ—μ„œ μ‚¬μš©ν•  수 μžˆλ‹€. yield ν‚€μ›Œλ“œλ₯Ό λ§Œλ‚˜λ©΄ λ’€μ˜ λ‘œμ§μ΄λ‚˜ 값을 μ „λ‹¬ν•˜κ³  μš°μ„  ν•¨μˆ˜μ—μ„œ λ²—μ–΄λ‚˜κΈ° λ•Œλ¬Έμ΄λ‹€. .next()라고 ν•˜λŠ” λ©”μ„œλ“œλ₯Ό λ°›μ•˜μ„ λ•Œλ§Œ λ‹€μŒ λ™μž‘μ„ μ²˜λ¦¬ν•˜κΈ° λ•Œλ¬Έμ— 비동기적인 처리λ₯Ό μ œμ–΄ν•˜κΈ° μ’‹λ‹€.

λ¦¬λ•μŠ€ 사가가 μ œλ„ˆλ ˆμ΄ν„° 문법 기반인 것도 비동기적 처리의 인식과 μ œμ–΄λ₯Ό 잘 ν†΅μ œν•˜κΈ° μœ„ν•¨μ΄λ‹€. λ¦¬λ“€μ„œμ— μ •μ˜λœ νŠΉμ •ν•œ μ•‘μ…˜μ„ 기닀리닀가 μ•‘μ…˜μ΄ λ°œμƒν•˜λŠ” μ‹œμ μ—μ„œ yield에 λ“±λ‘λœ ν•¨μˆ˜λ‚˜ 둜직이 λ™μž‘ν•˜κ²Œ ν•˜λŠ” 것이닀. 이런 μ²˜λ¦¬λ“€μ€ λ¦¬λ•μŠ€ 사가에 미리 μ •μ˜λœ μ—¬λŸ¬ λΆ€μˆ˜νš¨κ³Ό(effects) ν•¨μˆ˜λ“€λ‘œ λ™μž‘ν•˜κ²Œ λœλ‹€.

λ¦¬λ•μŠ€ 사가 μ‹œμž‘ν•˜κΈ°

λ¦¬λ•μŠ€μ—μ„œ λ™μž‘ν•˜λŠ” 것이기 λ•Œλ¬Έμ— 기본적으둜 μ•‘μ…˜, (μ•‘μ…˜ μƒμ„±μž), λ¦¬λ“€μ„œ, μŠ€ν† μ–΄λŠ” ꡬ성이 λ˜μ–΄μžˆμ–΄μ•Ό ν•œλ‹€. κ²°κ΅­ λ¦¬λ•μŠ€λ₯Ό μ‚¬μš©ν•˜λŠ” μ΄μœ λŠ” β€˜μƒνƒœκ°’ 변화’이기 λ•Œλ¬Έμ΄λ‹€. κ°„λ‹¨ν•˜κ²Œ μœ μ €μ˜ λ‘œκ·ΈμΈμ„ μΈμ§€ν•˜λŠ” λ¦¬λ•μŠ€ λ‘œμ§μ„ κ΅¬μ„±ν•΄λ³΄λ©΄μ„œ μ‚¬κ°€μ˜ μ‚¬μš©λ²•μ„ ν™•μΈν•΄λ³΄μž.

// reducer/user.js

// action
const LOGIN_REQ = 'LOGIN_REQ';
const LOGIN_SUCCESS = 'LOGIN_SUCCESS';
const LOGIN_FAIL = 'LOGIN_FAIL';
const loginRequestAction = (userData) => ({
  type: 'LOGIN_REQ',
  data: userData
});

// initialState
const INITIAL_STATE = {
  loginPendding: false,
  loginDone: false,
  loginError: null,
  userData: null
};

// reducer
const reducer = (state = INITIAL_STATE, action) => {
  switch (action.type) {
    case LOGIN_REQ:
      return {
        ...state,
        loginPendding: true,
        loginDone: false,
        loginError: null
      };
    case LOGIN_SUCCESS:
      return {
        ...state,
        loginPendding: false,
        loginDone: true,
        userData: action.data
      };
    case LOGIN_FAIL:
      return {
        ...state,
        loginPendding: false,
        loginError: action.error
      };
  }
};

export { LOGIN_REQ, LOGIN_SUCCESS, LOGIN_FAIL, loginRequestAction };
export default reducer;

μœ„μ—μ„œμ²˜λŸΌ κ°„λ‹¨ν•˜κ²Œ 둜그인 μš”μ²­μ— μ˜ν•œ μƒνƒœκ°’μ„ λ³€κ²½ν•  λ¦¬λ“€μ„œ λ‘œμ§μ„ κ΅¬μ„±ν–ˆλ‹€. switch κ΅¬λ¬Έμ—μ„œ 기본적인 λ¦¬λ“€μ„œ μ½”λ“œκ°€ μž₯ν™©ν•˜κΈ° λ•Œλ¬Έμ— immer와 같은 νŒ¨ν‚€μ§€λ₯Ό μ΄μš©ν•˜κ±°λ‚˜ createReducer와 같은 μœ ν‹Έλ¦¬ν‹° ν•¨μˆ˜λ₯Ό 직접 κ΅¬ν˜„ν•΄μ„œ μ‚¬μš©ν•˜λ©΄ μ’‹λ‹€. μš°μ„  일단은 λ² μ΄μ§ν•˜κ²Œ μ‚¬μš©ν•œλ‹€.

λ¦¬λ•μŠ€ μ‚¬κ°€λŠ” μŠ€ν† μ–΄λ₯Ό λ§Œλ“€λ•Œ λ―Έλ“€μ›¨μ–΄λ‘œ μ—°κ²°ν•˜κ³ , λ™μž‘ν•˜λŠ” ꡬ문을 λ„£μ–΄μ£Όλ©΄ λœλ‹€. 그건 μ–΄λ–»κ²Œ ν•˜λƒκ³ ? μ•„λž˜μ˜ μ½”λ“œμ²˜λŸΌ ν•˜λ©΄λœλ‹€.

// store.js
import { createStore, combineReducers, applyMiddleware } from 'redux';
import createSagaMiddleware from 'redux-saga';
import user from './user';
import rootSaga from './saga';

// μ—¬λŸ¬ μƒνƒœκ°’μ„ λ³€κ²½ν•˜λŠ” λ¦¬λ“€μ„œλ“€μ„ ν•˜λ‚˜μ˜ λ¦¬λ“€μ„œ ν•¨μˆ˜λ‘œ ν•¨μΉœλ‹€.
const rootReducer = combineReducers({ user });

// 사가 미듀웨어λ₯Ό μƒμ„±ν•΄μ„œ μŠ€ν† μ–΄μ— μ—°κ²°ν•΄μ€€λ‹€.
const sagaMiddleware = createSagaMiddleware();

// store 생성
const store = createStore(rootReducer, applyMiddleware(sagaMiddleware));

// 사가 λ―Έλ“€μ›¨μ–΄μ—μ„œ 톡합 사가 ν•¨μˆ˜λ₯Ό μ‹€ν–‰μ‹œν‚¨λ‹€.
sagaMiddleware.run(rootSaga);

export default store;

ν•˜λ‚˜μ”© 천천히 μ•Œμ•„λ³΄μž. λ¦¬λ•μŠ€ μ‚¬κ°€μ—μ„œ createSagaMiddlewareλ₯Ό λΆˆλŸ¬μ™€μ„œ 사가 미듀웨어 객체λ₯Ό 생성해쀀닀. μƒμ„±λœ 사가 미듀웨어λ₯Ό λ¦¬λ•μŠ€ μŠ€ν† μ–΄μ— applyMiddleware ν•¨μˆ˜μ˜ 인자둜 λ„˜κ²¨μ£Όλ©΄ λ“±λ‘λœ μŠ€ν† μ–΄ μƒνƒœκ°’μ„ λ³€κ²½ν•  λ•Œ 사가 ν•¨μˆ˜λ“€μ„ 인식할 μ€€λΉ„κ°€ λ˜μ—ˆλ‹€. 사가 미듀웨어 객체에 μžˆλŠ” run λ©”μ„œλ“œμ— 톡합적인 rootSaga ν•¨μˆ˜λ₯Ό μ—°κ²°ν•΄μ£Όλ©΄ 정말 사가λ₯Ό μ‚¬μš©ν•  κΈ°λ³Έ μ„ΈνŒ…μ„ 끝이닀. 이제 남은 것은 rootSagaλ₯Ό μž‘μ„±ν•˜λŸ¬ κ°€λŠ” 일뿐이닀!

사가 ν•¨μˆ˜μ™€ λΆ€μˆ˜νš¨κ³Ό ν•¨μˆ˜λ“€

본격적으둜 비동기적 μ•‘μ…˜μ„ μΈμ‹ν•˜κ³  μƒνƒœκ°’μ„ λ³€κ²½ν•˜κ²Œ ν•˜λŠ” 사가 ν•¨μˆ˜λ“€μ„ μž‘μ„±ν•΄λ³΄μž. λ¦¬λ•μŠ€ μŠ€ν† μ–΄μ— μ—°κ²°ν•œ rootSaga에 μ—¬λŸ¬ 사가 ν•¨μˆ˜λ“€μ„ μ—°κ²°ν•΄μ£Όμ–΄ 사가 ν•¨μˆ˜κ°€ μ•‘μ…˜μ„ 인식할 수 μžˆλ„λ‘ μ²˜λ¦¬ν•΄μ£Όλ©΄ λœλ‹€.

// saga/index.js
import { all, fork } from 'redux-saga/effects';
import userSaga from './user';

// yield ν‚€μ›Œλ“œ 뒀에 λ¦¬λ•μŠ€ μ‚¬κ°€μ˜ λΆ€μˆ˜νš¨κ³Ό ν•¨μˆ˜κ°€ μ˜¨λ‹€.
export default function* rootSaga() {
  yield all([fork(userSaga)]);
}

λ¦¬λ•μŠ€ μ‚¬κ°€μ˜ λΆ€μˆ˜νš¨κ³Ό ν•¨μˆ˜κ°€ λŒ€λΆ€λΆ„μ˜ 비동기 처리λ₯Ό ν•œλ‹€κ³  보면 λœλ‹€. λΆ€μˆ˜νš¨κ³Ό ν•¨μˆ˜λ₯Ό μ‚¬μš©ν•˜λ €λ©΄ yield둜 κ·Έ μ œμ•½μ„ 걸어쀄 ν•„μš”κ°€ μžˆλ‹€. rootSaga μ œλ„ˆλ ˆμ΄ν„° ν•¨μˆ˜ 내뢀에 λ“±λ‘λœ yield ν‚€μ›Œλ“œμ—λŠ” all이라고 ν•˜λŠ” λΆ€μˆ˜νš¨κ³Ό ν•¨μˆ˜κ°€ λΆ™λŠ”λ‹€. 단어 뜻 κ·ΈλŒ€λ‘œ ν•¨μˆ˜ λ‚΄λΆ€ 배열에 λ“±λ‘λœ 사가 ν•¨μˆ˜λ“€μ„ λ¦¬λ•μŠ€ 사가 미듀웨어에 λ“±λ‘ν•˜λŠ” λΆ€μˆ˜νš¨κ³Ό ν•¨μˆ˜λ‹€. λ“±λ‘λœ ν•¨μˆ˜κ°€ λ™μ‹œμ— 싀행될 수 μžˆλ„λ‘ μ²˜λ¦¬ν•œλ‹€.

fork λΆ€μˆ˜νš¨κ³Ό ν•¨μˆ˜λŠ” μ•‘μ…˜μ„ λ°œμƒμ‹œν‚¨λ‹€. 사싀 rootSaga의 all ν•¨μˆ˜ λ‚΄λΆ€μ—μ„œ fork둜 사가 ν•¨μˆ˜λ₯Ό 등둝해도 되고, λ°”λ‘œ ν˜ΈμΆœν•˜λŠ” ν˜•μ‹μœΌλ‘œ ꡬ성해도 λœλ‹€. yield all([userSaga()]) μ΄λŸ°μ‹μœΌλ‘œ!

이제 정말 둜그인 μ•‘μ…˜μ΄ λ°œμƒν•œ 것을 κ°μ§€ν•˜κ³  비동기적인 μ„œλ²„ 처리 μ΄ν›„μ˜ 응닡에 λ”°λΌμ„œ λ¦¬λ•μŠ€ μƒνƒœκ°’μ„ λ³€κ²½ν•˜λŠ” userSaga ν•¨μˆ˜λ₯Ό μž‘μ„±ν•΄λ³΄μž.

// login μš”μ²­μ„ λ³΄λ‚΄λŠ” api
// api ν•¨μˆ˜λŠ” μœ μΌν•˜κ²Œ μ œλ„ˆλ ˆμ΄ν„° ν•¨μˆ˜κ°€ μ•„λ‹ˆλ‹€!
function loginApi(data) {
  return axios.post('/api/login', data);
}

// login μš”μ²­μ— λŒ€ν•œ μ•‘μ…˜μ΄ κ°μ§€λ˜μ—ˆμ„λ•Œ μƒνƒœκ°’ 처리λ₯Ό μœ„ν•΄ λ™μž‘ν•˜λŠ” 사가 ν•¨μˆ˜
function* loginRequest(action) {
  const userData = yield call(loginApi, action.data);

  try {
    yield put({ type: LOGIN_SUCCESS, data: userData });
  } catch (err) {
    yield put({ type: LOGIN_FAIL, error: err.response.data });
  }
}

// 둜그인 μš”μ²­μ΄ λ“€μ–΄μ˜€λŠ”μ§€λ₯Ό κ°μ§€ν•˜λŠ” μ œλ„ˆλ ˆμ΄ν„° ν•¨μˆ˜
// addEventListener ν•¨μˆ˜μ™€ κ·Έ μ‚¬μš©λ²•μ΄ λΉ„μŠ·ν•˜λ‹€.
function* waitLogin() {
  yield takeLatest(LOGIN_REQ, loginRequest);
}

function* waitLogin2() {
  // μ œλ„ˆλ ˆμ΄ν„° 문법은 μ΄λ ‡κ²Œ λ¬΄ν•œλ°˜λ³΅ ꡬ문도 yield둜 μ œμ–΄ν•  수 μžˆλ‹€.
  while (true) {
    yield take(LOGIN_REQ, loginRequest);
  }
}

// userSaga ν•¨μˆ˜ 등둝
export default function* userSaga() {
  yield all([waitLogin()]);
}

userSaga라고 ν•˜λŠ” μ œλ„ˆλ ˆμ΄ν„° 사가 ν•¨μˆ˜μ— μ—­μ‹œ λ§ˆμ°¬κ°€μ§€λ‘œ all λΆ€μˆ˜νš¨κ³Ό ν•¨μˆ˜κ°€ λ“±λ‘λœ 것을 λ³Ό 수 μžˆλ‹€.

waitLogin μ œλ„ˆλ ˆμ΄ν„° ν•¨μˆ˜λŠ” takeLatest라고 ν•˜λŠ” 사가 λΆ€μˆ˜νš¨κ³Ό ν•¨μˆ˜λ₯Ό λ™μž‘ μ‹œν‚¨λ‹€. λ™μΌν•œ μ•‘μ…˜μ— λŒ€ν•œ μš”μ²­μ΄ μ—¬λŸ¬λ²ˆ λ“€μ–΄μ˜¬ 경우 κ°€μž₯ 졜근 즉, κ°€μž₯ λ§ˆμ§€λ§‰ μš”μ²­μ„ μš°μ„ ν•΄μ„œ μ²˜λ¦¬ν•˜λŠ” λΆ€μˆ˜νš¨κ³Ό ν•¨μˆ˜λ‹€. λΆ€μˆ˜νš¨κ³Ό λ‚΄λΆ€μ—μ„œ μ–΄λ–€ μ•‘μ…˜μ„ 감지할 지 λ“±λ‘ν•˜κ³ , μ•‘μ…˜μ΄ κ°μ§€λ˜λ©΄ λ™μž‘μ‹œν‚¬ μ‚¬κ°€ν•¨μˆ˜λ₯Ό λ“±λ‘ν•œλ‹€. ν•¨μˆ˜λ₯Ό μ•‘μ…˜ 이름에 λ“±λ‘ν•΄μ„œ μ‚¬μš©ν•˜λŠ” λͺ¨μŠ΅μ΄ addEventListener와 λΉ„μŠ·ν•˜λ‹€.

λ“±λ‘λœ loginRequest μ œλ„ˆλ ˆμ΄ν„° ν•¨μˆ˜λŠ” apiλ₯Ό μ§μ ‘μ μœΌλ‘œ ν˜ΈμΆœν•œλ‹€. yield ν‚€μ›Œλ“œ 뒀에 call이라고 ν•˜λŠ” λΆ€μˆ˜νš¨κ³Ό ν•¨μˆ˜ λ‚΄λΆ€μ—μ„œ apiκ°€ 호좜 된 것을 λ³Ό 수 μžˆλ‹€. 단어 뜻 κ·ΈλŒ€λ‘œ 비동기 μ•‘μ…˜μ„ ν˜ΈμΆœν•œλ‹€. 동기적인 처리λ₯Ό ν•œλ‹€. 첫 인자둜 λ“±λ‘ν•œ api ν•¨μˆ˜μ— λ‘λ²ˆμ§Έ μΈμžλ“€μ„ νŒŒλΌλ―Έν„°λ‘œ λ„˜κΈ΄λ‹€. loginRequest ν•¨μˆ˜κ°€ μ‹€μ§ˆμ μœΌλ‘œ μ»΄ν¬λ„ŒνŠΈ λ‘œμ§μ— λ“€μ–΄κ°„λ‹€λŠ” 것을 μš°λ¦¬λŠ” μ•Œ 수 μžˆλ‹€.

loginRequest ν•¨μˆ˜μ˜ try ~ catch ꡬ문 λ‚΄λΆ€μ—μ„œ μ•‘μ…˜μ„ λ””μŠ€νŒ¨μΉ˜ μ‹œν‚€λŠ” 것과 κ·Έ 둜직이 λΉ„μŠ·ν•΄λ³΄μΈλ‹€. put이라고 ν•˜λŠ” λΆ€μˆ˜νš¨κ³Ό ν•¨μˆ˜κ°€ μ•‘μ…˜ 객체λ₯Ό μ—…λ°μ΄νŠΈ ν•΄μ£ΌλŠ” 역할을 ν•œλ‹€. μ§μ ‘μ μœΌλ‘œ λ¦¬λ“€μ„œμ— μž‘μ„±ν•œ μ•‘μ…˜ μƒμ„±μžλ₯Ό λΆˆλŸ¬μ™€λ„ 되고, μœ„μ˜ μ½”λ“œμ²˜λŸΌ μ•‘μ…˜ 객체 자체λ₯Ό λ§Œλ“€μ–΄μ€„ 수 μžˆλ‹€.


κ·Έ 외에 정말 λ‹€μ–‘ν•œ λ¦¬λ•μŠ€ μ‚¬κ°€μ˜ λΆ€μˆ˜νš¨κ³Ό ν•¨μˆ˜κ°€ μžˆλ‹€. 자주 μ‚¬μš©ν•˜λŠ” λΆ€μˆ˜νš¨κ³Ό ν•¨μˆ˜λ₯Ό λ§ˆμ§€λ§‰μœΌλ‘œ 정리해본닀. λΆ€μˆ˜νš¨κ³Ό ν•¨μˆ˜ μ•žμ—μ„œλŠ” yield ν‚€μ›Œλ“œλ‘œ μ œμ•½μ„ κ±Έμ–΄μ€€λ‹€λŠ” 것을 μžŠμ§€λ§μž

  • take : 첫 인자둜 μ•‘μ…˜μ„ λ“±λ‘ν•΄μ„œ μ•‘μ…˜μ΄ λ°œμƒν•˜λŠ” 것을 κ°μ§€ν•œλ‹€. μ•‘μ…˜μ΄ λ°œμƒν•˜λ©΄ λ‘λ²ˆμ§Έ μΈμžμ— λ°˜μ˜ν•œ ν•¨μˆ˜λ₯Ό ν˜ΈμΆœν•œλ‹€. takeκ°€ prefix ν˜•νƒœλ‘œ 뢙은 λΆ€μˆ˜νš¨κ³Ό ν•¨μˆ˜λŠ” 보톡 μ•‘μ…˜μ„ κ°μ§€ν•˜κ³  λ“±λ‘ν•œ ν•¨μˆ˜λ₯Ό λ™μž‘μ‹œν‚¨λ‹€. ν•˜μ§€λ§Œ μ•‘μ…˜μ„ κ°μ§€ν•˜λŠ” 것이 λ‹¨λ°œμ μ΄λ‹€.
  • takeEvery : take ν•¨μˆ˜μ™€ μ‚¬μš©λ°©λ²•μ€ λΉ„μŠ·ν•˜μ§€λ§Œ λΉ„λ™κΈ°μ μœΌλ‘œ μ•‘μ…˜μ„ κΈ°λ‹€λ¦°λ‹€. λ‹¨λ°œμ μ΄μ§€ μ•Šλ‹€.
  • throttle : λ“±λ‘ν•œ μ‹œκ°„λ§ŒνΌ μš”μ²­μ„ 보낼 수 없도둝 μ œν•œμ„ κ±°λŠ” λΆ€μˆ˜νš¨κ³Ό ν•¨μˆ˜λ‹€.

λ‚˜λŠ” λ¦¬λ•μŠ€λ₯Ό μ‚¬μš©ν•΄μ„œ λ¦¬μ•‘νŠΈ ν”„λ‘œμ νŠΈμ˜ 전역적인 μƒνƒœκ°’μ„ κ΄€λ¦¬ν•˜λ©΄μ„œ μ½”λ“œλ₯Ό μž‘μ„±ν•˜κ³  μžˆλ‹€. λ‹Ήμ—°νžˆ apiλ₯Ό ν†΅ν•΄μ„œ 비동기적인 μƒνƒœκ°’ 변경을 μ²˜λ¦¬ν•˜λŠ” 둜직이 μžˆλ‹€. κ·Έλ•Œ 주둜 λ¦¬λ•μŠ€ 사가λ₯Ό μ‚¬μš©ν•˜κ³  μžˆλ‹€. λ„ˆλ¬΄ λ§Žμ€ λΆ€μˆ˜νš¨κ³Ό ν•¨μˆ˜λ“€μ΄ μžˆμ–΄μ„œ μ μ†Œμ— μ ν•©ν•œ λΆ€μˆ˜νš¨κ³Όλ₯Ό λ°œμƒμ‹œν‚€λŠ” 것이 아직도 μ–΄λ ΅μ§€λ§Œ, μ–Όλ§ˆλ‚˜ μ“°μž„μ§€ μ’‹μ€μ§€λŠ” μ‚¬μš©ν•  λ•Œλ§ˆλ‹€ μ§œλ¦Ών•˜κ²Œ 느끼고 μžˆλ‹€.