NEXTJS 로 twitter클론 해보기(8. Redux middleware )
리덕스 미들웨어
리덕스 미들웨어를 사용하면 액션이 디스패치 된 다음, 리듀서에서 해당 액션을 받아와서 업데이트하기 전에 추가적인 작업을 할 수 있습니다.
미들웨어 만들어보고 이해하기
리덕스 미들웨어의 기본 템플릿
const middleware = store => next => action => {
// 하고 싶은 작업...
}
첫번째 store
는 리덕스 스토어 인스턴스입니다. 이 안에 dispatch
, getState
, subscribe
내장함수들이 들어있습니다.
두번째 next
는 액션을 다음 미들웨어에게 전달하는 함수입니다. next(action)
이런 형태로 사용합니다. 만약 다음 미들웨어가 없다면 리듀서에게 액션을 전달해줍니다. 만약에 next
를 호출하지 않게 된다면 액션이 무시처리되어 리듀서에게로 전달되지 않습니다.
세번째 action
은 현재 처리하고 있는 액션 객체입니다.
미들웨어 직접 작성해보기
logger middleware
const myLogger = store => next => action => {
console.log(action); // 먼저 액션을 출력합니다.
const result = next(action); // 다음 미들웨어 (또는 리듀서) 에게 액션을 전달합니다.
return result; // 여기서 반환하는 값은 dispatch(action)의 결과물이 됩니다. 기본: undefined
};
export default myLogger;
thunk middleware
액션이 함수 타입이면 해당 함수의 인자에 store.dispatch, store.getState을 넣어줍니다. 이렇게하면
해당 함수에서 dispatch를 쓸수 있고 이말은 한번의 액션으로 dispatch를 여러번 할 수 있게 해준다는 것입니다.
const thunk = store => next => action =>
typeof action === 'function'
? action(store.dispatch, store.getState)
: next(action)
redux-saga 사용해보기
설치
npm i redux-saga
적용
store/configureStore.js
import { createWrapper } from "next-redux-wrapper";
import { applyMiddleware, compose, createStore } from "redux";
import { composeWithDevTools } from "redux-devtools-extension";
import createSagaMiddleware from "redux-saga";
import reducer from "../reducers/index";
import rootSaga from "../sagas/index";
const configureStore = () => {
const sagaMiddleware = createSagaMiddleware(); //sagamiddleware만들기
const middlewares = [sagaMiddleware]; //sagamiddleware넣어주기
const enhancer =
process.env.NODE_ENV === "production"
? compose(applyMiddleware(...middlewares))
: composeWithDevTools(applyMiddleware(...middlewares));
const store = createStore(reducer, enhancer); //앱의 상태를 유지하는 Redux 스토어를 만듭니다
store.sagaTask = sagaMiddleware.run(rootSaga);
return store;
};
const wrapper = createWrapper(configureStore, {
debug: process.env.NODE_ENV === "development",
});
export default wrapper;
sages/index.js 파일 만들기
먼저 rootSaga를 만들줍니다.
redux-saga/effects에 있는 fork를 통해 argument에 들어있는 함수를 실행시켜줍니다.
all은 여러개의 함수넣을때 사용합니다.
export default function* rootSaga() {
yield all([
//all은 배열안에 들어가 있는것을 동시에(한방에?) 실행시킨다.
fork(watchLogin), //fork는 인자에 들어가있는 함수를 실행시킨다.
fork(watchLogOut),
fork(watchAddPost),
]);
}
rootSaga를 만든후 fork에 들어갈 함수를 정의해 줍니다.
take는 첫번째 argument에들어가는 액션이 실행될때까지 기다리겠다 라는 뜻입니다. (동기)
해당 액션이 실행되면 두번째 argument가 실행됩니다.
하지만 문제가 하나 있습니다. take는 해당 액션이 한번 실행되면 그다음엔 실행을 안합니다.
즉 딱 한번만 실행된다는거죠 이를 해결하기위해 while(true)를 사용해도 되지만 Saga에 있는 takeEvery(비동기)와 takeLatest를 사용하면 해결됩니다.
takeEvery는 말그대로 딱한번만 실행되는걸 계속 실행돠게 만들어주고
takeLatest는 계속 실행되게 만들어 줄 뿐만 아니라 사용자가 실수로 액션을 연속적으로 2번 눌렀을때를 대비하여 마지막 액션만 실행하게 해줍니다.
function* watchLogin() {
//take는 LOG_IN_REQUES이라는 액션이 실행될때까지 기다리겠다 라는 뜻 (동기)
//LOG_IN_REQUES이라는 액션이 실행되면 두번째 argument가 실행된다.
//일회용 딱 한번만 실행된다.
yield take("LOG_IN_REQUEST", login);
}
call역시 argument에 들어간 함수를 실행시킵니다. fork와 차이점은 call은 동기 fork는 비동기 입니다.
call의 두번째 argument값으로는 첫번째 argument로 넣어준 함수의 argument로 사용됩니다.
put을 통해 argument에 넣어준 action을 실행시켜줍니다
function loginAPI(data) {
return axios.post("/api/login", data);
}
function* login(action) {
try {
const result = yield call(loginAPI, action.data); //call fork 차이 fork는 비동기 call은 동기
yield put({
type: "LOG_IN_SUCCESS",
data: result.data,
});
} catch (err) {
yield put({
//put은 dispatch라고 생각하면 된다.
type: "LOG_IN_FAILURE",
data: err.response.data,
});
}
}
redux-saga파일 분리하기
sagas
index.js → 분리된 saga를 합쳐준다.
user.js → user에 관련된 saga만 적어준다.
post.js → post에 관련된 saga만 적어준다.
sagas/index.js
import { all, fork } from "redux-saga/effects";
import postSaga from "./post";
import userSaga from "./user";
export default function* rootSaga() {
yield all([
fork(postSaga),
fork(userSaga),
]);
}
sagas/user.js
import { all, fork, delay, takeLatest, put } from "redux-saga/effects";
import axios from "axios";
//이부분은 제너레이터 아니다.
// 서버 요청 부분
function loginAPI(data) {
return axios.post("/api/login", data);
}
function* login(action) {
try {
console.log("saga Login");
// const result = yield call(loginAPI, action.data); //call fork 차이 fork는 비동기 call은 동기
yield delay(1000);
yield put({
type: "LOG_IN_SUCCESS",
data: action.data,
});
} catch (err) {
yield put({
//put은 dispatch라고 생각하면 된다.
type: "LOG_IN_FAILURE",
data: err.response.data,
});
}
}
function logoutAPI() {
return axios.post("/api/logout");
}
function* logout() {
try {
//const result = yield call(logoutAPI); //call fork 차이 fork는 비동기 call은 동기
yield delay(1000);
yield put({
type: "LOG_OUT_SUCCESS",
});
} catch (err) {
yield put({
//put은 dispatch라고 생각하면 된다.
type: "LOG_OUT_FAILURE",
data: err.response.data,
});
}
}
function* watchLogin() {
//take는 LOG_IN_REQUES이라는 액션이 실행될때까지 기다리겠다 라는 뜻 (동기)
//LOG_IN_REQUES이라는 액션이 실행되면 두번째 argument가 실행된다.
//일회용 딱 한번만 실행된다.
//takeEvery는 비동기
console.log("takeLatest");
yield takeLatest("LOG_IN_REQUEST", login);
}
function* watchLogOut() {
yield takeLatest("LOG_OUT_REQUEST", logout);
}
export default function* userSaga() {
yield all([
fork(watchLogin), //fork는 인자에 들어가있는 함수를 실행시킨다.
fork(watchLogOut),
]);
}
Author And Source
이 문제에 관하여(NEXTJS 로 twitter클론 해보기(8. Redux middleware )), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://velog.io/@abc5259/NEXTJS-로-twitter클론-해보기8.-Redux-middleware저자 귀속: 원작자 정보가 원작자 URL에 포함되어 있으며 저작권은 원작자 소유입니다.
우수한 개발자 콘텐츠 발견에 전념 (Collection and Share based on the CC Protocol.)