출처 : 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
* redux-thunk
- 리덕스의 미들웨어
- 리덕스가 비동기를 dispatch할 수 있도록 도와줌 하나의 액션에서 dispatch를 여러번 할 수 있음
(하나의 비동기 액션 안에 여러 개의 동기 액션을 넣을 수 있음)
- 참고 : https://developerntraveler.tistory.com/144
- state : 특정 시점의 app 상태
- actions : 어플리케이션에서 "무언가 일어나는 것을 설명한 이벤트", action은 객체로 가지나 thunk에서는 action을 function으로 둘 수 있음
- dispatch : action을 발생시키는 것(이벤트 트리거), 상태를 업데이트하는 유일한 방법은 store.dispatch() 메서드를 부르고 action 객체를 넘겨주는 것
- 정리 : 무언가 발생 시, store은 어떤 일이 발생하는지 알고 싶어함
= reducer은 이벤트 리스너와 같은 역할을 하며 관련된 action을 받으면 response로 state를 업데이트함
* redux-saga
- 딜레이(몇 초뒤에 실행되게 하는 것)를 미리 만들어서 제공 가능
- 여러 effect들 존재(all, fork, put, delay, debounce, throttle, takeLatest, takeMaybe 등)
- 제너레이터(function*) 존재
- 멈추지 않는 제너레이터가 존재할 수 있음(무한 반복이 계속 반복되는 것이 아닌 매번 중단되는 부분들이 존재하는 무한반복)
- rootSaga를 만들고 fork에 비동기 액션을 집어 넣음
- yeild를 사용해야 함 : redux-saga는 제너레이터(한줄씩 돌려 볼 수 있음)로 인해 테스트 하기 편함, 동작이 제대로 작동되는지 확인 가능
const gen = function* () {
while(true) {
yield 무한
}
}
* redux-thunk와 redux-saga 차이점
- 비동기 액션(sagas/index.js의 type들)
= thunk는 비동기 액션을 직접 실행, saga는 비동기 액션 크리에이터가 직접 실행되는 것이 아닌 이벤트 리스너와 같은 역할
= LOG_IN 액션이 들어오면 function* logIn() (제너레이터) 실행되도록 함
= saga는 effect 앞에는 yield를 붙일 것
= saga는 리턴값을 result에 받음
= yield put : dispatch 역할(액션을 dispatch)
= yield가 await과 비슷함
* 제너레이터
- 함수(특별한 역할), yield가 나오면 멈춤(중간점이 있는 함수)
- 일반 함수는 함수를 실행하다가 중간에 멈추고 싶을 때 멈추는 것이 불가능하나,
제너레이터를 쓰고 yield를 넣고 next를 중간에 호출하지 않으면 멈춤
const gen = function* () {}
gen() //아무것도 안 나옴
gen().next() //이래야 실행됨
const gen = function* () {
console.log(1);
yield;
console.log(2);
yield;
console.log(3);
yield 4;
}
const generator = gen();
generator //gen {<suspended>}
generator.next() //1 나오고 멈춤
generator.next() //2 나오고 멈춤
generator.next() //3 나오고 멈춤
generator.next() //4 나오고 멈춤
* redux-saga effect
- all : 배열을 받음, 배열 안에 들어있는 것들(fork, call)을 한 방에 실행됨
- fork, call : 함수를 실행
- fork와 call은 차이점 있음 = fork 비동기 함수 호출, call 동기 함수 호출
- call은 logInAPI가 return 할 때까지 기다려서 result에 넣음, 블로킹
(axios.post("/api/login").then(() => { yield.put({}) })과 마찬가지임)
- fork는 비동기이기 때문에 결과를 기다리지 않고 바로 다음 것이 실행됨, 논 블로킹(await이 빠진 역할)
(axios.post("/api/login")과 마찬가지임)
- takeLatest
= 클릭 실수로 2번 했을 때(마우스), takeEvery는 2번 실행됨
= takeLatest는 실수로 눌렀던 것은 무시되고 마지막 누른 것만 실행
= 이미 완료된 것은 놔두고, 완료되지 않은 것은 없앰(동시에 로딩 중인 것만)
= front에서만 생각함, 서버에는 2번 저장되는 것이나 마찬가지(검사가 필요)
= (단점) 응답을 취소하는 것이지 요청은 취소하지 못함
- takeLeading
= 처음 누른 것만 실행
- throttle(요청이 너무 많아지는 경우)
= 요청 보내는 것에 제한시간을 둚(특수한 경우)
* 블로킹 | 논블로킹
= A 함수가 B함수 호출 시 제어권을 어떻게 처리하냐에 따라 다름
- 블로킹 : A함수가 B함수 호출 시 제어권을 A가 호출한 B함수에 넘겨줌, A는 B에게 제어권을 넘겨주었기에 함수 실행이 잠시 멈춤, B는 실행이 끝나면 A에게 제어권을 돌려줌
- 논블로킹 : A함수가 B함수를 호출해도 제어권은 자신(A)이 그대로 가지고 있음, B 함수를 호출한 이후에도 자신의 코드 계속 실행
* 동기 | 비동기
= 호출되는 함수의 작업 완료 여부를 신경쓰는지 여부
- 동기 : 함수 A가 함수 B를 호출한 뒤, 함수 B의 리턴값을 계속 확인하면서 신경 쓰는 것
- 비동기 : 함수 A는 함수 B를 호출 한 후 함수 B의 작업 완료에 신경 쓰지 않음
* redux-saga login 예시
function logInAPI(data) { //제너레이터가 아님(요청을 보내므로)
return axios.post("/api/login", data);
}
function* logIn(action) { //요청의 결과를 받음, 요청 실패를 대비해 try catch문 작성
try {
console.log("saga logIn");
// const result = yield call(logInAPI)
yield delay(1000);
yield put({
type: LOG_IN_SUCCESS,
data: action.data, //성공 결과 : result.data
});
} catch (err) {
console.log(err);
yield put({
type: LOG_IN_FAILURE,
error: err.response.data, //실패 결과 : err.response.data
});
}
const result = yield call(logInAPI)
//logInAPI(action.data)가 아닌 call(logInAPI, action.data)로 써야 함
위의 예시의 logInAPI에서 매개변수 인수 쓰려면
function logInAPI(data, a, b, c) {} 만들고
const result = yield call(logInAPI, action.data, 'a', 'b', 'c') 이렇게 사용해야 함
* throttling(쓰로틀링)과 debouncing(디바운싱) 차이
- 둘다 웹에서 발생하는 이벤트 제어
- 쓰로틀링 : 마지막 함수가 호출된 후 일정 시간이 지나기 전에 다시 호출되지 않도록 하는 것
- 디바운싱 : 연이어 호출되는 함수들 중 마지막 함수(또는 제일 처음)만 호출되도록 하는 것
- 둘의 차이점 : 디바운싱은 설정한 특정 시간동안 이벤트 발생하지 않았을 때 맨 마지막 이벤트 딱 한 번 발생, 쓰로틀링 : 설정한 특정 주기로 계속 실행