๐Ÿฑ #11 React.js - Redux middleware

๋ฐ•์ค€์„ยท2022๋…„ 11์›” 14์ผ
0

React

๋ชฉ๋ก ๋ณด๊ธฐ
12/13
post-thumbnail

๐Ÿงฉ Redux middleware ๋ž€?

Redux๋Š” UI์—์„œ Action์ด ์ƒ๊ธฐ๊ณ  ์ด Action์„ dispatch๊ฐ€ ๋ฐ›์•„ ๋˜์ ธ์ฃผ๊ณ  Reducer๊ฐ€ ๋ฐ›์•„ ์ž‘์—… ์ง€์‹œ๋ฅผ return์„ ํ•˜๋ฉด state๊ฐ€ ๋ฐ›์•„์ฃผ๋Š” ์›๋ฆฌ์ด๋‹ค.

์ด Redux์—๊ฒŒ ๋‹จ์ ์ด ์žˆ๋‹ค. ๋‹จ์ ์ด ๋ฌด์—‡์ด๋ƒ๋ฉด ๋น„๋™๊ธฐ์  ์ฒ˜๋ฆฌ๋ฅผ ํ•˜์ง€ ๋ชปํ•˜๋Š” ๊ฒƒ์ด๋‹ค.

Redux๋Š” ๋™๊ธฐ์  ์ฒ˜๋ฆฌ๋ฅผ ํ•˜๊ธฐ ๋•Œ๋ฌธ์— ๋งŒ์•ฝ A๋ฅผ ๋ฐ”๊ฟ”๋‹ฌ๋ผ๊ณ  ์š”์ฒญํ•˜๋ฉด ์ฝ”๋“œ๊ฐ€ ์ˆœ์ฐจ์ ์œผ๋กœ ์‹คํ–‰๋˜๋ฉฐ ๋ฐ”๋กœ ๋ฐ”๊ฟ”์ค€๋‹ค. ๊ทธ๋ž˜์„œ API ์š”์ฒญ๊ฐ™์€ ๋น„๋™๊ธฐ์  ์ž‘์—…์ด ๋ถˆ๊ฐ€๋Šฅ ํ•˜๋‹ค๋Š” ๋‹จ์ ์ด ์žˆ๋‹ค.

์ด ๋‹จ์ ์„ ์ฒ˜๋ฆฌ ํ•˜๊ธฐ ์œ„ํ•ด ๋งŒ๋“ค์–ด์ง„ ๊ฒƒ์ด Redux middleware์ด๋‹ค.

Redux middleware๋Š” Redux ์›๋ฆฌ์™€ ๊ฐ™์ง€๋งŒ ์›๋ž˜๋ผ๋ฉด dispatch๊ฐ€ ๋˜์ ธ์ฃผ๋Š” Action์„ ๋ฐ”๋กœ Reducer๊ฐ€ ๋ฐ›์•˜์ง€๋งŒ Redux middleware๊ฐ€ ์ค‘๊ฐ„์— ๊ฑฐ์ณ๊ฐ„๋‹ค.
Redux middleware์—์„œ ํ•จ์ˆ˜๋ฅผ ๋งŒ๋“ค ์ˆ˜ ์žˆ์–ด ์—ฌ๊ธฐ์„œ ๋น„๋™๊ธฐ์  ์ฒ˜๋ฆฌ๋ฅผ ํ•˜๊ฒŒ ๋˜๊ณ  ๋น„๋™๊ธฐ์ฒ˜๋ฆฌ๊ฐ€ ๋๋‚˜๋ฉด ๋‹ค์‹œ dispatch๋กœ ๋˜์ ธ์ค˜ returnํ•ด state๊ฐ€ ๋ฐ›์•„์ฃผ๋Š” ์›๋ฆฌ์ด๋‹ค.

middle ware๋Š” ์–ด๋–ค ์ปจ์…‰์— ์ด๋ฆ„์ด๊ณ , ์ค‘๊ฐ„์— ๊ฐ€๋กœ์ฑˆ๋‹ค๋Š” ์˜๋ฏธ์ด๋‹ค.

Redux middleware์—๋Š” ๋‘ ๊ฐ€์ง€ ์ข…๋ฅ˜๊ฐ€ ์žˆ๋‹ค. redux-saga, redux-thunk๊ฐ€ ์žˆ๋‹ค.

์—ฌ๊ธฐ์„œ ์ ์šฉํ•  ๊ฒƒ์€ redux-thunk๋ฅผ ์ ์šฉํ•ด ๋ณผ ๊ฒƒ์ด๋‹ค.

๐Ÿงฉ Redux middleware(redux-thunk) ์„ค์น˜

redux-thunk์— ์„ค๋ช…์ด ๋˜์–ด ์žˆ๋‹ค.

๋จผ์ € ์ž‘์—…ํ•˜๊ณ ์ž ํ•˜๋Š” ํ”„๋กœ์ ํŠธ์— Redux์™€ React-Redux๋ฅผ ์„ค์น˜ํ•œ๋‹ค.

npm install redux react-redux

yarn add redux react-redux

๊ทธ ํ›„ redux-thunk๋ฅผ ์„ค์น˜ํ•œ๋‹ค.

npm install redux-thunk

yarn add redux-thunk

์ดํ›„ ์ ์šฉ ๋ฐฉ๋ฒ•์€ redux ์ ์šฉ๋ฐฉ๋ฒ•์„ ์ฐธ๊ณ ํ•˜๋ฉด ๋œ๋‹ค.

์œ„ ๋ฐฉ๋ฒ•์„ ์ ์šฉํ–ˆ๋‹ค๋ฉด store.jsํŒŒ์ผ์— ์ถ”๊ฐ€๋ฅผ ํ•ด์ค˜์•ผ ํ•˜๋‚˜๋‹ค.

