redux-toolkit

Goofi·2025년 1월 24일
0

Redux와 Redux Toolkit 정리


Redux 등장 배경

Redux는 React에서 props 없이 컴포넌트 간 상태를 공유할 수 있도록 도와주는 라이브러리입니다.
하지만 Redux는 설정이 복잡하고, 코드가 반복되며, 불변성을 유지하기 어렵다는 단점이 있었습니다. 이를 해결하기 위해 React ReduxRedux Toolkit이 등장했습니다.


주요 API

Redux (기본 Redux API)

  • createStore: Redux 스토어 생성
  • subscribe: 스토어 상태 변화를 감지
  • getState: 현재 상태 가져오기
  • dispatch: 액션을 스토어에 전달

React Redux

  • connect: 클래스형 컴포넌트에서 Redux 상태/액션 연결
  • useDispatch: 함수형 컴포넌트에서 액션 전달
  • useSelector: 함수형 컴포넌트에서 상태 가져오기

Redux Toolkit (더 편리한 상태 관리 제공)

  • configureStore: Redux 스토어 설정 및 생성
  • createSlice: state와 reducer를 함께 생성
  • createAsyncThunk: 비동기 액션을 쉽게 생성

Redux Toolkit의 특징과 장점

Redux Toolkit은 기존 Redux의 단점을 보완하기 위해 만들어졌습니다.
1. 설정 간소화: 복잡한 설정을 최소화
2. 미들웨어 설치 간편화: 기본적으로 Redux Thunk 포함
3. 코드 중복 감소: createSlice로 reducer와 action 동시 관리
4. 불변성 유지 용이: Immer.js를 사용하여 state를 직접 수정 가능하게 만듦


Redux 사용 시 주의사항

  1. Redux store에 모든 state를 넣지 말 것
    컴포넌트 간 공유가 필요한 상태만 Redux에서 관리하세요.
  2. 컴포넌트 내부에서만 사용하는 상태는 useState를 활용
    React의 useState로 충분히 관리 가능한 상태는 Redux를 사용하지 않아도 됩니다.

Redux 사용법

1. 설치

npm install @reduxjs/toolkit react-redux

2. 스토어 설정

store.js

import { configureStore } from "@reduxjs/toolkit"

export default configureStore({
	reducer : {  }
})

3. Provider로 감싸기

Redux 상태를 앱 전체에서 사용할 수 있도록 설정합니다.

index.js

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

const root = ReactDOM.createRoot(document.getElementById('root'));

root.render(
    <Provider store={store}>
    	<App /> //store.js에 있던 state 전부 사용가능
    </Provider>
);

Redux 상태 관리

1. Redux Store에 State 보관 (Slice 생성)

store.js 파일에서 상태와 리듀서를 정의하고 Redux Store를 구성합니다.

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

// Slice 생성
let user = createSlice({
  name: "user",
  initialState: "kim", // 기본 상태 값
});

// Store 생성
export default configureStore({
  reducer: {
    user: user.reducer, // user 등록
  },
});

주요 개념

  • createSlice
    상태(state)와 리듀서(reducer)를 동시에 생성하는 함수.
    Redux Toolkit에서 상태 관리를 간편하게 설정할 수 있도록 도와줍니다.

  • initialState
    상태의 초기값을 지정합니다.
    Slice가 처음 생성될 때 사용할 기본 상태 값입니다.

  • reducer
    상태를 변경하는 함수입니다.
    각 리듀서는 특정 액션에 따라 상태를 어떻게 업데이트할지 정의합니다.

configureStore

Reducer에서 반환된 새로운 state를 Store라는 개체로 정리해 관리하는 곳이다.

상태를 변경하는 방법

  1. State 변경 함수 작성
    store.js
let user = createSlice({
  name: "user",
  initialState: "kim",
  reducers: {
    changeName(state) {
      return "john " + state;
    },
  },
});

export let { changeName } = user.actions; // 함수 export
  1. 컴포넌트에서 Dispatch 사용
import { useSelector, useDispatch } from "react-redux";
import { changeName } from "../store";

const Cart = () => {
  const userName = useSelector((state) => state.user); // 상태 가져오기
  const dispatch = useDispatch(); // 상태 변경 요청 함수

  return (
    <button
      onClick={() => {
        dispatch(changeName()); // 상태 변경 요청
      }}
    >
      이름 변경
    </button>
  );
};

reducers
상태를 변경하는 함수들이다. 리듀서를 정의한다.

리듀서란?

상태를 변경하는 함수. 어떤 액션이 발생했을 때 상태를 어떻게 변화시킬지 정의한다.

리듀서 함수

UpdateNumber(state, action){
	return state = action.payload;
}
  • state:state: 현재의 상태이다. initialState로 설정된 값이 처음 상태가 된다.
  • action: dispatch된 액션 객체이다. 액션 객체는 type과 payload를 가지고 있다. payload는 상태를 어떻게 변경할지에 대한 값을 포함한다.
  1. export
    → 변경함수를 export 해야한다. 하지만...

store.js

let user = createSlice({
	name : 'user',
  	intialState : 'kim',
  	reducers : {
    	export changeName(state){
        	return 'john '+ state
        }
    }
})

