본문 바로가기

[코딩애플] Redux-toolkit

codeConnection 2024. 6. 15.

리덕스란?

Redux는 전역으로 상태를 관리할 수 있게 도와주는 라이브러리이다.

이런 라이브러리를 사용하지 않는다면 상위 컴포넌트에서 만든 State가 10개 밑의 deps를 가진 자식 컴포넌트에서 이 state를 전달 받으려면

중간에 끼어 있는 부모들은 그 state를 사용하지 않음에도 계속 드릴처럼 뚫고 내려 가야 하는 단점이 있다.

 

그런데 Redux는 Store라는 중앙 저장소를 만들어 두고 그곳에서 필요한 곳에 바로 state를 전달할 수 있는 장점이 있다.

 

리덕스 구 버전과 툴킷이라는 신 버전이 있는데, 신버전을 권장한다.

사용방법

패키지 설치

yarn add @reduxjs/toolkit react-redux

Store.js 파일 만들기

중앙 저장소인 store 파일을 만든다.

// src/store.js

import { configureStore } from '@reduxjs/toolkit'

export default configureStore({
  reducer: { }
})

provider로 전역에 store 내리기

main.jsx 또는 index.js에서 provider를 부모 컴포넌트로 감싸 전역으로 내려준다.

// main.jsx

import { Provider } from "react-redux";
import store from './store.js'

const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
  <React.StrictMode>
    <Provider store={store}>
      <BrowserRouter>
        <App />
      </BrowserRouter>
    </Provider>
  </React.StrictMode>
);

Store에 필요한 state 만들기

// store.js

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

const user = createSlice({
    name: 'user',
    initialState: 'kim'
})

const stock = createSlice({
	name: 'stock',
    initialState: [10, 11, 12]
})

const cart = createSlice({
	name: 'cart',
    initialState: [
        {id: 0, name: '장원영', count: 2},
        {id: 1, name: '임나연', count: 1}
    ]
})

export default configureStore({
	reducer: {
            user: user.reducer
            stock: stock.reducer
            cart: cart.reducer
	}
})

createSlice()에 필요한 state를 만들고,

configureStore()에 전역에서 사용하겠다는 등록을 하면 됨.

 

createSlice()에서 name은 state의 이름, initialState는 state의 초기값임.

state와 유사해 보이는데 redux에서는 이런 state 형태를 하나의 slice라고 부름.

 

configureStore()에서 사용 등록을 할 땐 reducer에 객체 형태로, {state명: state의 값}을 적어 주면 된다.

 

아래 configureStore에서 key와 value는 다음과 같이 구성되어 있다.

보통은 내가 만든 slice 이름과 다른 데서 호출 할 이름과 동일하게 작명한다.

{ 내가다른데서호출할이름 : 여기서만든slice의이름(user).reducer }

다른 컴포넌트에서 state 사용하기

import { useSelector } from 'react-redux';

function App() {

	const user = useSelector((state) => state.user);
    const stock = useSelector((state) => state.stock);
    const cart = useSelector((state) => state.cart);

	return (
    	<>
            {state.cart.map((item, index)=>
            	<tr key={index}>
            		<td>{state.cart.id}</td>
            		<td>{state.cart.name}</td>
                </tr>
            )};
        </>
    );
}

useSelector훅을 통해 store에 접근 가능하도록 호출하여 state.state명으로 꺼내온다. 그리고 변수에 할당해서 사용하면 된다.

상태 변경 함수 만들기 (reducer)

reducer 만들고 export 하기

// store.js

const user = createSlice({
  name : 'userName',
  initialState : '원영',
  
  // 상태 변경 함수 작성
  reducers : {
    changeUserName(state) {
    	return '장' + state
    },
    // 여러 개 작성 가능
    
  }
  })
  
// 만든 상태 변경 함수 내보내야 함.

export let { changeName, 함수2, 함수3, ... } = user.actions;
// actions에는 Reducer가 모두 호출된다.

상태 변경 함수(reducer)는 여러 개 만들어도 된다. 콤마를 찍어서 분리만 해주면 된다. state를 매개 변수로 받으면 현재 slice(상태)의 initialState(초기값)을 함수 내에서 사용할 수 있다.

 

함수에서 return 값을 하는 형태로 slice의 initialState 값을 변경해줄 수 있다.

 

만든 함수 사용하기

// App.jsx

// reducer 함수 import 하기
import { changeUserName } form './../store.js';

function App (

  // reducer 함수를 실행해주는 dispatch 메서드 사용 선언하기
  const dispatch = useDispatch();

return (
  <>
    {/* dispatch 함수로 감싸서 삿ㅇ태 변경 함수 호출하기 */}
  	<button onclick={ () => {dispatch(changeUserName())} }>
  </>
)

만든 reducer 함수를 사용하는 3 steps.

  • 1. 사용할 컴포넌트에서 상태 변경 함수(reducer) store로 부터 import하기
  • 2. 사용할 컴포넌트에서 dispatch 사용 선언하기
  • 3. 함수를 호출하는 부분에서 dispatch(실행할함수()) 로 감싸서 호출하기

state가 객체, 배열일 경우 상태 변경하는 방법

// store.js

import { createSlice } from '@reduxjs/toolkit'; // 필요한 import 추가

const user = createSlice({
  name: 'user',
  initialState: {name: '장원영', age: 21},
  
  reducers: {
    changeName(state) {
      state.name = '안유진'
    increase(state) {
      state.age += 1
  }
})

export const { changeName, increase } = user.actions;

export default user.reducer;

store에 있는 state의 initialState가 원시 자료형일 경우에는 return문을 통해 데이터를 바꿔 주었지만, object, array 타입에서는 return문 없이 직접 수정이 가능하다.

slice를 분리 시키기

위 예제 처럼 store와 slice를 store 한 파일에 작성할 수도 있지만, 프로젝트의 규모가 커지면 가독성이 좋지 않고 유지보수가 어려울 수 있기 때문에 slice가 적더라도 분리해서 관리하는 패턴에 익숙해지는 것이 좋다.

파일트리

src/
├── store/
│   ├── store.js         // Redux store 설정 파일
│   ├── userSlice.js     // user slice 파일
│   ├── anotherSlice.js  // another slice 파일
│   └── ...              // 추가적인 slice 파일들
└── ...

slice 파일들

// userSlice.js

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

const user = createSlice({
  name: 'user',
  initialState: { name: '장원영', age: 21 },
  
  reducers: {
    changeName(state) {
      state.name = '안유진';
    },
    increase(state) {
      state.age += 1;
    }
  }
});

export const { changeName, increase } = user.actions;
export default user.reducer; // 리듀서 export




// anotherSlice.js

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

const another = createSlice({
  name: 'another',
  initialState: { value: 0 },
  
  reducers: {
    increment(state) {
      state.value += 1;
    }
  }
});

export const { increment } = another.actions;
export default another.reducer; // 리듀서 export

store.js 파일

// store.js

import { configureStore, combineReducers } from '@reduxjs/toolkit';
import userReducer from './userSlice'; // user 슬라이스 리듀서 import
import anotherReducer from './anotherSlice'; // 다른 슬라이스 리듀서 import

const rootReducer = combineReducers({
  user: userReducer,
  another: anotherReducer,
});

const store = configureStore({
  reducer: rootReducer,
});

export default store;

store 파일에서는 slice 자체가 아니라 리듀서만 import 한다.

댓글