Develop/React & React Native

[React Native] Redux Saga (개인공부용)

안다희 2020. 5. 1. 13:46
728x90

 

 

참고블로그

https://jeonghwan-kim.github.io/dev/2019/07/22/react-saga-ts-1.html

 

 

 

 

1. 비동기와 동기의 차이

https://private.tistory.com/24

 

동기와 비동기의 개념과 차이

데이터를 받는 방식인 동기와 비동기. 이 둘의 개념에 대해 설명하는 게시물은 매우 많은데 프로그래밍적으로 생각했을 때 이해가 가지 않아서 쉽게 이해를 할 수 있는 동기와 비동기의 예가 ��

private.tistory.com

 

동기: 동시에 일어난다.

비동기: 요청과 결과가 동시에 일어나지 않는다.

 

 

2. REST API

https://private.tistory.com/28?category=655782

 

REST API의 탄생/구성/특징 등

REST API의 탄생  - REST는 Representational State Transfer라는 용어의 약자로써 2000년도에 로이 필딩(Roy Fielding)의 박사학위 논문에서 최초로 소개되었습니다. 로이 필딩은 HTTP의 주요 저자 중 한사람으..

private.tistory.com

 

 

 

3. redux saga

 

3-1. 왜 쓰는가

 

 

 

 

await fetch

setState

이런건 사실 한 스크린에서 써도 됨.

 

 

이거를 왜 미들웨어를 이용해서 써야하는가.

=> setState만 쓸거면 안쓰는게 맞고, Redux 안거치면 안써도 되잖아.

=> API 요청, 비동기 작업과 관련된 상태 관리를 위해 쓴다.

 

=> 로그인 유저정보 받아오는거 redux?

 

그리고 왜 thunk보다 saga를 써야하는가.

 

아니면 이런거는 setState니까 굳이 할 필요가 없는건가??

redux에 대해서만 이렇게 하고 한 스크린에서 괜찮은건 여기서 해도 되는건가?

 

- setState는 왜 비동기로 처리되는가

https://soldonii.tistory.com/112

 

비동기로 작동하는 setState 이해하기

*Udemy의 "Complete React Developer in 2020" 강의에서 학습한 내용을 정리한 포스팅입니다. *자바스크립트와 리액트를 배우는 단계라 오류가 있을 수 있습니다. 틀린 내용은 댓글로 말씀해주시면 수정하

soldonii.tistory.com

앞선 글에서도 살펴봤지만, 리액트는 state가 변경될 때마다 매번 DOM을 re-rendering하는 것은 아니다. 퍼포먼스 효율성을 위해서 여러 차례 setState가 있을 경우 다른 state의 변경까지 한꺼번에 통합해서 리액트 자신이 판단하기에 가장 적절한 시기에 DOM을 re-rendering한다.

 

const [count, setCount] = useState(1);

setCount(prev => prev + 1) 이게 맞는 표현.

 

 

https://velog.io/@eomttt/Redux-%EC%A0%81%EC%9A%A9%ED%95%98%EA%B8%B0-Thunk-Saga

 

Redux 미들웨어 적용하기 (Thunk, Saga)

지난 포스팅에서 React 프로젝트에 Redux 를 적용해보았습니다.이번 포스팅에서는 Redux 에서 비동기 처리를 위해 많이 쓰이는 미들웨어 중 Thunk, Saga를 둘 다 적용해 보고 이를 비교해보도록 하겠습�

velog.io

 

 

 

 

https://brunch.co.kr/@hee072794/108

 

React.setState를 사용하지 않는 3가지 이유

[번역] 3 Reasons why I stopped using React | * 이 글은 Michel Weststrate 의 3 Reasons why I stopped using React.setState 를 번역한 글입니다. ** 오역 및 오탈자가 있을 수 있습니다. 발견하시면 제보해주세요! 저와 비슷

brunch.co.kr

이거 읽다가 mobx 잠깐 영업 당했음... 그래도 saga는 알아두면 좋으니 다시 이어서 공부해보자

 

 

- 여전한 나의 의문. 

https://medium.com/@saturnuss/setstate-%EB%8A%94-await%EC%99%80-%EC%82%AC%EC%9A%A9%EC%9D%B4-%EA%B0%80%EB%8A%A5%ED%95%A0%EA%B9%8C-7b02581f6df4

 

setState() 는 await와 사용이 가능할까?

