[React] redux Tookit을 사용한 전역변수 관리

김범식·2023년 4월 20일
0
post-thumbnail





설치


npm install @reduxjs/toolkit

이 안에 이미 react-redux가 포함되어 있기 때문에 기존에 redux패키지가 있다면 삭제해도 좋다.

이거 삭제해도 좋음

삭제한 후에 npm install을 다시 해주자




사용 목적


redux를 사용해도 얼마든지 전역으로 변수를 관리할 수 있다. 하지만 대형 프로젝트로 갈 수록 관리해야할 전역 변수가 많아지고, 그만큼 많은 action을 관리해야한다는 것인데, 이것을 좀더 쉽게 해주는것이 redux tookit이다.

또 react의 공식 라이브러리이기 때문에 react와 next.js에서 편리하게 사용할 수 있다.




동작 원리


출처 : https://www.tutorialspoint.com/redux/redux_middleware.htm

  1. store에서 전역으로 변수를 관리한다.
  2. 변수를 어떻게 관리할지 정의한 reducer를 만든다.
  3. reducer에서 정의한 action을 dispatch로 전달한다.
  4. reducer에 정의된 변수 관리 방식으로 state가 변경된다.

처음에는 무슨말인지 몰라도 된다. 코드를 통해 살펴보자




store/index.js

import { createSlice, configureStore } from "@reduxjs/toolkit";

const initialCounterState = {
  counter: 0,
  showCounter: true,
  isAuthenticated: false,
};

const counterSlice = createSlice({
  name: "counter", 
  initialState: initialCounterState, 
  reducers: {
    increment(state) {
      state.counter++; 
    },
    decrement(state) {
      state.counter--;
    },
    increase(state, action) {
      
      state.counter = state.counter + action.payload;
    },
    toggleCounter(state) {
      state.showCounter = !state.showCounter;
    },
  },
});


const store = configureStore({
  reducer: { counter: counterSlice.reducer, auth: authSlice.reducer }, //이러면 슬라이스를 여러개 넣을 수 있다.
});

export const counterAction = counterSlice.actions;

export default store;

전체적인 코드의 모습이다 하나하나살펴보자




import { createSlice, configureStore } from "@reduxjs/toolkit";
  • createSlice : redux에서는 Reducer라는 함수를 따로 정의해 주었는데 redux tookit에서는 createSlice라는 reducer를 편리하게 생성할 수 있는 함수를 제공한다.
  • configureStore : redux에서는 createStore를 사용했다면 redux tookit에서는 configureStore 로 내가 관리하고 싶은 전역변수를 구역별로 나눈다.




const initialCounterState = {
  counter: 0,
  showCounter: true,
};

초기에 관리해줄 데이터를 정의해 준다.




const counterSlice = createSlice({
  name: "counter", 
  initialState: initialCounterState, 
  reducers: {
    increment(state) {
      state.counter++; 
    },
    decrement(state) {
      state.counter--;
    },
    increase(state, action) {
      
      state.counter = state.counter + action.payload;
    },
    toggleCounter(state) {
      state.showCounter = !state.showCounter;
    },
  },
});

createSlice로 기존에 redux에서 만들었던 reducer를 생성할 수 있다.

  • name : Redux DevTools에서 slice를 식별하는데 사용된다. 실제 코드에서는 사용되지 않고 개발자가 개발자 도구에서 slice를 쉽게 구별해 줄 수 있도록 하는 역할을 한다.
  • initialState : 관리해줄 전역변수의 초기값을 삽입한다.
  • reducer : 전역변수의 상태를 어떻게 관리할지 정의한다.

전역변수는 전역변수 자체를 변경하면 안되다고 redux를 사용할 때 배웠을 것이다. 여기서는 state.countrer++ 와 같이 state에서 직접 값을 변경하는데 그 이유는 내부적으로 값을 복사하는 immer이라는 함수가 존재하기 때문에 보이는 코드처럼 직접 값을 수정해도 아무 문제가 없다.

또 변경할 값을 임의로 넣고 싶다면 action에 정의되어있는 payload의 값을 사용하면 되는데 이건 실제로 redux를 컴 포넌트에서 사용할 때 값이 어떻게 넘어오나 확인할 것이다.





//slice를 한개만 넣고 싶을 때 
const store = configureStore({
  reducer:counterSlice.reducer     
});

//slice를 여러개 넣고 싶을 때
const store = configureStore({
  reducer: { counter: counterSlice.reducer, other: otherSlice.reducer }, //이러면 슬라이스를 여러개 넣을 수 있다.
});

