Front-end/React

[React] 상태관리 라이브러리 - Redux toolkit(RTK)

개발중인제이 2024. 5. 1. 15:18

이번 프로젝트의 필수 사항 중 하나인 리덕스툴킷! 

확실히 리덕스의 아키텍처를 먼저 이해하고 보니 리덕스 툴킷이 훨씬 사용하기 쉽고 편리하다는 걸 알 수 있었다.

 

 

 

 

우선 Redux, React Redux, Redux toolkit 각각의 역할에 대해 알아보자.

  • Redux : 상태 관리자, 프로그램이 동작하는데 필요한 데이터를 체계적으로 관리해주는 도구
  • 리덕스 자체는 리액트에 최적화되었다기 보다 자바스크립트로 된 프로젝트에 최적화된 도구라고 할 수 있다. 
  • 리액트에서 사용하기 쉽게 등장한것이 react redux이다.
  • React Redux : 리덕스와 리액트를 연결시켜주는 도구
  • 그럼에도 불구하고 리덕스는 많은 설정을 해줘야한다는 것과 코드의 양이 방대해지는 단점들이 생김 
  • 이러한 단점들을 보완하기 위해 나온것이 Redux toolkit

 

리덕스툴킷이 등장하게 된 배경

  • 기존의 리덕스는 설정할 것이 많음
  • 미들웨어를 사용하려면 설치, 설정할 것이 많음
  • 반복되는 코드
  • 불변성 유지의 어려움 

 

리덕스 사용해보기

간단하게 카운터가 증가하는 앱을 생성한다고 해보자.

import React from "react"

function Counter(){
	return ( 
		<div>
			<button>+</button> 0
		</div>
	)
}

 

1. store 생성 (리덕스의 store는 단일 스토어임)

import { createStore } from "redux"

//1. 리듀서 정의
function reducer(state, action){
	return state;
}

//2. 초기값 제공
const initialState = { value:0 }

//3. 스토어 생성
const store = createStore(reducer, initialState);

 

2. Provider를 통해 앱에 공급

import { Provider } from "react-redux"

export default function App(){
	return (
		<Provider store={store}>
			<Counter />
		</Provider>
	);
}

 

3. 카운터 컴포넌트 스토어에 있는 초기 value값을 가져오기

import React from "react"
import { useSelector } from "react-redux"

function Counter(){
	const count = useSelector(state => state.value);

	return ( 
		<div>
			<button>+</button> {count}
		</div>
	)
}

 

4. 앱의 버튼을 클릭했을 때 state(count)값을 바꾸는 dispatch 작업

import React from "react"
import { useSelector, useDispatch } from "react-redux"

function Counter(){
	const count = useSelector(state => state.value);
	const dispatch = useDispatch();

	return ( 
		<div>
			<button 
				onClick={()=>{
					dispatch({type:"up", step:2});
				}}>+</button> {count}
		</div>
	)
}

 

5. dispatch 작업을 해준 뒤 이것을 받는 리듀서 작업

function reducer(state, action){
	if(action.type === "up"){
		// 이때, state를 리턴하는데 
		// 불변하게 코드를 다루기 위해 기존의 값을 복제 해준다.
		return { ...state, value: state.value + action.step }
	}
	return state;
}

이렇게 해주면 버튼을 클릭했을 때 count가 2씩 증가하는 걸 확인할 수 있다.

 

💡 리덕스 툴킷을 사용해보기 전 스토어에 대해 이해해보자.

지금까지 리덕스를 활용한 코드는 모두 하나의 store에 모든 정보를 담고있다.

하지만 프로젝트의 규모가 커지면 하나의 스토어에 너무 방대한 양의 코드가 들어가면서 기능을 구별하기 어려울 수 있다.

리덕스 툴킷에서는 기능별로 작은 스토어인 slice를 하나의 스토어에 모아두고 리덕스 툴킷이 알아서 값을 만들어 준다. 

이 관점을 잘 이해하고 알아두자!

 

리덕스툴킷 사용하기

1. Counter 기능을 위한 작은 스토어라고 할 수 있는 counterSlice를 만들어보자. 

