React/NodeBird(ZeroCho)

take 시리즈, throttle 알아보기

느리지만 꾸준하게 2021. 10. 29. 15:54

아래코드 yield take의 단점은 일회용이라는 것이다. 그래서 한번 LOG_IN_REQUEST 하면 로그인 실행되고 LOG_OUT_REQUEST 하면 로그아웃 실행되고 한다. 그런데 take를 써서 한번밖에 받지 않으니까 한 번 로그인 했다가 한번 로그아웃하면 그 다음 로그인 할 때는 이벤트 리스너가 사라져 버리게 된다. 

// sagas 폴더안에 index.js

function* watchLogin() {
  yield take("LOG_IN_REQUEST", logIn);
}

function* watchLogout() {
  yield take("LOG_OUT_REQUEST", logOut);
}

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

그러므로 while로 감싼 것이다. while로 감싸면 무한하게 실행을 할수있다. 단 일반 while true와는 다르게 컴퓨터는 먹통이 되지 않고 그때그때 실행이 되고 다음 반복문에서 다음꺼 실행되기를 기다린다.(while take는 동기적으로 동작하지만 takeEvery는 비동기로 동작한다는 차이가 있다.)

function* watchLogout() {
  while (true) {
    yield take("LOG_OUT_REQUEST", logOut);
  }
}

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

while 대신에 takeEvery가 대신 쓰인다. 반복문 쓰는게 직관적이지도 않고 코드 상으로도 보기가 않좋으니까 takeEvery라는 것을 쓴다.

import { all, fork, call, takeEvery, put } from "redux-saga/effects";

function* watchLogin() {
    yield takeEvery("LOG_IN_REQUEST", logIn);
}

function* watchLogout() {
  yield takeEvery("LOG_OUT_REQUEST", logOut);
}

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

takeLatest도 있다. 클릭 실수를 두번 했을 때 즉 로그인 버튼을 두번 눌리는 경우가 있다. 그럴때 takeEvery로 해놓으면 두번눌렀으니까 진짜로 두번 실행되어 같은 게시글이 두번 업로드가 된다. takeLatest로 해놓으면 앞에 실수로 눌렀던거는 한번 무시되고 마지막꺼만 실행이 된다.(100번을 동시에 눌러도 99개 무시되고 마지막꺼만 실행됨) 만약 첫번째꺼만 하고싶으면 takeLeading이 있다. 

보통은 takeLatest를 많이 해놓는다.(완료된거는 가만히 놔두고 동시에 로딩중인것만 앞에거를 취소한다.)

// takeLatest
import { all, fork, call, takeLatest, put } from "redux-saga/effects";

function* watchLogin() {
  yield takeLatest("LOG_IN_REQUEST", logIn);
}

function* watchLogout() {
  yield takeLatest("LOG_OUT_REQUEST", logOut);
}

function* watchAddPost() {
  yield takeLatest("ADD_POST_REQUEST", addPost);
}


// takeLeading
import { all, fork, call, takeLeading, put } from "redux-saga/effects";

function* watchLogin() {
  yield takeLeading("LOG_IN_REQUEST", logIn);
}

function* watchLogout() {
  yield takeLeading("LOG_OUT_REQUEST", logOut);
}

function* watchAddPost() {
  yield takeLeading("ADD_POST_REQUEST", addPost);
}

takeLatest를 그림으로 설명을하면 front단에서 request 요청을 두번하면 back단에서 response 응답을 하나 취소 하는 것이다.(요청 두번은 그대로 되어 서버에는 데이터가 두번 저장이 된다. 서버쪽에서 반드시 같은데이터가 저장되어있지는 않은지 검사를 해줘야 한다.) 요청까지는 취소를 못한다는 것을 알자.(새로고침을 하면 똑같은 글 두개가 뜬다.)

takeLatest원리

throttle라는 것이 있는데 2초동안은 postRequest를 한번만 실행을 할 수 있다. 즉 throttle라는 것을 쓰면 요청 보내는 것 까지 몇초 제한둬서 그 시간안에는 한번만 보내도록 하게하는 이펙트이다. 

function* watchAddPost() {
  yield throttle("ADD_POST_REQUEST", addPost, 2000);
}

하지만 대부분의 경우에는 takeLatest를 쓰고 만약 두번 눌렀다 그러면 서버에서 검사를 해서 돌려주도록 한다. 첫번째 데이터는 등록을 해도 두번째 데이터에서는 "중복된 데이터를 등록하였습니다" 이렇게 해서 취소하여 막도록 한다. 즉 서버쪽에서 검증을 하는 편이여서 프론트에서는 맘놓고 takeLatest를 쓰자.

