Redux

์›๋ž˜ ๋ฆฌ๋•์Šค๋ฅผ ๋„ฅ์ŠคํŠธ์—์„œ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ์€ ์ƒ๋‹นํžˆ ๋ณต์žกํ•ฉ๋‹ˆ๋‹ค.

next-redux-wrapper๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ๋น„๊ต์  ์‰ฝ๊ฒŒ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์„ค์น˜ํ•ด์ค์‹œ๋‹ค.

yarn add next-redux-wrapper

๊ทธ๋ฆฌ๊ณ  ๋ฃจํŠธ ๋””๋ ‰ํ† ๋ฆฌ ์•ˆ์— storeํด๋”๋ฅผ ๋งŒ๋“ ๋’ค configureStore.jsํŒŒ์ผ์„ ์ƒ์„ฑํ•ด์ฃผ์„ธ์š”

๋ฒ„์ „์€ 6๋ฒ„์ „์ž…๋‹ˆ๋‹ค.

๊ธฐ๋ณธ์ ์ธ ํ‹€์€ ๋‹ค์Œ๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค.

import { createWrapper } from "next-redux-wrapper";

const configureStore = () => {};

const wrapper = createWrapper(configureStore);

export default wrapper;

๋‹ค์Œ์€ ๋ฆฌ๋•์Šค์— ๋Œ€ํ•œ ์ž์„ธํ•œ ๋‚ด์šฉ์„ ๋ณผ ์ˆ˜ ์žˆ๋„๋ก wrapper์•ˆ์— ์˜ต์…˜์„ ์ฃผ๊ฒ ์Šต๋‹ˆ๋‹ค.

const wrapper = createWrapper(configureStore, {
  debug: process.env.NODE_ENV === "development",
});

๋‹ค์Œ์€ _app.js๋กœ ์ด๋™ํ•ด์„œ ์ œ์ผ ์ƒ์œ„ ์ปจํ…Œ์ด๋„ˆ๋ฅผ ๋ฆฌ๋•์Šค๋กœ ๊ฐ์‹ธ์ฃผ๊ฒ ์Šต๋‹ˆ๋‹ค.

import React from "react";
import Head from "next/head";
import "antd/dist/antd.css";
import wrapper from "../store/configureStore";


function _app({ Component }) {
  return (
    <div>
      <Head>
        <meta charSet="utf-8"></meta>
        <title>Wongeun</title>
      </Head>
      <Component />
    </div>
  );
}
//๊ฐ์‹ธ์ค€๋‹ค.
export default wrapper.withRedux(_app);

์—ฌ๋Ÿฌ ์ปดํฌ๋„ŒํŠธ์—์„œ ๊ณตํ†ต์ ์œผ๋กœ ์“ฐ์ด๋Š” ๋ฐ์ดํ„ฐ๊ฐ€ ์žˆ๋Š”๋ฐ,

์ปดํฌ๋„ŒํŠธ๋Š” ๋ถ„๋ฆฌ๊ฐ€ ๋˜์–ด์žˆ๊ธฐ ๋•Œ๋ฌธ์— ๊ทธ ๋ฐ์ดํ„ฐ๋“ค์„ ๋ถ€๋ชจ์ปดํฌ๋„ŒํŠธ์—์„œ ๋ฐ์ดํ„ฐ๋ฅผ ๋ฐ›์•„ ์ž์‹์œผ๋กœ ์ค˜์•ผ ํ•ฉ๋‹ˆ๋‹ค.

redux๋Š” ์ค‘์•™์—์„œ ๋ฐ์ดํ„ฐ๋ฅผ ๋ฐ›์•„ ์ปดํฌ๋„ŒํŠธ๊ฐ€ ํ•„์š”๋กœ ํ• ๋•Œ ๊ทธ ๋ฐ์ดํ„ฐ๋ฅผ ๋ฟŒ๋ ค์ฃผ๋Š”๋ฐ,

react์˜ ContextAPI, Redux , graphQL , MobX ๋“ฑ์ด ์žˆ์Šต๋‹ˆ๋‹ค.

๊ฐ๊ฐ์˜ ์žฅ๋‹จ์ ์ด ์žˆ๋Š”๋ฐ

Redux๋Š” ์•ฑ์ด ์•ˆ์ •์ ์ด์ง€๋งŒ, ์ฝ”๋“œ๋Ÿ‰์ด ๋งŽ์Šต๋‹ˆ๋‹ค.

MobX๋Š” ์ฝ”๋“œ๋Ÿ‰์€ ์ ์ง€๋งŒ, ๋‚œ์ด๋„๊ฐ€ ์š”๊ตฌ๋˜์ฃ ..

