본문 바로가기
Frontend/React

React : Redux에 대해서 알아보자

by 코딩쥐 2024. 9. 9.

Redux 시작하기 | Redux

 

Redux 시작하기 | Redux

소개 > 시작하기: Redux를 배우고 사용하기 위한 자료

ko.redux.js.org

 

React를 공부하면서 props에 대해 배운 적이 있다. Props를 사용해서 상위에서 하위 컴포넌트로 데이터를 전달하고자 할 때, 필요한 하위 컴포넌트까지 계속해서 전달해야 하는 상황(props drilling)이 발생한다. 이를 해결하기 위해 React에서는 전역적으로 데이터를 공유할 수 있는 Context API 기능이 있지만, Context API의 경우에는 불필요한 재렌더링이 발생하는 단점이 있어 최근 사용이 많이 줄었다.

 

Redux는 Context API에서 발생했던 불필요한 렌더링을 줄이며 state를 관리할 수 있도록 돕는 서드파티 라이브러리이다. Redux는 상태 관리의 복잡성을 줄여줘 대규모 애플리케이션에서 유용하다. Redux가 가지고 있는 주요 개념 3가지는 다음과 같다.

  • store : 데이터가 담겨있는 저장소
    • dispatch
      store의 내장 함수 중 하나로, state를 다른 값으로 업데이트하고 재렌더링을 해주는 함수이다. 이 함수는 상태 변화에 대한 action을 파라미터로 전달한다. 
  • action
    상태에 변화를 주기 위한 이벤트를 정의하는 객체로, type 속성을 가지고 있으며 필요한 정보는 payload라는 속성을 통해 전달된다. 

  • reducer
    state가 어떻게 업데이트 될 것인지를 지정하는 함수로, state와 action을 인자로 받고 다음 state를 반환한다.

출처 : Redux Fundamentals, Part 1: Redux Overview | Redux

 

Redux 시작하기

1. Redux 설치하기

Redux Toolkit을 설치한다.

// NPM
npm install @reduxjs/toolkit react-redux

// Yarn
yarn add @reduxjs/toolkit

 

2. Reducer 생성하기

상태를 관리할 JavaScript 파일을 생성(store.js)하고, createSlice 함수를 사용하여 리듀서를 생성한다. createSlice는 상태와 액션을 함께 관리할 수 있도록 한다.

  • let 슬라이스 이름 = createSlice({
    name : "state 이름", 
    initialState : state 설정,
    reducers: { 실행하고자하는 함수; }

    })
// store.js 
import {createSlice} from "@reduxjs/toolkit";

// num 이라는 슬라이스 이름 생성
let numSlice = createSlice({
    name: 'num', //해당 값의 이름 설정
    initialState : 3, //값 설정
    reducers : { // 실제 동작하는 함수의 집합
        increaseNum(state, action){
            // state = 저장되어 있는 데이터 
            // action.payload = 상태 변경에 필요한 추가 정보를 담고있다
            return state + action.payload;
        },
        decreaseNum(state, action) {
            return state - action.payload;
        }
    }
});

 

3. Action 추출하기 & Store 설정하기

 createSlice로 생성한 리듀서를 사용하여 Redux 스토어를 설정한다.  Action의 경우 구조분해할당을 통해서 생성한 슬라이스에서 액션을 추출할 수 있다. 

  • export const { 함수명 } = 슬라이스 이름.actions;
  • export default configureStore({
       reducer : {
            리듀서 이름 : 슬라이스 이름
       }
    })
// store.js

// 구조분해 할당을 통해 action 추출 
export const { increaseNum, decreaseNum } = numSlice.actions;

// store에 저장, 'num'라는 키로 슬라이스를 스토어에 추가 
export default configureStore({
    reducer: {
        num : numSlice.reducer
    }
})

 

<< store.js 전체 코드 >>

더보기
// store.js 
import {configureStore, createSlice} from "@reduxjs/toolkit";

// num 이라는 슬라이스 이름 생성
let numSlice = createSlice({
    name: 'num', //해당 값의 이름 설정
    initialState : 3, //값 설정
    reducers : { // 실제 동작하는 함수의 집합
        increaseNum(state, action){
            // state = 저장되어 있는 데이터 
            // action.payload = 상태 변경에 필요한 추가 정보를 담고있다
            return state + action.payload;
        },
        decreaseNum(state, action) {
            return state - action.payload;
        }
    }
});

// 구조분해 할당을 통해 action 추출 
export const { increaseNum, decreaseNum } = numSlice.actions;

// store에 저장
export default configureStore({
    reducer: {
        num : numSlice.reducer
    }
})

 

4. <Provider> 컴포넌트로 Redux를 사용할 컴포넌트를 감싸기

Redux 스토어를 React 애플리케이션에 제공하기 위해 Provider로 감싸고, 사용할 store를 전달한다. 

import { StrictMode } from 'react'
import { createRoot } from 'react-dom/client'
import App from './App.jsx'
import './index.css'
import { Provider } from 'react-redux'
import store from './store.js'

createRoot(document.getElementById('root')).render(
  <StrictMode>
    <Provider store={store}>
      <App />
    </Provider>
  </StrictMode>,
)

 

 

5. Redux 사용하기

useSelector를 통해서 스토어의 상태를 선택하여 컴포넌트에서 사용할 수 있고, useDispatch를 통해서 action을 받아올 수 있다. 

  • const 변수명 = useSelector(state => state.리듀서이름);
  • const 변수명 = useDispatch();
import { useSelector, useDispatch } from 'react-redux';
import { increaseNum, decreaseNum } from '../store';

const Inner = () => {
    const numValue = useSelector(state => state.num); // 상태 선택
    const dispatch = useDispatch(); // 액션 디스패치 준비

    // initial 값에 1을 더함
    const handleIncrease = () => {
        dispatch(increaseNum(1)); 
    };

    const handleDecrese = () => {
        dispatch(decreaseNum(1))
    }

    return (
        <div>
            현재 값: {numValue}
            <button onClick={handleIncrease}>증가</button>
            <button onClick={handleDecrese}>감소</button>
        </div>
    );
};

export default Inner;

 

 

State를 가지고 있는 해당하는 컴포넌트(Inner.jsx)만 렌더링되고 나머지는 렌더링 되지 않는 모습을 볼 수 있다.