[Redux] Redux-saga를 알아보자

2023. 1. 3. 21:01·🍞 FrontEnd/React

Redux-saga란?

공식문서에서는 Redux-saga를 다음과 같이 소개하고 있다.

Redux-saga는 react/redux 애플리케이션의 사이드 이펙트, 예를 들면 데이터 fetching이나 브라우저 캐시에 접근하는 순수하지 않은 비동기 동작들을 더 쉽고 보기 좋게 만드는 것을 목적으로 하는 라이브러리이다.

 

redux에서는 action을 발생시키면 reducer를 통해 state를 변경시켜 store를 갱신했다.

 

 

Redux-saga는 action과 reducer 사이에서 흐름을 제어하는 미들웨어이다. 이 중간에서 Redux-saga는 action이 발생하면 reducer가 액션을 처리하기 위해 다양한 작업을 할 수 있다.

 

다양한 작업들의 예시는 이렇다.

- 기존 요청을 취소 처리하거나 불필요한 중복 요청을 방지할 수 있다.

- 비동기 작업을 처리하는데 효과적이다.

- 특정 액션이 발생했을 때 다른 액션을 발생시키거나, 리덕스와 관계없는 코드 실행시 사용한다.

 

즉, Redux-saga를 이용하면 보다 간편하면서도 깊게 state의 흐름을 제어할 수 있다.

 

Redux-saga를 도입하는 이유

1️⃣ api 호출 로직을 효율적으로 관리할 수 있다.

오로지 비동기 작업만을 위해 redux-saga를 쓰는 것은 크게 효율적이지 않다고 생각한다. async/await 라는 훌륭한 기능으로 비동기 동작들을 처리할 수 있는데 굳이 코드량을 늘려가며 redux-saga를 도입할 이유가 있을까?

 

하지만 redux-saga에서는 비동기 동작에 대해 더 세부적인 컨트롤이 가능하다. 예를 들면 사용자의 부주의로 동일한 api를 여러번 호출할 경우, 가장 마지막 호출의 response만 받아오도록 제어할 수 있다.

 

무엇보다 api 호출 로직을 saga에서 관리하면서, Presentational 컴포넌트와 Container 컴포넌트의 명확한 분리가 가능하다. 또한 같은 api를 다른 페이지에서 호출시 같은 코드를 두번 적어줘야 했다면, api 호출 로직을 Redux-saga로 관리하면서 selector로 데이터만을 간편하게 가져올 수 있게 된다.

 

2️⃣ callback 함수를 action payload로 넘길 수 있다.

공통으로 사용할 모달창을 구현할 때, 이 모달창에서 확인 버튼을 누르면 모달창을 호출한 페이지에서 그 사실을 알아야 하는데 그 로직을 어떻게 구현할까?

모달창을 열 때 페이지의 callback 함수를 함께 넘겨주는 것이 가장 좋은 방법이지만, redux에서는 callback을 state값으로 저장하는 것을 권장하지 않는다.

 

이때 redux-saga를 사용하면, callback값을 redux-saga에서 처리해주기 때문에 action payload로 callback을 넘길 수 있게 된다.

 

Redux-Saga 동작 원리

Redux-saga는 제너레이터 함수 문법을 기반으로 비동기 작업을 관리한다.

Redux-saga는 우리가 dispatch하는 action을 모니터링 해서 그에 따라 필요한 작업을 따로 수행할 수 있다.

function* watchGenerator(){
    console.log("모니터링 중...");
    let prevAction = null;
    while(true){
        const action = yield;
        console.log("이전 액션: ", prevAction);
        prevAction = action;

        if(action.type === "HELLO"){
            console.log("안녕");
        }
    }
}
const watch = watcjGenerator();
watch.next();
// 모니터링 중...
// { value: undefined, done: false}
watch.next({type: "TEST"});
// 이전 액션: null
// { value: undefined, done: false}
watch.next({ type: "HELLO"});
// 이전 액션: {type: "TEST"}
// 안녕
// {value: undefined, done: false}

위 코드는 Redux-saga가 실제로 action을 어떻게 캐치하고 구분하는지를 비슷하게 흉내낸 코드다.

 

redux-saga의 헬퍼 함수

delay
설정된 시간 이후에 resolve를 하는 Promise 객체를 리턴한다.