๊ฐ„๋‹จํ•œ ์ž‘์—…์€ ContextAPI๋ฅผ ์ด์šฉํ•˜๋ฉด ๋˜๋Š”๋ฐ,

ContextAPI๋ฅผ ์ด์šฉํ•˜๋ฉด ๋น„๋™๊ธฐ ์ž‘์—…์„ ์ฒ˜๋ฆฌํ•˜๊ธฐ์— ์žˆ์–ด์„œ ์–ด๋ ต๊ธฐ ๋•Œ๋ฌธ์—

์ด๋ฒˆ์—๋Š” Redux๋ฅผ ์‚ฌ์šฉํ•ด๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค.

์ ์šฉ๋ฒ•

Next์—์„œ๋Š” ์šฐ๋ฆฌ๊ฐ€ ์•„๋Š” ๋ฆฌ๋•์Šค์™€ ๋‹ค๋ฅธ ๋ฐฉ๋ฒ•์„ ํ†ตํ•ด ์ ์šฉ์‹œํ‚ต๋‹ˆ๋‹ค.

storeํด๋”๋ฅผ ์ƒ์„ฑํ•œ๋’ค configureStoreํŒŒ์ผ์„ ๋งŒ๋“ค๊ณ  ๋‹ค์Œ๊ณผ ๊ฐ™์ด ์ž‘์„ฑํ•ด์ฃผ์„ธ์š”

import { createWrapper } from "next-redux-wrapper";
import { createStore } from "redux";
import reducer from "../reducers";

const configureStore = () => {
 
  const store = createStore(reducer);
  return store;
};
const wrapper = createWrapper(configureStore, {
  debug: process.env.NODE_ENV === "development",
});
export default wrapper;

๊ทธ๋ฆฌ๊ณ  app.js ํŒŒ์ผ์— ๋“ค์–ด๊ฐ€์„œ_app ์ปดํฌ๋„ŒํŠธ๋ฅผ ๊ฐ์‹ธ์ค๋‹ˆ๋‹ค.

import React from "react";
import Head from "next/head";
import "antd/dist/antd.css";
import wrapper from "../store/configureStore";

function _app({ Component }) {
  return (
    <div>
      <Head>
        <meta charSet="utf-8"></meta>
        <title>Wongeun</title>
      </Head>
      <Component />
    </div>
  );
}
//๊ฐ์‹ธ์ค€๋‹ค.
export default wrapper.withRedux(_app);

๋ฆฌ๋•์Šค ์ ์šฉ์ด ์™„๋ฃŒ๋˜์—ˆ์Šต๋‹ˆ๋‹ค.

๋ฆฌ๋•์Šค์˜ ์•ก์…˜ํ•จ์ˆ˜์™€ ๋ฆฌ๋“€์„œ๋ฅผ ๋งŒ๋“ค์–ด ๋ณผ๊นŒ์š”?

const LOG_IN = "users/LOG_IN";
const LOG_OUT = "users/LOG_OUT";

export const logIn = () => ({ type: LOG_IN });
export const logOut = () => ({ type: LOG_OUT });

const initialState = {
  user: {
    isloggedIn: false,
    user: null,
    signUpData: {},
    loginData: {},
  },
  post: {
    mainPosts: [],
  },
};

export default function users(state = initialState, action) {
  switch (action.type) {
    case LOG_IN:
      return { ...state, user: { ...state.user, isloggedIn: true } };
    case LOG_OUT:
      return { ...state, user: { ...state.user, isloggedIn: false } };
    default:
      return state;
  }
}

๊ฐœ๋ฐœ์ž ๋„๊ตฌ

yarn add redux-devtools-extension

๊ฐœ๋ฐœ์ž ๋„๊ตฌ๋ฅผ ๋‹ค์šด๋ฐ›์€ ๋’ค ์Šคํ† ์–ด์— ์ ์šฉํ•ด์ฃผ๋ฉด ๋ฉ๋‹ˆ๋‹ค.

import { createWrapper } from "next-redux-wrapper";
import { createStore, applyMiddleware, compose } from "redux";
import reducer from "../reducers";
import { composeWithDevTools } from "redux-devtools-extension";

const configureStore = () => {
  const middlewares = [];
  const enhancer =
    process.env.NODE_ENV === "production"
      ? compose(applyMiddleware(...middlewares)) //๋ฐฐํฌ์šฉ
      : composeWithDevTools(applyMiddleware(...middlewares)); //๊ฐœ๋ฐœ์šฉ
  const store = createStore(reducer, enhancer);
  return store;
};
const wrapper = createWrapper(configureStore, {
  debug: process.env.NODE_ENV === "development",
});
export default wrapper;