import {createStore, applyMiddleware} from "redux";
import thunk from 'redux-thunk' //์ถ”๊ฐ€
import rootReducer from "./reducers";

let store = createStore(rootReducer, applyMiddleware(thunk));


export default store;

์ด๋ ‡๊ฒŒ ๋ฏธ๋“ค์›จ์–ด๋ฅผ ์‚ฌ์šฉ์ค€๋น„๊ฐ€ ์™„๋ฃŒ๋œ ๊ฒƒ ์ด๋‹ค.

๐Ÿงฉ Redux middleware ์ ์šฉ

API ์š”์ฒญ์„ ์˜ˆ๋ฅผ ๋“ค์–ด ์ ์šฉํ•œ ๊ฒƒ ์ด๋‹ค.

redux ํด๋” ์•ˆ์— actions ๋ผ๋Š” ํด๋”๋ฅผ ๋งŒ๋“ค์–ด ์›ํ•˜๋Š” ํŒŒ์ผ๋ช…์œผ๋กœ ํŒŒ์ผ์„ ๋งŒ๋“ค์–ด ์ค€๋‹ค.

๋งŒ๋“  ํŒŒ์ผ์— ํ•จ์ˆ˜๋ฅผ ๋งŒ๋“ค์–ด ์ค€๋‹ค.

๊ธฐ์กด์— ์žˆ๋˜ API ์š”์ฒญ ํ•จ์ˆ˜๋ฅผ ๋ฏธ๋“ค์›จ์–ด๋กœ ๋ณ€๊ฒฝ ํ•ด๋ณด์ž

  • ๊ธฐ์กด ProductAll.js
const [productList, setProductList] = useState('');
const getProducts = async () => {
      let searchQuery = query.get('q') || "";
      dlet url = `https://my-json-server.typicode.com/jjunseokk/hnm-site/products?q=${searchQuery}`;
      let response = await fetch(url);
      let data = await response.json();
      setProductList(data);
  };
  • ๋ณ€๊ฒฝ ProductAll.js
const productList = useSelector((state)=>state.product.productList); //reducer๋ฅผ ๊ฐ€์ ธ์˜จ ๊ฒƒ.
const dispatch =useDispatch();
const getProducts = () => {
      let searchQuery = query.get('q') || "";
      dispatch(productAction.getProducts(searchQuery));
  };
  • productAction.js
function getProducts(searchQuery) {
    return async (dispatch, getState) => {
        let url = `https://my-json-server.typicode.com/jjunseokk/hnm-site/products?q=${searchQuery}`;
        let response = await fetch(url);
        let data = await response.json();
        dispatch({type : "GET_PRODUCT_SUCCESS", payload : {data}});
    };
}

๊ทธ๋ฆฌ๊ณ  ์ด์ œ dispatch๊ฐ€ ๋ณด๋‚ด๋ˆˆ `reducer๋ฅผ ๋”ฐ๋ผ์„œ ๋งŒ๋“ค์–ด ์ฃผ๋ฉด ๋œ๋‹ค.

let initialState = {
    productList: [],
    selectedItem: null,
}

function productReducer(state = initialState, action) {
    let { type, payload } = action;
    switch (type) {
        case "GET_PRODUCT_SUCCESS":
            return { ...state, productList: payload.data };
        default:
            return { ...state };
    }
}

export default productReducer;

๐Ÿงฉ Redux middleware ์—ฌ๋Ÿฌ๊ฐœ ํ•œ๋ฒˆ์— ์ ์šฉ

  • productReducer.js
let initialState = {
    productList: [],
    selectedItem: null,
}

function productReducer(state = initialState, action) {
    let { type, payload } = action;
    switch (type) {
        case "GET_PRODUCT_SUCCESS":
            return { ...state, productList: payload.data };
        case "GET_SINGLE_PRODUCT_SUCCESS":
            return { ...state, selectedItem: payload.data };
        default:
            return { ...state };

    }
}

export default productReducer;
  • authenticateReducer.js
let initialState = {
    id : '',
    password : '',
    authenticate : false
}

function authenticateReducer (state=initialState, action){
    let {type, payload} =action;

    switch(type){
        case "LOGIN_SUCCESS" :
            return {...state, id : payload.id, password : payload.password, authenticate : true};

        default : 
            return {...state};
    };

}

export default authenticateReducer;

์œ„ ๋‘˜ reducer๋ฅผ ํ•œ๋ฒˆ์— ์ ์šฉํ•  ๋•Œ CombineReducer๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์—ฌ๋Ÿฌ๊ฐœ๋ฅผ ํ•ฉ์น  ์ˆ˜ ์žˆ๋‹ค.
์•„๋ž˜์ฒ˜๋Ÿผ index.jsํŒŒ์ผ์„ ๋งŒ๋“ค์–ด ์•„๋ž˜์™€ ๊ฐ™์ด ์ž‘์„ฑํ•˜๋ฉด ๋œ๋‹ค.

  • index.js
import { combineReducers } from "redux";
import authenticateReducer from "./authenticateReducer";
import productReducer from "./productReducer";



export default combineReducers({
    auth : authenticateReducer,
    product : productReducer
});
profile
์•ˆ๋…•ํ•˜์„ธ์š” ํ”„๋ก ํŠธ์—”๋“œ ๊ฐœ๋ฐœ์ž์ž…๋‹ˆ๋‹ค. ๊ธ€์„ ์ด์ „ ์ค‘์ž…๋‹ˆ๋‹ค.

0๊ฐœ์˜ ๋Œ“๊ธ€