put
특정 액션을 dispatch한다. (ex: put({type: 'INCREMEMT}))

call
주어진 함수를 실행한다. (ex: call(delay, 1000))
미들웨어가 Promise의 resolve를 기다리게 하기 때문에 동기함수(주로 api 호출)에 사용한다.

take
들어오는 특정 액션을 처리한다. 한번 실행되고 이벤트가 삭제된다.

takeEvery

모든 리퀘스트에 대해 task를 실행한다.

function* watchFetchData(){
    yield takeEvery('FETCH_REQUESTED', fetchData)
}

만약 fetchData task가 시작되었을때 이미 이전 task가 실행중이라면, 이전 task는 자동으로 취소된다.

 

fork

백그라운드에서 task가 실행된다.

function* loginFlow() {
  while (true) {
    const {user, password} = yield take('LOGIN_REQUEST')
    const token = yield call(authorize, user, password)
    if (token) {
      yield call(Api.storeItem, {token})
      yield take('LOGOUT')
      yield call(Api.clearItem, 'token')
    }
  }
}

위와 같은 로그인 로직이 있다. 이 로직은 로그인을 하고('LOGIN_REQUEST'), 사용자 인증을 거치면 'LOGOUT' task를 기다린다. 만약 'LOGIN_REQUEST'가 실행되고, token을 받아오는 중에 사용자가 'LOGOUT' task를 실행한다면 어떻게 될까? 'LOGOUT' task는 무시된다. call은 봉쇄(blocking) effect라서 호출이 종료되기 전까지는 아무것도 수행할 수 없기 때문이다.

function* loginFlow() {
  while (true) {
    ...
    try {
      // non-blocking call, what's the returned value here ?
      const ?? = yield fork(authorize, user, password)
      ...
    }
    ...
  }
}

이럴때 fork를 사용해주면, task는 백그라운드에서 시작되고, 호출자는 fork된 task가 종료될 떄까지 기다리지 않고 플로우를 계속해서 진행한다.
❗ 단, fork는 백그라운드에서 실행되기 때문에 token을 받아올 수 없다. 이럴 경우에는 token을 authorize 안에서 받아와야한다.

 

function* authorize(user, password) {
  try {
    const token = yield call(Api.authorize, user, password)
    ...
  } catch(error) {
    ...  
    }
}

function* loginFlow() {
  while (true) {
    ...    
        yield fork(authorize, user, password)
    ...
  }
}

그러면 위와 같은 로직이 된다.
( 만약 finally 구간에서 제너레이터가 취소된건지 확인이 필요하다면 yield cancelled() 으로 확인가능하다. )

 

cancle
fork된 task를 취소시킨다. (ex: yield cancel(task))
제너레이터를 finally 구간으로 가게한다. 이때 취소한 task 하위에 다른 task가 포함되어 있다면 모두 취소된다.

all
이 함수를 사용해서 제너레이터 함수를 배열의 형태로 넣어주면, 제너레이터 함수들이 병행적으로 동시에 실행되고, 전부 resolve될때까지 기다린다. (Promise.all과 비슷하다.)

 

 

 

10. redux-saga · GitBook

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

react.vlpt.us

 

저작자표시 (새창열림)

'🍞 FrontEnd > React' 카테고리의 다른 글

[React] React-query에 대해서 알아보자  (0) 2023.01.05
[React] React-slick 캐러셀 구현 모듈  (0) 2023.01.04
[React] 구독하기(팔로워, 팔로우) 기능  (0) 2022.12.29
[React] 비디오 업로드(multipart/form-data) 기능  (0) 2022.12.29
[React] Expected an assignment or function call and instead saw an expression no-unused-expressions 오류  (0) 2022.12.28
'🍞 FrontEnd/React' 카테고리의 다른 글
  • [React] React-query에 대해서 알아보자
  • [React] React-slick 캐러셀 구현 모듈
  • [React] 구독하기(팔로워, 팔로우) 기능
  • [React] 비디오 업로드(multipart/form-data) 기능
박빵이
박빵이
2025년에도 갓생살기
  • 박빵이
    기억보다 기록
    박빵이
  • 전체
    오늘
    어제
    • 분류 전체보기 (337)
      • 🍞 FrontEnd (97)
        • HTML+CSS (4)
        • JavaScript (17)
        • TypeScript (4)
        • React (52)
        • Next.js (2)
        • Android (15)
      • 🍞 BackEnd (24)
        • Java (15)
        • Node.js (6)
        • Spring (1)
      • 🍞 Cloud & Infra (0)
        • AWS SAA (0)
        • Microsoft Azure (0)
      • 🍞 Algorithm (147)
        • C++ (4)
        • Baekjoon (41)
        • Programmers (97)
      • 🍞 Computer Science (18)
        • 운영체제 (1)
        • 데이터 통신 (6)
        • 네트워크 (6)
        • 데이터베이스 (1)
      • 🍞 대외활동 & 부트캠프 (42)
        • 삼성 청년 SW 아카데미 (1)
        • LG유플러스 유레카 (0)
        • 한국대학생IT경영학회 (1)
        • IT연합동아리 UMC (17)
        • 길벗 블로깅 멘토 (18)
        • IT연합동아리 피로그래밍 (3)
        • 개발 컨퍼런스 (2)
  • 블로그 메뉴

    • Admin
  • 링크

    • GitHub
  • 인기 글

  • 태그

    umc
    유니온파인드
    위상정렬
    알고리즘
    Android
    백준
    길벗 블로깅 멘토
    코딩자율학습
    C++
    코틀린
    level1
    길벗 블로깅 멘토링
    JavaScript
    react
    Front
    안드로이드
    map
    Java
    프로그래머스
    level2
  • 최근 댓글

  • 최근 글

  • hELLO· Designed By정상우.v4.10.3
박빵이
[Redux] Redux-saga를 알아보자
상단으로

티스토리툴바