configureStore : 기존에 사용했던 createStore 대신에 configureStore에 reudcer를 등록한다 좋은점은 여러개의 slice를 동시에 등록하고 관리할 수 있다는 점이다.




export const counterAction = counterSlice.actions;

export default store;

외부에서 해당 reducer를 사용할 수 있도록 export 해주자 counterAction으로 import되는 counterSlice.actions는 reducer를 조작할 수 있는 함수를 갖고 있다.

실제 코드를 통해 어떻게 컴포넌트에 적용되는지 확인해 보자




app.js

function App() {
 

  return (
    <>
      <Counter />
    </>
  );
}

단순한 동작화면만 보여주기 위해 Counter 만 배치해 두었다




counter.js

import classes from "./Counter.module.css";
import { useSelector, useDispatch } from "react-redux";
import { counterAction } from "../store";

const Counter = () => {

  const counter = useSelector((state) => state.counter); 
  const dispatch = useDispatch();

  const incrementHandler = () => {
    dispatch(counterAction.increment());
  };
  const decrementHandler = () => {
    dispatch(counterAction.decrement());
  };

  const increaseHandler = () =>{
    dispatch(counterAction.increase(10))
  }
  const toggleCounterHandler=()=>{
    console.log(counter.showCounter)
    dispatch(counterAction.toggleCounter())
  }

  return (
    <main className={classes.counter}>
      <h1>Redux Counter</h1>
     {counter.showCounter && <div className={classes.value}>{counter.counter}asdf</div>}
      <div>
        <button onClick={incrementHandler}>increase</button>
        <button onClick={increaseHandler}>increase by 5</button>
        <button onClick={decrementHandler}>decrement</button>
      </div>
      <button onClick={toggleCounterHandler}>Toggle Counter</button>
    </main>
  );
};

export default Counter;

해당 코드는 다음과 같은 화면을 의미한다.

  • increase : 1씩 증가한다.
  • increase by 5 : 5씩 증가한다.
  • decrement : 1씩 감소한다.
  • Toggle Counter : 숫자가 보이지 않게 한다.

이제 코드를 하나씩 살펴보자




import { useSelector, useDispatch } from "react-redux";
import { counterAction } from "../store";
  • ukseSelector : configureStore에 등록된 state를 알아서 찾아주는 아주 고마운 hook이다.
  • useDispatch : 이 함수에 action을 전달하여 state를 변경한다.




  const counter = useSelector((state) => state.counter); 
  const dispatch = useDispatch();
  • counter : 우리는 configureStorecounter: counterSlice.reducer 라는 리듀서를 넣은 기억이 있을 것이다. 여기서 말하는 state.counter란 counterSlice에 있는 초기값을 말하는 것이다. 만약 state.counter가 아닌 state.other 을 사용했다면 otherSlice의 초기값을 const counter에 담았을 것이다.
  • dispatch : 이함수에 action을 담아 전달한다.





  const incrementHandler = () => {
    dispatch(counterAction.increment());
  };
  const decrementHandler = () => {
    dispatch(counterAction.decrement());
  };

  const increaseHandler = () =>{
    dispatch(counterAction.increase(10))
  }
  const toggleCounterHandler=()=>{
    console.log(counter.showCounter)
    dispatch(counterAction.toggleCounter())
  }

dispatch로 액션을 전달하기 위해서는 위에서 importcounterAction을 사용한다. counterActioncounterSlice.actions에서 나온것으로 reducer를 조작할 수 있는 함수들이 담겨있다.

즉 counterAction.incrememt()를 동작하게 되면 자동으로 action객체가 생성되어 dispatch에 전달하게 된다.




  const increaseHandler = () =>{
    dispatch(counterAction.increase(10))
  } 

payload : 함수내부에 값을 전달하면 { type : SOME_UNIQUE_TYPE, payload : 10 } 다음과 같은 action객체가 전달된다. payload는 reducer에서 사용할 수 있다.




return (
    <main className={classes.counter}>
      <h1>Redux Counter</h1>
     {counter.showCounter && <div className={classes.value}>{counter.counter}</div>}
      <div>
        <button onClick={incrementHandler}>increase</button>
        <button onClick={increaseHandler}>increase by 5</button>
        <button onClick={decrementHandler}>decrement</button>
      </div>
      <button onClick={toggleCounterHandler}>Toggle Counter</button>
    </main>
  );
};






/이제 전역변수를 컴포넌트에 마음것 사용해도 된다!

recoil만 사용해서 프로젝트를 진행했었는데 왜 redux를 사용하는지 알것같은 느낌이든다 . 혹시라도 틀린점이 있다면 댓글로 알려주세요!

profile
frontend developer

0개의 댓글