위 코드와 같이 변경함수 앞에 export를 해주게 되면 작동하지 않는다. export는 createSlice() 함수 밖에서 사용해주어야 한다.

아래와 같이 작성해주어야 한다.

export let { changeName } = user.actions //함수명을 적어주면 된다. (destructuring)

createSlice()함수 밖에서 작성한 코드 변경함수가 있는 createSlice이름.action라고 적으면 된다.

  1. dispatch(state변경함수())
    component 들이 state 수정이 필요하다면 store.js 한테 변경함수 좀 실행해달라고 부탁하는 것이다.
    즉, dispatch는 store.js 한테 실행 부탁 메시지를 보내주는 것이다.

Cart.jsx

import {useDispathch, useSelctor}
import { changename } from "../redux/store.js"

const Cart = ()=>{
	let a = useSelector((state) => state); //redux store 가져오기
  	let dispatch = useDispatch() //dispatch는 store.js로 요청을 보내주는 함수이다.
 
    return(
    	<>
        	<button onClick = {()=>{
				dispatch(changeName())  //dispatch(state변경함수()) 이렇게 사용해주면 된다.
          		//그냥 state 변경함수()를 가져다 사용하지 말고 꼭 import 해주어야 한다.	
          }>
				store.js한테 실행 부탁 메시지 보내는 버튼
        	</button>
        </>
    )

}

state가 array/object

state가 array/object경우 return 없이 직접 수정할 수 있다.
❗️return을 입력할 경우 에러가 발생한다.
→ 수정이 편리하기 때문에 문자 하나만 필요해도 {}안에 담기도 한다.
store.js

let user = createSlice({
    name : 'user',
    initialState : {name : 'kim', age:20},
    reducers : {
        changeName(state){ //기존 state를 뜻한다.
            state.name = "park"
        },
        increase(state, action){
            state.age += action.payload //action.payload 화물보낸거 출력문법
        },
    }
})
  • 파라미터 뚫어 놓으면 비슷한 함수 여러개 필요없다.
  • 파라미터 작명을 할때 보통 action이라고 많이 한다.
    왜? action에는 화물 뿐만 아니라 액션에 대한 여러가지 정보들이 들어 있기 때문이다.

결론 : state 변경함수를 action이라고 한다.

store.js 안에 있는 let user 코드 길면 import export 사용하면 된다.

동기 액션과 비동기 액션

동기 액션

  • 바로 실행되는 작업
    (e.g., 버튼 클릭 시 카운트 증가)
  • reducer로 상태를 변경합니다.

비동기 액션

  • 결과가 지연되는 작업
    (e.g., 서버 요청, API 호출)
  • createAsyncThunkextraReducers를 사용합니다.

reducers 와 extraReducers

reducer는 action creator를 자동으로 만들어준다.

액션(action) : Redux에서 상태를 변경하기 위해 사용하는 객체이다. 주로 typepayload를 가지고 있다.

{ type: 'count/increment', payload: undefined }

액션 크리에이터(action creator): 액션 객체를 생성하는 함수입니다. 예를 들어:

const increment = () => ({ type: 'count/increment' });

결론 : type: 'count/increment' 자동으로 생성
name: 'count' 슬라이스 이름
reducers : { increment } 리듀서 함수 이름

extraReducers는 외부에서 정의된 액션에 대한 리듀서를 추가할 때 리듀서를 추가할 때 사용된다.
즉, 현재 슬라이스 외부에서 정의된 액션 타입에 반응하도록 상태 변경 로직을 정의할 수 있다.

사용 목적

  • 다른 슬라이스에서 발생한 액션에 반응할 때
  • Redux Thunk, Redux Saga, 또는 기타 미들웨어에서 생성된 비동기 액션에 반응할 때

왜 액션 크리에이터를 안 만들어줄까?

  • extraReducers는 외부에서 정의된 액션과 관련 있기 때문에, Redux Toolkit은 해당 액션 타입에 대해 이미 액션 크리에이터가 존재한다고 가정한다.
  • 중복된 액션 크리에이터를 만들지 않도록 설계된 거다. extraReducers의 역할은 이미 정의된 액션 타입에 리듀서 로직만 추가하는 거다.

reducers
현재 슬라이스에서 사용하는 동기적인 상태 변경 로직 작성
액션 크리에이터를 자동으로 생성합니다.

let user = createSlice({
  name: "user",
  initialState: "kim",
  reducers: {
    changeName(state) {
      return "john " + state;
    },
  },
});

// 자동 생성된 액션 객체
{ type: "user/changeName", payload: undefined }

extraReducers
외부에서 정의된 액션에 대해 상태 변경 로직을 작성
액션 크리에이터는 자동 생성되지 않습니다.

extraReducers: (builder) => {
  builder.addCase(fetchData.pending, (state) => {
    state.status = "Loading";
  });
  builder.addCase(fetchData.fulfilled, (state, action) => {
    state.value = action.payload;
    state.status = "Complete";
  });
  builder.addCase(fetchData.rejected, (state) => {
    state.status = "Failed";
  });
};
profile
오늘보단 내일이 강한 개발자입니다!!🧑🏻‍💻

0개의 댓글