클라이언트 측에서 보내는 2가지 요청이 있다. 첫번째는 맥주를 달라는 것이고 두번째는 숙취를 보내고 알약을 받아오는 것이다.

medium.com

 

 

-- 그니까.. 

 

그냥 한 스크린에서 await, setState 해도 되지 않냐 이거지.

만약 redux를 쓴다해도 그냥 스크린 안에서

await, dispatch 하면 되는 문제 아냐?

dispatch를 하는김에 await도 같이 하면 되는건가......

그니까 왜 그걸 굳이 미들웨어까지 써서 하냐는거지...

그냥 함수 만들면 될것을...

이해가 안됨.

 

redux-thunk를 왜쓰냐.

간단하게 정리하면 redux-thunk는 일반 액션 생성자에 날개를 달아줍니다.

보통의 액션생성자는 그냥 하나의 액션객체를 생성할 뿐이지만 redux-thunk를 통해 만들게 되면
그 내부에서 여러가지 작업을 할 수도 있습니다.

https://jw910911.tistory.com/48

 

Redux - redux-thunk란? [리덕스 미들웨어]

redux-thunk 란? 리덕스를 사용하는 어플리케이션에서 비동기 작업을 사용할때 가장 기본적인 방법으로는 redux-thunk를 사용하는 것입니다. 해당 미들웨어는 리덕스를 개발한 Dan Abramov가 만든 것으로

jw910911.tistory.com

 

나랑 같은 생각을 가진 사람의 글 발견

https://changhoi.github.io/posts/etc/redux-async-demo/

 

React환경에서 Redux로 비동기 처리 하기

