본문 바로가기

개발/React.js

[React.js] React-saga의 이펙트 fork / call / take / takeEvery / takeLatest / throttle

fork와 call의 차이점은 무엇일까?
 
fork는 비동기 함수 호출
call은 동기 함수 호출
 
call을 하면 logInAPI가 Return할 때까지 기다려서 Result에 넣어준다.
반대로, fork를 하면 요청 보내고 결과와 상관 없이 바로 다음 것을 실행해버린다.
 
call인 경우에는 axios.then을 하는 것이다.
yield가 await이랑 비슷하다.
 
import { all, fork, call, put, take } from 'redux-saga/effects';
import axios from 'axios';

function logInAPI() {
    // Generator가 아니므로 * 붙이면 안 된다.
    return axios.post('/api/login')
}

// 요청이 성공/실패 할 수 있으므로 try-catch
function* logIn() {
    try {        
        const result = yield call(logInAPI) // 결과값을 받는다.
        axios.post('/api/login')
            .then((result) => {
                // 결과를 받아서 
                yield put({
                    type: 'LOG_IN_SUCCESS',
                    data: result.data
                });
            });
        
    } catch (err) {
        yield put({
            type: 'LOG_IN_FAILURE',
            data: err.response.data
        });
    }
}


function* watchLogIn() {
    // LOG_IN이라는 액션이 실행될 때까지 기다리겠다. (EventListener 역할)
    yield take('LOG_IN_REQUEST', logIn);
}

function* watchLogOut() {
    yield take('LOG_OUT_REQUEST');
}

function* watchAddPost() {
    yield take('ADD_POST_REQUEST');
}

export default function* rootSaga() {
    // 배열 안에 것들을 모두 실행시켜준다.
    // fork : 함수 실행
    // fork / call은 Generator 기능이지만 서로 다른 것이다. (명확한 이해 필요)
    yield all([
        fork(watchLogin),
        fork(watchLogOut),
        fork(watchAddPost),
    ]);
}

// put : 액션을 Dispatch

 

 

자, 그러면 굳이 call과 yield를 쓰는 이유는 무엇일까?

 

그 이유는 redux-saga가 테스트 할때 yield 특성으로 인해 편리하기 때문이다.

 

generator를 쓰면 yield가 있는 곳마다 한 줄 한 줄 실행하며 돌려볼 수 있게 때문에

매우 편리하다.

 

 

Take의 특성

 

한 번 사용하면 사라져버린다.

function* watchLogIn() {
    // LOG_IN이라는 액션이 실행될 때까지 기다리겠다. (EventListener 역할)
    yield take('LOG_IN_REQUEST', logIn);
}

function* watchLogOut() {
    yield take('LOG_OUT_REQUEST', logOut);
}

function* watchAddPost() {
    yield take('ADD_POST_REQUEST', addPost);
}

이것을 해결하는 방법은?

 

function* watchLogIn() {
    // LOG_IN이라는 액션이 실행될 때까지 기다리겠다. (EventListener 역할)
    while (true) {
        yield take('LOG_IN_REQUEST', logIn);
    }
}

function* watchLogOut() {
    while (true) {
        yield take('LOG_OUT_REQUEST', logOut);
    }
}

function* watchAddPost() {
    while (true) {
        yield take('ADD_POST_REQUEST', addPost);
    }
}

while로 감싸준다.

그러면 진정한 EventListener처럼 구동된다.

 

* while take는 동기적으로 동작하지만,

takeEvery는 비동기로 동작한다.

 

But, While문으로 계속 감싸주면 가독성도 좋지 않고

코드 라인 수가 늘어난다.

이 부분을 극복하기 위해 나온 이펙트가

'takeEvery'이다.

 

function* watchLogIn() {
    // LOG_IN이라는 액션이 실행될 때까지 기다리겠다. (EventListener 역할)
    yield takeEvery('LOG_IN_REQUEST', logIn);
}

function* watchLogOut() {
    yield takeEvery('LOG_OUT_REQUEST', logOut);
}

function* watchAddPost() {
    yield takeEvery('ADD_POST_REQUEST', addPost);
}

 

이 외에도

 

takeLatest

 

클릭 두 번 했을 때 처리.

버튼을 두 번 클릭 했을 때, takeEvery의 경우 2번 호출된다.

그러나, takeLatest로 처리하게 되면 마지막것만 처리된다.

100번을 클릭해도 앞에 99개는 무시되고 마지막 1개만 처리된다.

 

하지만 무조건적이지는 않다.

 

예외로 이미 완료됐다면 그것은 완료된 채로 놔두고,

그 다음에 실행된다.

 

앞에 것도 로딩중이고 뒤에 것도 로딩중일 경우

완료되지 않은 것이 있다면 그 때 구동된다.

 

< 치명적인 단점 >

front에서만 그렇게 생각하는 것이다.

server에서는 n번 요청이 가는 것이다.

(새로고침할 때 나타남...)

 

 

takeLeading

 

첫 번째 클릭만 실행.

 

 

throttle

function* watchAddPost() {
	yield throttle('ADD_POST_REQUEST', addPost, 10000);
}

2초로 설정해놓는다면,

2초동안 딱 한 번만 실행하는 것이다.