import { createSlice } from @reduxjs/toolkit;

// createSlice에는 필요한 객체들이 있다.
const counterSlice = createSlice(
	// slice의 이름
	name:"counter",
	// 초기값
	initialState:{ value:0 }
	// 리듀서 공급(s를 꼭 붙여야함), 타입별로 함수정의
	reducers:{
		// action.type이 up일때 이 함수가 실행할 것이다.
		// 리덕스를 사용할 때는 불변성때문에 복제({})를 했었는데 툴킷에서는 자동으로 해줌
		up:(state, action) => {
			state.value = state.value + action.step
		}
	}
});

 

2. 작은 스토어인 slice를 만들어 준 뒤에는 모아주기위해 configureStore를 사용해준다. → 하나의 거대한 스토어 만들어주기

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

// configureStore에는 객체를 전달해주면 됨
const store = configureStore({
	// 필수적으로 들어가야하는 항목
	// 리듀서에는 각각의 슬라이스의 리듀서들이 들어가면됨
	reducer:{
		// counterSlice에 들어있는 모든 리듀서들이 합쳐진 것
		counter:counterSlice.reducer
	}
});

 

3. 데이터가 필요한 컴포넌트에서 state를 가져오기 위해 useSelector 사용

import React from "react"
import { useSelector, useDispatch } from "react-redux"

function Counter(){
	const dispatch = useDispatch();
	const count = useSelector(state => { 
		return state.counter.value;
	});

	return ( 
		<div>
			<button 
				onClick={()=>{
					dispatch({type:"counterSlice/up", step:2});
				}}>+</button> {count}
		</div>
	)
}

 

4. 리덕스 툴킷의 action creator를 사용해서 코드를 더 간결하게 만들어보자.

import React from "react"
import { useSelector, useDispatch } from "react-redux"

function Counter(){
	const dispatch = useDispatch();
	const count = useSelector(state => { 
		return state.counter.value;
	});

	return ( 
		<div>
			<button 
				onClick={()=>{
					dispatch(counterSlice.actions.payload(2))			
				}}>+</button> {count}
		</div>
	)
}

콘솔에 찍어보면 알겠지만 "counterSlice.actions.payload(2)" 했을 때 전달하는 값은 다 payload로 간다.

 

따라서 createSlice 값도 이렇게 변경됨

import { createSlice } from @reduxjs/toolkit;

const counterSlice = createSlice(
	name:"counter",
	initialState:{ value:0 }
	reducers:{
		up:(state, action) => {
			state.value = state.value + action.payload
		}
	}
});

직접 액션을 직접 만들어주는 경우는 원래대로 하겠지만 action creator를 사용할때는 payload를 사용하도록 약속되어있다. → 툴킷의 장점 중 하나

 

완성 코드!

App.js

import React from "react";
import store from './store';

export default function App() {
  return (
    <Provider store={store}>
      <div>
        <Counter></Counter>
      </div>
    </Provider>
  );
}

 

Counter.js

import React from "react";
import { Provider,useSelector,useDispatch } from 'react-redux';
import { up } from './counterSlice';

function Counter(){
  const dispatch = useDispatch();
  const count = useSelector(state=>{
    return state.counter.value;
  });
  
  return <div>
    <button onClick={()=>{
      dispatch(up(2));
    }}>+</button> {count}
  </div>
}

 

counterSlice.js

import { createSlice } from '@reduxjs/toolkit';

const counterSlice = createSlice({
  name:'counterSlice',
  initialState:{value:0},
  reducers:{
    up:(state, action)=>{
      state.value = state.value + action.payload;
    }
  }
});

export default counterSlice;
// 다른곳에서 사용하기 간결하게! 
// counterSlice의 actions들 중에서 up을 내보내는 것.
export const {up} = counterSlice.actions;

 

store.js

import {configureStore} from '@reduxjs/toolkit';
import counterSlice from './counterSlice';

const store = configureStore({
  reducer:{
    counter:counterSlice.reducer
  }
});
export default store;

 

 

📌

생활코딩