๋ฏธ๋“ค์›จ์–ด๋Š” ๋‚˜์ค‘์— ์ ์šฉ์‹œํ‚ค๊ธฐ ์œ„ํ•œ ๊ณต๊ฐ„์„ ๋งŒ๋“ค์–ด ๋‘” ๊ฒƒ์ž…๋‹ˆ๋‹ค.

๊ผญ ๋ฆฌ๋“€์„œ์—์„œ ๋ถˆ๋ณ€์„ฑ์„ ์ง€์ผœ์ค˜์•ผ๋งŒ ํžˆ์Šคํ† ๋ฆฌ๊ฐ€ ์œ ์ง€๋˜์–ด ๋™์ž‘ํ•  ์ˆ˜์žˆ๋Š”๊ฒƒ์ž…๋‹ˆ๋‹ค.

๋ฆฌ๋“€์„œ๋ถ„ํ•ด

import { HYDRATE } from "next-redux-wrapper";
import user from "./user";
import post from "./post";
import { combineReducers } from "redux";

const rootReducer = combineReducers({
  // HYDRATE SSR์„ ์œ„ํ•ด ์ผ๋ถ€๋Ÿฌ ๋งŒ๋“ค์–ด ์ง„ ๊ฒƒ์ž…๋‹ˆ๋‹ค.
  index: (state = {}, action) => {
    switch (action.type) {
      case HYDRATE:
        return { ...state, ...action.payload };
      default:
        return state;
    }
  },
  user,
  post,
});
export default rootReducer;

์ปดํฌ๋„ŒํŠธ๋ฅผ ๊ตฌ์„ฑํ•˜๊ธฐ ์ „์—postReducer๋„ ๊ตฌ์„ฑํ•ด๋ณผ๊ฒŒ์š”

export const initialState = {
  mainPosts: [
    {
      id: 1,
      User: {
        id: 1,
        nickname: "์ œ๋กœ์ดˆ",
      },
      content: "์ฒซ๋ฒˆ์งธ๊ฒŒ์‹œ๊ธ€",
      Images: [
        {
          src:
            "https://media.vlpt.us/images/wooder2050/post/d2764478-dc72-4cc9-9128-f66bfb8b3aa3/reactintroduction.png",
        },
        {
          src:
            "https://media.vlpt.us/images/wooder2050/post/d2764478-dc72-4cc9-9128-f66bfb8b3aa3/reactintroduction.png",
        },
        {
          src:
            "https://media.vlpt.us/images/wooder2050/post/d2764478-dc72-4cc9-9128-f66bfb8b3aa3/reactintroduction.png",
        },
      ],
      Comments: [
        {
          User: {
            nickname: "nero",
          },
          content: "์™€์šฐ",
        },
        {
          User: {
            nickname: "hero",
          },
          content: "์˜ค์•„์•„์•„",
        },
      ],
    },
  ],
  imagePaths: [],
  postAdded: false,
};
//์•ก์…˜์ƒ์„ฑ
const ADD_POST = "ADD_POST";
export const addPost = () => ({
  type: ADD_POST,
});
//๊ฐ€์งœ๋ฐ์ดํ„ฐ
const dummyPost = {
  id: 2,
  content: "๋”๋ฏธ์ž…๋‹ˆ๋‹ค",
  User: {
    id: 1,
    nickname: "conrad",
  },
  Images: [],
  Comments: [],
};

export default function reducer(state = initialState, action) {
  switch (action.type) {
    case ADD_POST:
      return {
        ...state,
        mainPosts: [dummyPost, ...state.mainPosts], //์•ž์— ์ถ”๊ฐ€ํ•ด์•ผ ๊ฒŒ์‹œ๊ธ€์ด ์œ„์— ๋‚˜ํƒ€๋‚œ๋‹ค.
        postAdded: true,
      };
    default:
      return state;
  }
}

๋”๋ฏธ๋ฐ์ดํ„ฐ๋ฅผ ํ™œ์šฉํ•˜์˜€์Šต๋‹ˆ๋‹ค.

์ฒ˜์Œ์— FE ๊ฐœ๋ฐœ์ž๋Š” BE๊ฐœ๋ฐœ์ž์™€ ์ž˜ ๋…ผ์˜ํ•˜์—ฌ์„œ ๋”๋ฏธ๋ฐ์ดํ„ฐ๋ฅผ ์ž˜ ๋งŒ๋“ค์–ด ๋†“๋Š”๊ฒƒ์ด ์ค‘์š”ํ•ฉ๋‹ˆ๋‹ค.

๋˜ํ•œ ๊ทธ ๋”๋ฏธ๋ฐ์ดํ„ฐ๋Š” ์กฐ๊ธˆ์€ ๋ฐ”๋€”์ˆ˜๋„ ์žˆ๋‹ค๋Š”๊ฑธ ์•Œ๊ณ  ์žˆ์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

Last updated