요청이 너무많이 와서 DDoS처럼 공격이 된다 싶으면 throttle를 써서 요청이 아예 안가게 막는다. 

 

redux-thunk는 단순히 아래코드 하나만을 제공해 주었다. put 같은거 dispatch를 한방에 여러번 할 수 있게 해줌

function* logOut() {
  try {
    const result = yield call(logOutAPI);
    yield put({
      type: "LOG_OUT_SUCCESS",
      data: result.data,
    });
  } catch (err) {
    yield put({
      type: "LOG_OUT_FAILURE",
      data: err.response.data,
    });
  }
}

redux-saga에서는 takeLatest takeEvery takeLeading takeMaybe throttle debounce 이러한 이펙트들을 다 미리 구현을 해놓았다. thunk 쓰다보면 이런 이펙트들은 우리가 직접 구현을 해줘야 한다.

그리고 서버를 구현하기 전까지 delay로 비동기적인 효과를 넣어주자.

import { all, fork, call, takeLatest, put, delay } from "redux-saga/effects";

function* logIn(action) {
  try {
    // const result = yield call(logInAPI, action.data);
    yield delay(1000);
    yield put({
      type: "LOG_IN_SUCCESS",
    });
  } catch (err) {
    yield put({
      type: "LOG_IN_FAILURE",
      data: err.response.data,
    });
  }
}

function logOutAPI() {
  return axios.post("/api/logOut");
}

function* logOut() {
  try {
    // const result = yield call(logOutAPI);
    yield delay(1000);
    yield put({
      type: "LOG_OUT_SUCCESS",
      data: result.data,
    });
  } catch (err) {
    yield put({
      type: "LOG_OUT_FAILURE",
      data: err.response.data,
    });
  }
}

function addPostAPI(data) {
  return axios.post("/api/post".data);
}

function* addPost() {
  try {
    // const result = yield call(addPostAPI, action.data);
    yield delay(1000);
    yield put({
      type: "ADD_POST_SUCCESS",
      data: result.data,
    });
  } catch (err) {
    yield put({
      type: "ADD_POST_FAILURE",
      data: err.response.data,
    });
  }
}

 

그리고 throttle과 debounce의 차이점도 있는데 여기를 보자. 스크롤 같은거 내릴 때 스로틀링을 많이 쓰고 검색창에 타이핑을 할 때 한 글자 한 글자 타이핑 할 때마다 검색결과 바뀌면 정신없으니까 단어가 완성되면 요청 보내게 하는 것이 디바운싱이다.

  • 쓰로틀링: 마지막 함수가 호출된 후 일정 시간이 지나기 전에 다시 호출되지 않도록 하는 것
  • 디바운싱: 연이어 호출되는 함수들 중 마지막 함수(또는 제일 처음)만 호출하도록 하는 것

 

 

 

 

 

 

 

 

 

 

 

<출처 조현영: [리뉴얼] React로 NodeBird SNS 만들기>

https://www.inflearn.com/course/%EB%85%B8%EB%93%9C%EB%B2%84%EB%93%9C-%EB%A6%AC%EC%95%A1%ED%8A%B8-%EB%A6%AC%EB%89%B4%EC%96%BC/dashboard

 

[리뉴얼] React로 NodeBird SNS 만들기 - 인프런 | 강의

리액트 & 넥스트 & 리덕스 & 리덕스사가 & 익스프레스 스택으로 트위터와 유사한 SNS 서비스를 만들어봅니다. 끝으로 검색엔진 최적화 후 AWS에 배포합니다., 새로 만나는 제로초의 리액트 노드버

www.inflearn.com

https://www.zerocho.com/category/JavaScript/post/59a8e9cb15ac0000182794fa

 

(JavaScript) 쓰로틀링과 디바운싱

안녕하세요. 이번 시간에는 쓰로틀링(throttling)과 디바운싱(debouncing)에 대해 알아보겠습니다. 원래 예정에 없던 강좌이지만 요청을 받았기 때문에 써봅니다. 프로그래밍 기법 중 하나입니다(아니

www.zerocho.com

 

'React > NodeBird(ZeroCho)' 카테고리의 다른 글

eslint로 프로젝트 점검하기  (0) 2021.10.31
saga 쪼개고 reducer와 연결하기  (0) 2021.10.29
saga 이펙트 알아보기  (0) 2021.10.29
saga 설치 & generator 이해하기  (0) 2021.10.28
redux-thunk 이해하기  (0) 2021.10.28