새롭게 공동 창업을 시작한 이후로 개인적으로 공부하고 글 쓸 시간이 확 줄어서 계획했던 글을 쓰지를 못 하고 있다 (거창하게 JS에서 공부하기 어려웠던 부분들을 정리해보겠다고 목차만 써놨

changhoi.github.io

https://github.com/reduxjs/redux-thunk/blob/master/src/index.js

 

reduxjs/redux-thunk

Thunk middleware for Redux. Contribute to reduxjs/redux-thunk development by creating an account on GitHub.

github.com

redux-thunk를 위해 추가된 코드는 단 14줄.

https://react.vlpt.us/redux-middleware/04-redux-thunk.html

 

4. redux-thunk · GitBook

4. redux-thunk 소개 redux-thunk는 리덕스에서 비동기 작업을 처리 할 때 가장 많이 사용하는 미들웨어입니다. 이 미들웨어를 사용하면 액션 객체가 아닌 함수를 디스패치 할 수 있습니다. redux-thunk는 �

react.vlpt.us

 

=> saga 볼 때도 이 글 보기.

 

 

 

store에 listener라는 기능도 있나보네? subscribe

React.FC<IProps>

 

 

그러면... 비동기 작업 + 리덕스 상태 변경을 위해 있는건가?

한 파일에서 코드가 길어지는걸 막는건가?

그러니까 한파일에서 미들웨어 없이 그냥 해도 되는건데, 그걸 미들웨어가 비동기 처리가 가능하기도 하고,

dispatch, getState를 쉽게 받아올 수 있기도 해서 쓰는거야?

 

나중에 맞는지 확인하기...

 

그래서 사실 한 스크린에서만 쓰는거면 await, setState도 좋은 방법인거지.

만약에 비동기 관련해서 loading, data, error 같은 데이터 흐름을 처리하고 싶은거면 saga 쓰는게 맞는거고.

 

 

 

redux-thunk: 객체 대신 함수를 생성하는 액션 생성함수를 작성 할 수 있게 해준다

https://ideveloper2.tistory.com/53

 

React -redux thunk, redux saga

React -redux thunk, redux saga >reudx-saga에 들어가기에 앞서, |redux-thunk란? redux-saga를 적용시키기 전에 redux-thunk를 먼저 사용해봤다면, 왜 redux-saga를 써야하는지 더 쉽게 파악할 수 있으므로, 시..

ideveloper2.tistory.com

 

https://velog.io/@bigbrothershin/redux-thunk

 

redux-thunk

1. 개념 redux-thunk는 리덕스에서 비동기 작업을 처리 할 때 가장 많이 사용하는 미들웨어입니다. 이 미들웨어를 사용하면 액션 객체가 아닌 함수를 디스패치 할 수 있습니다. > 함수를 디스패치 할

velog.io

 

 

 

 

그니까.. 일반 함수로 dispatch 다 할 수 있지만... 비동기적인 api 요청과 리덕스를 변경할 때 thunk를 쓰면 쉽게 비동기를 처리할 수 있다는거지.

뭐 굳이 하고 싶으면 일반 함수로도 쓸 수 있는데, dispatch와 getState가 인자로 넘어오는 thunk를 쓰면 쉽다는거지.

 

 

 

 

그럼 loading 끝났고 데이터가 있어.

ㅡㄴ데 다시 Refetch 하면 로딩은 사가 시작할 때 다시 true 만드는 건가? 

끝나면 false 만들고?

 

그니까 graphql 처럼 그때그때 딱딱 맞게 loading, data, error 가 다 동작하냐 이거지.

 

 

---

미들웨어를 사용하는 이유: 비동기 처리를 위함이야.

근데 단순히 그냥 데이터 받고 setState 할거면 그렇게 해도되고,

만약 그 외에 loading, data, error와 뭐 여러 리듀서 실행, 및 등등 많은 작업을 하고 싶으면,

함수를 따로 생성해야 깔끔하겠지? 근데 그냥 일반 함수 하면 store랑 dispatch를 그때그때 넘겨줘야할거니까 좀 번거롭겠지?

그래서 액션과 리듀서 사이 중간자 역할을 해줄 수 있는 미들웨어를 사용해서 dispatch와 store에 쉽게 접근하고 사용하고 그 값을 변경하면서, 여러 비동기 작업을 처리할 수 있는거지.

이게 내가 이해한 미들웨어 사용 이유

 

이제 드디어 saga를 쓸 수 있을 것이야.

왜 thunk를 안쓰냐. 

 

 

redux-saga:

복잡한 비동기 작업을 잘 쓸 수 있게 해줌

사이드 이펙트를 쉽게 관리

 

+ 더 검색해봐. 아마 비슷하겠지만.

 

 

 

 

=====

아무튼 이제 제대로 이해했어. 좀만 더 검색해보고 시작하자.

 

Logger 써보기

 

 

 

 

3-2. 개념서

https://react.vlpt.us/

 

벨로퍼트와 함께하는 모던 리액트 · GitBook

벨로퍼트와 함께하는 모던 리액트 본 강의자료는 패스트캠퍼스 온라인 강의에서 제공하는 리액트 강의에서 사용되는 강의 문서입니다. 이 튜토리얼은 여러분들이 JavaScript 의 기초를 잘 알고있�

react.vlpt.us

react.vlpt.us/redux-middleware/10-redux-saga.html

 

10. redux-saga · GitBook

10. redux-saga 소개 redux-saga는 redux-thunk 다음으로 가장 많이 사용되는 라이브러리입니다. redux-thunk의 경우엔 함수를 디스패치 할 수 있게 해주는 미들웨어였지요? redux-saga의 경우엔, 액션을 모니터��

react.vlpt.us

 

 

- ducks 패턴

http://guswnsxodlf.github.io/redux-ducks-pattern

 

Redux의 ducks패턴에 대해서

Ducks patternRedux를 사용하는 어플리케이션을 구축하다 보면 기능별로 여러 개의 액션 타입과, 액션, 리듀서 한 세트를 만들어야 한다. 이들은 관습적으로 여러 개의 폴더로 나누어져서, 하나의 기�

guswnsxodlf.github.io

https://github.com/JisuPark/ducks-modular-redux

 

JisuPark/ducks-modular-redux

A proposal for bundling reducers, action types and actions when using Redux - JisuPark/ducks-modular-redux

github.com

 

 

- Promise (개념서에 나오는 링크)

https://learnjs.vlpt.us/async/

 

 

- Generator 문법

https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Global_Objects/Generator

 

Generator

Generator 객체는 generator function 으로부터 반환된 값이며 반복자와 반복자 프로토콜을 준수합니다.

developer.mozilla.org

 

 

 

 

 

 

3-3. 리덕스 참고 블로그

https://jeonghwan-kim.github.io/dev/2019/07/22/react-saga-ts-1.html

 

리덕스 사가 사용하기 (타입스크립트 버전) - 1편

지난 글에서 정리한 것 처럼 리덕스는 다음 순서로 상태를 관리한다. 액션 객체 생성 스토어로 전달 리듀서가 액션 객체를 수신 액션 타입에 따라 전달받은 패이로드를 가지고 스토어 상태 변경

jeonghwan-kim.github.io

 

 

 

리덕스 사가!!!!

1. 문서

react.vlpt.us/redux-middleware/10-redux-saga.html

 

10. redux-saga · GitBook

10. redux-saga 소개 redux-saga는 redux-thunk 다음으로 가장 많이 사용되는 라이브러리입니다. redux-thunk의 경우엔 함수를 디스패치 할 수 있게 해주는 미들웨어였지요? redux-saga의 경우엔, 액션을 모니터��

react.vlpt.us

 

다 봤음. 실제 활용 사례로는 턱없이 부족하다.

 

2-1. 블로그

jeonghwan-kim.github.io/dev/2019/07/29/react-saga-ts-2.html

 

리덕스 사가 사용하기 (타입스크립트 버전) - 2편

jeonghwan-kim.github.io

=> 클래스 컴포넌트라 보기 불편

 

2-2. 블로그

chaewonkong.github.io/posts/react-redux-saga-typescript.html

 

React에서 TypeScript로 Redux-Saga 사용하기(예제)

튜토리얼(예제)을 직접 따라하며 React에서 TypeScript로 Redux-Saga의 사용법을 배워봅니다.

chaewonkong.github.io

 

jonir227.github.io/develop/2019/06/04/redux-saga%EC%99%80-typescript-%ED%8E%B8%ED%95%98%EA%B2%8C-%EC%82%AC%EC%9A%A9%ED%95%98%EA%B8%B0.html

 

redux-saga와 typescript 편하게 사용하기

나는 리덕스 사가의 팬이다. 작은 단위로 코드를 쪼개놓아 재사용성이 늘어나는 것도 좋고, 개발자의 코드가 개발자가 실행 시점을 정의하는게 아닌 사가가 실행의 주체가 된다는 점도 좋다. 작

jonir227.github.io

 

 

 

[9.14 월]

kamang-it.tistory.com/entry/React13reduxsaga%EB%A1%9C-%EB%B9%84%EB%8F%99%EA%B8%B0-%EA%B4%80%EB%A6%AC%ED%95%98%EA%B8%B0

 

[React-13]redux-saga로 비동기 관리하기

JavaScript와 HTML,CSS등에 대해서는 일체 다루지 않는다. 기초 지식은 다른 강의를 참조하도록 하라. 이 강의는 React의 사용법 위주로 작성되어 있다. 왜 React여야는지, React를 써야하는지에 대하지는

kamang-it.tistory.com

 

따라해보기!

 

 

gracefullight.dev/2017/12/06/Why-redux-saga/

 

왜 리덕스 사가(Redux-saga) 인가?

redux, redux-thunk, redux-promise-middleware, redux-actions, redux-saga 머리가 뽀개질 지경이다. 결국엔 redux-saga를 써야만 했고 왜 saga로 수렴하게 되는지에 대한 삽질기다. 도대체 몇 개의 미들웨어 라이브러리�

gracefullight.dev

 

왜 thunk가 아닌가? 거기에는  return이 promise이기도 하고 암것도 리턴안하고 액션이랑 api랑 짬뽕되어있다.

사가는 액션리스너다. 액션을 디스패치하면 사가가 실행된다. 직관적이다. userInfo라는 애를 api로 가져올 때 액션리스너만 디스패치 하면 사가가 듣고 있다가 캐치해서 알아서 해주니까!

 

 

 

root에서는 spawn 을 써야 한대 ???

mskims.github.io/redux-saga-in-korean/advanced/ForkModel.html

 

redux-saga의 포크(fork) 모델 · GitBook

No results matching ""

mskims.github.io

 

주요 함수 정리

sustainable-dev.tistory.com/94

 

redux-saga의 주요함수(delay, call, put, all, takeEvery, takeLatest)

새 프로젝트에서 redux-saga를 써보려고하는데 헷갈려서 주로 쓰는 함수를 정리해보았다. redux-saga docs 링크(한글번역) delay 설정된 시간 이후에 resolve하는 Promise 객체를 리턴한다. 예시: delay(1000) →.

sustainable-dev.tistory.com

 

 

-----

폴더 구조

 

코드정리

 

// store/modules/join/userInfo.ts
import { UserInfoRes } from "~/utils/interface";

export interface RFetchResult<TData> {
  // Redux Fetch Result
  loading: boolean;
  data: TData;
  error: Error;
}

export interface UserInfoState {
  userInfo: RFetchResult<UserInfoRes>;
}

const initialState: UserInfoState = {
  userInfo: {
    loading: false,
    data: null,
    error: null,
  },
};

export const GET_USER_INFO_REQUEST = "GET_USER_INFO_REQUEST"; // 요청 시작
export const GET_USER_INFO_SUCCESS = "GET_USER_INFO_SUCCESS"; // 요청 성공
export const GET_USER_INFO_ERROR = "GET_USER_INFO_ERROR"; // 요청 실패

export const getUserInfoRequest = () => {
  const fetchResult: RFetchResult<UserInfoRes> = {
    loading: true,
    data: null,
    error: null,
  };
  return {
    type: GET_USER_INFO_REQUEST,
    userInfo: fetchResult,
  };
};
export const getUserInfoSuccess = (userInfo: UserInfoRes) => {
  const fetchResult: RFetchResult<UserInfoRes> = {
    loading: false,
    data: userInfo,
    error: null,
  };
  return {
    type: GET_USER_INFO_SUCCESS,
    userInfo: fetchResult,
  };
};
export const getUserInfoError = (error: Error) => {
  const fetchResult: RFetchResult<UserInfoRes> = {
    loading: false,
    data: null,
    error,
  };
  return {
    type: GET_USER_INFO_ERROR,
    userInfo: fetchResult,
  };
};

export default (state = initialState, action): UserInfoState => {
  switch (action.type) {
    case GET_USER_INFO_REQUEST:
    case GET_USER_INFO_SUCCESS:
    case GET_USER_INFO_ERROR:
      return { ...state, userInfo: action.userInfo };

    default:
      return state;
  }
};
// store/modules/index.ts
import { combineReducers } from "redux";

import JoinReducer, { JoinState } from "./join";

export type RootState = {
  join: JoinState;
};

const rootReducer = combineReducers({
  join: JoinReducer,
});

export default rootReducer;
// saga/index.ts
import { all } from "redux-saga/effects";

import authSaga from "./auth";
import counterSaga from "./counter-test";
import { userInfoSaga } from "./join/userInfo";

export default function* rootSaga() {
  yield all([counterSaga(), authSaga(), userInfoSaga()]); // all 은 배열 안의 여러 사가를 동시에 실행시켜줍니다.
}
// saga/join/userInfo.ts
import { takeEvery, call, put } from "redux-saga/effects";

import * as userInfoAPI from "~/api/join/userInfo";
import {
  GET_USER_INFO_REQUEST,
  getUserInfoSuccess,
  getUserInfoError,
} from "~/store/modules/join/userInfo";

function* getUserInfoSaga() {
  try {
    const userInfo = yield call(userInfoAPI.getUserInfo);
    yield put(getUserInfoSuccess(userInfo));
  } catch (error) {
    yield put(getUserInfoError(error));
  }
}

// 사가들을 합치기
export function* userInfoSaga() {
  yield takeEvery(GET_USER_INFO_REQUEST, getUserInfoSaga);
}
// api/join
export const getUserInfo = async (): Promise<UserInfoRes> => {
  // try catch 하지 않음. saga에서 해주므로
  const token = await getTokenItem();
  if (!token) {
    throw Error("client - no token");
  }

  const response = await fetch(`${BASE_URL}/accounts/`, {
    method: "GET",
    headers: {
      Authorization: `Bearer ${token}`,
    },
  });

  const json: UserInfoRes = await response.json();
  return json;
};
// store/index.ts

import { createStore, Unsubscribe, applyMiddleware } from "redux";
import createSagaMiddleware from "redux-saga";

import rootSaga from "./saga";
import rootReducer from "./modules";

const sagaMiddleware = createSagaMiddleware();
const store = createStore(rootReducer, applyMiddleware(sagaMiddleware));
sagaMiddleware.run(rootSaga);
export default store;

 

 

 

 


- 비동기를 처리하기 위해서 씀.

- 비동기: 

- 동기: 리덕스는 결국 동기. 그게 처리되는 순간 아무것도 할 수 업성.

 

- 내부에서

리듀서는 순수함수여야 해.

순수함수? 항상 같은 값을 리턴하는 ㅏㅁ수

그걸 지켜주기 위해 괴상한 짓 하면 안돼

 

서버가 멈춰버리면 

 

모든 서버와의 통신은 비동기적으로 처리해야해.

request success failure pattern

 

질문

이 과정을

 

 

순수함수? 

 

redux saga entity pattern

 

action 하나하나가 기능이다

 

 

export enum aaaa {

request = 'GET_~~'

 

 

대괄호로 감싸면 key값으로 쓸 수 있다.

 

redux-actions

라이브러리

redux 편하게 쓸 수 있도록 메소드를 정의해놓은 라이브러리

 

immer: 불변성을 지켜줌 (라이브러리)

 

redux 세가지의 원칙

 

redux-actions

createAction 쓰면 type, action 안써줘도 돼 이미 추상화된 애거든

increment = () => ({type: sdafsdfa})

말고

increment = createActio(~~~~)b

 

 

import produce from 'immer'

produce()

이걸 쓰면 ...state 이거 안해줘도 되지

 

 

state.status

이걸로 상태관리 

 

swich 에서 쓰는 case 같은거야

['key']

키 값을 변수로 쓸 수 있어

 

js에서 대괄호를 쓸 수 있어서 추상화

 

handleActions

...~~~해준다음

success만 다시 오버라이딩

https://velopert.com/3358

 

 

 

saga gogogogogogo

 

fetchEntity !?

 

es6 generator ***

yield: 코드를 멈추는 아이.

 

 

 

{all, fork, takeLatest, put, call}

from redux-saga/effects

 

function* watchGetNotices() {

  yield takeLatest()

}

 

yield는 코드를 block시켜서 while true를 쓴다.

takeEvery 가 while true랑 똑같대~ (머 약간은 다르나)

 

takeLatest

버튼을 계속 누르면 그걸 다 처리하지 않고 

마지막 액션에 대해서만 동작을 실행한다

안심하는 느낌으로 쓰지만, 사실 여러번 누르면 그동안 컴퓨터가 다~~~ 처리해서

여러번요청 자체가 안되겠징

 

dispatch가 put

put이 redux state바꾸는 애.

draft 가 옆에 있는 stateㄹㄹ 받아서 그대로~~~ 쓰는거

 

그게 왜 비동기를 처리해야하는거지?

순간순간 요청들이 많다.

하나하나 기다

기다리지 않는 동안 로딩애니메이션을 보여주는거

 

 

로딩애니메이션 렌더

요청데이터 렌더

useSelector 렌더 (stater값 바뀌어서)

 

saga를 쓰면

똑같긴 한데 

끝났다는걸 받어 (_SUCCESS) 

그러면 손을 넣어서

스토어가 데이터를 받아서 

 

한번 사이클을 만들어 놓으면 어디서든 쓸 수 있다.

 

- redux pattern 추상화

SUCESS, FAILURE, 이런거 해줬던 것처럼

${name}_SUCCESS

요론식으로~

 

비동기동기 아직도 헷갈림

watch는 ㅇ왜 함??

어떤 액션이 dispatch됐는지 보는 함수.

걔를 take

데이터 가져오려면 걍 dispatch하면돼. 평소처럼

그러면 saga는 전체 액션을 보고 있는데 (watch)

거기서 take 해서 바로바로 가져오는거지

 

 

- saga pattern 추상화

try call put catch put

fetchEntity로 하면돼!

 

watchGetNotices 안써도 됨

 

물론 saga를 안쓰고 걍 redux 바꿔도 되긴 하는데..

오히려 토이프로젝트엔 독일수도 있는데

점점 프로젝트 커지면서 유용할거야

 

yield는 await 이랑 비슷해?

역할은 다른데 하는건 비슷해

yield 는 멈춘다. 그래서 비동기적으로 처리하기 수월하다.

 

 

es6 비구조화 할당 const {} = asdfasdf

 

saga는 store를 바꾸는게 아니라

dispatch를 하는거야 이건 원래 알고 있는거지

saga: 지맘대로 동작하는 컴포넌트가 하나 더 있다!! 라고 생각ㄱ

 

 

 

 

 

https://dailyhotel.io/how-to-use-redux-saga-63a6078c74b3

 

https://begin-redux.vlpt.us/07-immer.html

 

https://medium.com/@FourwingsY/redux-redux-saga-%EB%A1%9C-async-%EB%8B%A4%EB%A3%A8%EA%B8%B0-b7b9a9110356

 

https://jeonghwan-kim.github.io/dev/2019/07/22/react-saga-ts-1.html

 

 

 

 

 

 

===

https://medium.com/@han7096/redux-saga%EC%97%90-%EB%8C%80%ED%95%98%EC%97%AC-5e39b72380af

 

 

ducks 패턴?