처음 만난 리덕스 (Redux) 문서


12.4 redux-saga 사용 방법

지금부터는 redux-saga의 기본적인 사용 방법에 대해서 알아보도록 하겠습니다.

가장 먼저 할 일은 필요한 Saga를 만드는 것입니다.

import { call, put, takeLatest } from 'redux-saga/effects';
import apiClient from '../api/client';

function* createPost(action) {
    try {
        const { title, content } = action.payload;
        const newPost = yield call(apiClient.post, '/api/post', title, content);

        yield put({ type: 'CREATE_POST_SUCCEEDED', payload: { newPost } });
    } catch (error) {
        yield put({ type: 'CREATE_POST_FAILED', error: true });
    }
}

function* fetchPost(action) {
    try {
        const post = yield call(
            apiClient.get,
            '/api/post',
            action.payload.postId
        );

        yield put({ type: 'FETCH_POST_SUCCEEDED', payload: { post } });
    } catch (error) {
        yield put({ type: 'FETCH_POST_FAILED', error: true });
    }
}

function* postSaga() {
    yield takeLatest('CREATE_POST_REQUESTED', createPost);
    yield takeLatest('FETCH_POST_REQUESTED', fetchPost);
}

export default postSaga;

이 코드는 post와 관련된 Saga를 나타낸 예시 코드입니다.

코드를 보면 이름은 postSaga라고 지었고, 총 두 가지 Action Type에 대해서 takeLatest() Effect Creator를 사용하고 있는 것을 볼 수 있습니다.
post를 생성하는 비동기 요청을 처리하기 위한 CREATE_POST_REQUESTED Action과 post를 받아오는 비동기 요청을 처리하기 위한 FETCH_POST_REQUESTED Action입니다.

그리고 위에 나온 두 개의 Generator Function은 실제로 비동기 요청을 처리하는 역할을 합니다.
각각 call()put() 함수를 사용해서 비동기 요청을 처리하고 처리 결과를 Action객체로 만들어 Dispatch하게 됩니다.

이 구조가 기본적인 형태의 Saga라고 보면 되고, 각각의 Action 분류에 대해서 별도의 Saga파일을 만들어서 관리하는 것이 좋습니다.

위 예시에서는 postSaga라는 하나의 Saga파일만 등장합니다.
하지만 프로젝트 규모가 커질수록 수많은 Saga들이 생기게 됩니다.
rootSaga는 이러한 Saga들을 합쳐서 하나로 만든 것입니다.
Reducer들을 모두 합친 rootReducer를 Redux Store에 연동하는 것처럼, Saga들도 하나로 합쳐서 rootSaga 형태로 사용합니다.

import { all } from 'redux-saga/effects';
// Sagas
import postSaga from './postSaga';
import commentSaga from './commentSaga';

function* rootSaga() {
    yield all([
        postSaga(),
        commentSaga(),
    ]);
}

export default rootSaga;

위 코드는 rootSaga를 만드는 예시 코드를 나타낸 것입니다.
이 때 redux-saga/effects 패지키에 포함되어 있는 all() 이라는 함수를 사용합니다.
all() 함수는 파라미터로 Effect들이 들어있는 배열을 받게 되며, 이렇게 만들어진 rootSaga는 모든 Saga들을 병렬적으로 한 번에 실행시킬 수 있는 단일 시작점이 됩니다.

여기에서는 postSagacommentSaga, 이렇게 2개의 Saga를 all() 함수에 넣어주었습니다.
이 때 주의할 점은 import해온 각각의 Saga가 Generator Function이기 때문에, 이걸 그냥 넣으면 안되고 호출을 해서 만들어진 Generator Object를 넣어야 한다는 점입니다.

Saga를 연동하기 위한 마지막 단계는 Redux Store에 Saga middleware를 연동하는 과정입니다.

import { createStore, applyMiddleware } from 'redux';
import createSagaMiddleware from 'redux-saga';
import rootReducer from './reducers';
import rootSaga from './sagas';

// Saga middleware 생성
const sagaMiddleware = createSagaMiddleware();
// Saga middleware가 적용된 Redux Store 생성
const store = createStore(rootReducer, applyMiddleware(sagaMiddleware));
// 이후 rootSaga 실행
sagaMiddleware.run(rootSaga);

이 코드는 Redux Store에 Saga middleware를 연동하는 과정을 나타낸 코드입니다.
위쪽에서 rootReducer와 함께 앞에서 만든 rootSagaimport 해온 것을 볼 수 있습니다.

먼저 redux-saga 패키지의 createSagaMiddleware() 함수를 호출해서 Saga middleware를 생성합니다.
그리고 이렇게 생성된 Saga middleware를 applyMiddleware() 함수에 넣어서 createStore() 함수의 두 번째 파라미터로 넣어줍니다.
그러면 Saga middleware가 적용된 Redux Store가 생성됩니다.

마지막으로 sagaMiddlewarerun() 함수를 사용해서 꼭 rootSaga를 실행해주어야 합니다.
그렇지 않으면 Saga middleware만 적용되어 있을뿐 Saga 작업 대기열에 아무런 Effect가 들어가지 않는 상태가 됩니다.

이렇게 Redux Store에 Saga middleware가 연동되었다면, 이제 Saga와 관련된 Action을 Dispatch해서 원하는 시점에 원하는 Effect를 실행시킬 수 있습니다.

지금까지 redux-saga의 기본적인 사용 방법에 대해서 살펴보았습니다.


마지막 업데이트: 2023년 07월 14일 00시 00분

이 문서의 저작권은 이인제(소플)에 있습니다. 무단 전재와 무단 복제를 금합니다.