생활코딩! React 리액트 프로그래밍

egoing - redux tool kit - stackblitz ver

lshjju 2025. 9. 6. 22:33

React.js - redux tool kit


https://lshjju.tistory.com/152

 

React.js - redux toolkit- 리덕스툴킷

Redux Toolkit(리덕스 툴킷)이란 무엇인가요?Redux Toolkit은 Redux(리덕스)를 더 쉽고 효율적으로 사용하기 위해 만들어진 **공식적으로 권장하는 '도구 모음'**입니다. Redux 자체는 강력하지만, 설정하고

lshjju.tistory.com


https://lshjju.tistory.com/162

 

reducer와 reducers의 차이

React에서 reducer (단수)와 reducers (복수)의 차이에 대해 알기 쉽게 설명해 드릴게요. 특히 Redux Toolkit의 createSlice를 사용할 때 이 두 단어가 자주 나와 헷갈리실 수 있는데, 아주 간단해요!마치 **요리

lshjju.tistory.com


https://lshjju.tistory.com/160

 

reducer / redux / redux-toolkit

reducer / redux / redux-toolkit 관계와 차이 React에서 reducer, Redux, 그리고 Redux Toolkit은 상태 관리와 관련해서 서로 깊은 관계를 가지고 있어요. 이걸 마치 요리에 비유해서 알기 쉽게 설명해 드릴게요.

lshjju.tistory.com



Final build


https://stackblitz.com/edit/vitejs-vite-ufskmlaq?file=src%2FApp.jsx

 

egoing - redux tool kit - StackBlitz

Next generation frontend tooling. It's fast!

stackblitz.com



New project building


https://lshjju.tistory.com/104

 

StackBlitz web editor - 스택블리츠 웹 에디터

스택블리츠로 새 프로젝트 생성하기 https://stackblitz.com/ 로그인 방법 선택 대쉬보드에서 new project + 버튼 - 탭 모달 - frontend 탭 - 탭react javascript - 탭 바이트 리액트가 생성됩니다.프로젝트 생성 완

lshjju.tistory.com



카운트 데이터바인딩 합니다.


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

 

펑션 앱 위에 펑션 카운터 추가 합니다.


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

 

펑션 카운터 렌더링 합니다.


https://lshjju.tistory.com/150

 

React.js - redux - 리액트 리덕스

Redux(리덕스)란 무엇인가요? Redux는 React(뿐만 아니라 다른 JavaScript 라이브러리/프레임워크)에서 **애플리케이션의 상태(State)를 효율적으로 관리하기 위한 예측 가능한 상태 컨테이너(Predictable Stat

lshjju.tistory.com

 

redux 

react-redux

인스톨 합니다.


import { createStore } from "redux";

 

스토어를 생성하기 위해 크리에이트스토어 레거시 임포트 합니다.

import { createStore } from 'redux'; 
// Redux의 핵심 함수인 createStore를 가져와 상태 저장소(스토어)를 생성합니다.

const store = createStore(reducer, initialState);

 

스토어 스코프는 글로벌이어야 합니다.

그러므로 임포트 아래에 스토어를 초기화 합니다.

파라미터로 리듀서와 이니셜스테이트를 사용합니다.

이렇게 만든 리덕스스토어는 프로바이더로 컴포넌트에 공급될 것입니다.

const store = createStore(reducer, initialState); 
// 위에서 정의한 리듀서와 초기 상태를 사용하여 Redux 스토어를 생성합니다.

const initialState = { value: 0 };

 

스토어는 이니셜스테이트가 필요합니다.

스토어 변수 위에 이니셜스테이트를 오브젝타입으로 초기화 합니다.

const initialState = { value: 0 }; 
// Redux 스토어의 초기 상태를 정의합니다. 'value'는 0으로 시작합니다.

function reducer(state, action) {
  return state;
}

 

스토어는 리듀서도 필숩니다.

임포트 아래에 펑션리듀서를 추가합니다.

이 펑션은 현재상태와 액션을 파라미터로 받아 연산한 결과를 리턴 합니다.

/**
 * Redux 리듀서 함수입니다.
 * 현재 상태(state)와 발생한 액션(action)을 인자로 받아 새로운 상태를 반환합니다.
 * 이 함수는 아직 상태를 변경하는 로직이 구현되어 있지 않으며, 현재 상태를 그대로 반환합니다.
 * @param {object} state - 현재 애플리케이션의 상태
 * @param {object} action - 발생한 액션 객체
 * @returns {object} - 새로운 상태
 */
function reducer(state, action) {
  return state;
}

import { Provider } from "react-redux";

 

컴포넌트와 리덕스스토어를 연결하는 프로바이더 임포트 합니다.


    <Provider store={store}>
      <div>
        <Counter></Counter>
      </div>
    </Provider>

 

펑션앱 디브를 프로바이더로 래핑해서 스토어를 공급 합니다.


import { Provider, useSelector } from "react-redux";

 

스토어 상태를 조회할 수 있는 유즈셀렉터를 임포트 합니다.

import { Provider, useSelector } from 'react-redux'; 
// React 애플리케이션에 Redux 스토어를 연결하는 Provider와, 스토어의 상태를 컴포넌트에서 조회할 수 있는 useSelector Hook을 가져옵니다.

const count = useSelector((state) => state.value);

 

펑션카운터 리턴 위에 유즈셀렉터로 카운트를 초기화 합니다.

이제 펑션카운터와 스토어가 연결되었습니다.

파라미터에 스테이트를 리턴하는 펑션도 살짝 껴줍니다.

즉 이 컴포넌트에서 스토어 밸류를 가져 와서 조회할 수 있습니다.

  // useSelector Hook을 사용하여 Redux 스토어의 'value' 상태를 가져옵니다.
  // 이 값은 'count' 변수에 할당되어 컴포넌트 내에서 사용됩니다.
  const count = useSelector((state) => state.value);

    <div>
      <button>+</button> {count}
    </div>

 

펑션카운터에 카운트를 데이터 바인딩 합니다.


test

카운터가 표시 되는지 체크 합니다.


Completion

import { useState } from 'react';
import reactLogo from './assets/react.svg';
import viteLogo from '/vite.svg';
import './App.css';
import { createStore } from 'redux';
import { Provider, useSelector } from 'react-redux';

function reducer(state, action) {
  return state;
}

const initialState = { value: 0 };
const store = createStore(reducer, initialState);

function Counter() {
  const count = useSelector((state) => state.value);
  return (
    <div>
      <button>+</button> {count}
    </div>
  );
}
export default function App() {
  return (
    <Provider store={store}>
      <div>
        <Counter></Counter>
      </div>
    </Provider>
  );
}


버튼을 탭해서 데이터를 증가 시킵니다.


import { Provider, useSelector, useDispatch } from "react-redux";

 

액션을 보내기 위한 유즈디스패치를 임포트 합니다.

import { Provider, useSelector, useDispatch } from "react-redux"; 
// useDispatch: Redux store로 액션(action)을 보낼 수 있게 하는 Hook입니다.

  const dispatch = useDispatch();

 

펑션카운터 카운트변수 위에 디스패치를 유즈디스패치로 초기화 합니다.

지금부터 유즈디스패치를 사용할 수 있습니다.

  // useDispatch Hook을 사용하여 Redux store로 액션을 보낼 수 있는 dispatch 함수를 가져옵니다.
  const dispatch = useDispatch();

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

 

펑션카운터 리턴 내부 버튼에 이벤트를 겁니다.

액션을 리듀서로 디스패치 합니다.

그러면 펑션리듀서는 상태를 업테이트 합니다.

왜냐하면 그렇게 약속되어 있기 때문입니다.

액션 보낼 때 오브젝 타입으로 스텝도 같이 보냅니다.

    <div>
      <button
        // 버튼 클릭 시 익명 함수가 실행됩니다.
        // 이 함수 내에서 dispatch를 호출하여 Redux store로 액션을 보냅니다.
        // 액션 객체는 type: "up"과 step: 2를 포함하고 있으며, 이는 리듀서가 상태를 업데이트하는 데 사용됩니다.
        onClick={() => {
          dispatch({ type: "up", step: 2 });
        }}
      >
        + {/* 버튼에 표시될 텍스트입니다. */}
      </button>{" "}
      {count} {/* 현재 Redux store에서 가져온 count 값을 화면에 표시합니다. */}
    </div>

function reducer(state, action) {
  if (action.type === "up") {
    return { ...state, value: state.value + action.step };
  }
  return state;
}

 

펑션리듀서에서 타입 액션을 받기 위해 이프문을 추가하고 결과를 리턴합니다.

기존 스테이트를 유지하면서 복제합니다.

오른쪽 연산을 처리합니다.

연산 결과를 왼쪽에 업데이트 합니다.

그 외 액션인 경우 원래 상태 그대로 리턴합니다.

/**
 * Redux 리듀서 함수입니다.
 * 현재 상태(state)와 발생한 액션(action)을 인자로 받아, 액션의 타입에 따라 새로운 상태를 반환합니다.
 * Redux에서 상태 변경은 반드시 이 리듀서 함수를 통해서만 이루어집니다.
 *
 * @param {object} state - 현재 애플리케이션의 상태 객체 (초기값은 initialState입니다).
 * @param {object} action - 상태 변경을 지시하는 객체 (예: { type: "up", step: 2 }).
 * @returns {object} - 변경된 새로운 상태 객체입니다.
 */
function reducer(state, action) {
  // 액션의 타입이 "up"인 경우, 상태의 value를 액션의 step만큼 증가시킵니다.
  // ...state를 사용하여 기존 상태를 불변적으로 유지하면서 필요한 속성만 업데이트합니다.
  if (action.type === "up") {
    return { ...state, value: state.value + action.step };
  }
  // 그 외의 액션 타입에 대해서는 현재 상태를 변경하지 않고 그대로 반환합니다.
  return state;
}

test

카운터를 탭했을 때 숫자가 2씩 증가하는지 체크 합니다.

 


Completion

import { useState } from 'react';
import reactLogo from './assets/react.svg';
import viteLogo from '/vite.svg';
import './App.css';
import { createStore } from 'redux';
import { Provider, useSelector, useDispatch } from 'react-redux';

function reducer(state, action) {
  if (action.type === 'up') {
    return { ...state, value: state.value + action.step };
  }
  return state;
}

const initialState = { value: 0 };
const store = createStore(reducer, initialState);

function Counter() {
  const dispatch = useDispatch();
  const count = useSelector((state) => state.value);
  return (
    <div>
      <button
        onClick={() => {
          dispatch({ type: 'up', step: 2 });
        }}
      >
        +
      </button>{' '}
      {count}
    </div>
  );
}
export default function App() {
  return (
    <Provider store={store}>
      <div>
        <Counter></Counter>
      </div>
    </Provider>
  );
}

 


리덕스툴킷을 설치하는 두 가지 방법


https://lshjju.tistory.com/152

 

React.js - redux toolkit- 리덕스툴킷

Redux Toolkit(리덕스 툴킷)이란 무엇인가요?Redux Toolkit은 Redux(리덕스)를 더 쉽고 효율적으로 사용하기 위해 만들어진 **공식적으로 권장하는 '도구 모음'**입니다. Redux 자체는 강력하지만, 설정하고

lshjju.tistory.com



리덕스를 리덕스툴킷으로 업그레이드 합니다.

크리에이트슬라이스로 스토어를 관리 합니다.


npm install @reduxjs/toolkit

 

@reduxjs/toolkit 을 설치합니다.


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

 

리듀서/액션타입 관리 도구 크리에잇슬라이스 임포트 합니다.

import { createSlice } from "@reduxjs/toolkit"; 
// createSlice: Redux 리듀서와 액션 타입을 한 번에 정의할 수 있도록 도와주는 유틸리티 함수입니다.

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

 

임포트 아래에 기존의 리듀서를 대신 할  슬라이스를 세팅합니다.

슬라이스 이름 세팅 합니다.

이것은 액션타입의 접두사로 사용합니다.

슬라이스 초기값 세팅 합니다.

리듀서스 세팅합니다.

업 액션이 디스패치 되면 콜 할 펑션을 세팅 합니다.

... 로 원본데이터 복제할 필요 없습니다.

리덕스툴킷에 이 일을 대신 해 주는 내장 기능이 있습니다.

연산해서 데이터를 갱신 합니다.

/**
 * createSlice를 사용하여 Redux 상태 슬라이스(slice)를 정의합니다.
 * 이 함수는 액션 타입, 액션 생성자, 그리고 리듀서 로직을 한데 모아 관리할 수 있게 해줍니다.
 */
const counterSlice = createSlice({
  name: "counter", // 이 슬라이스의 이름입니다. 액션 타입의 접두사로 사용됩니다 (예: "counter/up").
  initialState: { value: 0 }, // 이 슬라이스의 초기 상태입니다. 'value'는 0으로 시작합니다.
  reducers: {
    // 리듀서 함수들을 정의합니다. 여기서 각 함수는 특정 액션에 대한 상태 변경 로직을 포함합니다.
    // Redux Toolkit에서는 Immer 라이브러리가 내장되어 있어, 상태를 직접 '변경'하는 것처럼 코드를 작성해도 불변성을 유지합니다.
    up: (state, action) => {
      // 'up' 액션이 디스패치될 때 실행되는 리듀서 로직입니다.
      // `action.step` 값만큼 `state.value`를 증가시킵니다.
      state.value = state.value + action.step;
    },
  },
});

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

 

스토어를 효율적으로 세팅하기 위해 컨피규어스토어를 임포트 합니다.

import { createSlice, configureStore } from "@reduxjs/toolkit"; 
// configureStore: Redux 스토어 설정을 간소화하고, 기본적으로 Redux DevTools 확장 프로그램 통합 및 일부 미들웨어를 포함하여 스토어를 생성해주는 함수입니다.

/*
function reducer(state, action) {
  if (action.type === "up") {
    return { ...state, value: state.value + action.step };
  }
  return state;
}

const initialState = { value: 0 };
const store = createStore(reducer, initialState);
*/

 

리듀서를 슬라이스로 업그레이드 할 것입니다.

그러므로 리듀서/이니셜스테이트/스토어를 주석처리 합니다.


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

 

리덕스를 리덕스툴킷으로 업그레이드 합니다.

컨피규어 스토어는 리덕스 스토어 콘트롤 타워 입니다.

카운터슬라이스 아래에 스토어를 세팅 합니다.

카운터슬라이스를 카운터에 세팅해서 관리 합니다.

이제부터 이 스토어를 원하는 곳에 공급할 수 있습니다.

이 스토어에 담긴 특정 데이터를 사용하려면 유즈셀렉터로 지정하여 사용할 수 있습니다.

/**
 * configureStore를 사용하여 Redux 스토어를 설정하고 생성합니다.
 * Redux Toolkit을 사용하면 스토어 설정을 훨씬 간편하게 할 수 있습니다.
 */
const store = configureStore({
  reducer: {
    // 여러 슬라이스의 리듀서들을 결합하여 루트 리듀서를 만듭니다.
    // 'counter'라는 이름 아래에 counterSlice의 리듀서를 할당합니다.
    counter: counterSlice.reducer,
  },
});

  const count = useSelector((state) => {
    return state.counter.value;
  });

 

펑션카운터 내부에서 카운터 슬라이스가 연산한 스테이트 밸류를 데이터로 사용해야 합니다.

유즈셀렉터로 그것을 가져옵니다.

그 데이터로 카운트를 초기화 합니다.

현재 유저는 이벤트를 발생시키지 않았습니다.

즉, 디스패치가 실행되지 않았습니다.

그러므로 카운트 데이터는 이니셜스테이트 밸류인 0이 찍힐 것입니다.

  // useSelector Hook을 사용하여 Redux 스토어의 상태에서 필요한 데이터를 선택합니다.
  // configureStore에서 'counter'라는 이름으로 리듀서를 할당했으므로,
  // 상태에 접근할 때 `state.counter.value`와 같이 경로를 지정해야 합니다.
  const count = useSelector((state) => {
    return state.counter.value;
  });

test

0이 데이터 바인딩 되는지 체크 합니다.


Completion

import { useState } from 'react';
import reactLogo from './assets/react.svg';
import viteLogo from '/vite.svg';
import './App.css';
import { createStore } from 'redux';
import { Provider, useSelector, useDispatch } from 'react-redux';
import { configureStore, createSlice } from '@reduxjs/toolkit';

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

const store = configureStore({
  reducer: {
    counter: couterSlice.reducer,
  },
});

/*
function reducer(state, action) {
  if (action.type === 'up') {
    return { ...state, value: state.value + action.step };
  }
  return state;
}

const initialState = { value: 0 };
const store = createStore(reducer, initialState);
*/

function Counter() {
  const dispatch = useDispatch();
  const count = useSelector((state) => {
    return state.counter.value;
  });
  return (
    <div>
      <button
        onClick={() => {
          dispatch({ type: 'up', step: 2 });
        }}
      >
        +
      </button>{' '}
      {count}
    </div>
  );
}
export default function App() {
  return (
    <Provider store={store}>
      <div>
        <Counter></Counter>
      </div>
    </Provider>
  );
}


데이터를 증가 시킵니다.


  name: "counterSlice",

 

스토어 counter와 카운터슬라이스 네임이 같습니다.

헷갈리지 않도록 카운터슬라이스 네임을 - counter에서 couterSlice로 변경합니다.


        onClick={() => {
          dispatch({ type: "counterSlice/up", step: 2 });
        }}

 

펑션카운터 온클릭메소드 디스패치 타입 up을 counterSlice/up으로 변경합니다.

counterSlice는 카운터슬라이스 변수 내부에 있는 네임입니다.

유저가 탭하면 타입과 스텝을 스토어로 디스패치 합니다.

그리고 슬라이스가 리턴한 밸류가 새로운 스테이트가 됩니다.

이제부터 플러스 유아이를 탭하면 값이 증가합니다.

        // 버튼 클릭 시 호출되는 이벤트 핸들러입니다.
        // dispatch 함수를 사용하여 'counterSlice/up' 타입의 액션과 'step' 값(2)을 포함하는 액션 객체를 스토어로 보냅니다.
        // 이 액션은 counterSlice 내의 'up' 리듀서에 의해 처리됩니다.
        // Redux Toolkit에서는 `counterSlice.actions.up(2)`와 같이 액션 생성자를 사용하는 것이 권장되는 방식입니다.
        onClick={() => {
          dispatch({ type: "counterSlice/up", step: 2 });
        }}

test

플러스 탭 체크 합니다.


Completion

import { useState } from 'react';
import reactLogo from './assets/react.svg';
import viteLogo from '/vite.svg';
import './App.css';
import { createStore } from 'redux';
import { Provider, useSelector, useDispatch } from 'react-redux';
import { configureStore, createSlice } from '@reduxjs/toolkit';

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

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

/*
function reducer(state, action) {
  if (action.type === 'up') {
    return { ...state, value: state.value + action.step };
  }
  return state;
}

const initialState = { value: 0 };
const store = createStore(reducer, initialState);
*/

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>
  );
}
export default function App() {
  return (
    <Provider store={store}>
      <div>
        <Counter></Counter>
      </div>
    </Provider>
  );
}

 


디스패치를 액션스 오브젝으로 업그레이드 합니다.


        onClick={() => {
          dispatch(counterSlice.actions.up(2));
        }}


펑션카운터 디스패치를 액션스코드로 변경 합니다.

리덕스툴킷은 리듀서 펑션을 참고해서 자동으로 액션을 생성하는 액선크리에이터를 생성해 줍니다.

즉, 크리에잇슬라이스는 리듀서펑션 액션을 액션스 오브젝에 자동 생성해 줍니다.

{ type: "counterSlice/up", payload: 2 } 이것이 액션스 오브젝에 세팅 합니다.

이것은 리덕스 툴킷이 권장하는 코드입니다.

        // `dispatch(counterSlice.actions.up(2))`는 Redux Toolkit에서 권장하는 액션 디스패치 방식입니다.
        // `createSlice`는 자동으로 리듀서 함수에 해당하는 액션 생성자들을 `.actions` 객체 안에 생성합니다.
        // `counterSlice.actions.up(2)`는 `{ type: "counterSlice/up", payload: 2 }`와 같은 액션 객체를 생성하여 반환하고,
        // 이를 `dispatch` 함수가 스토어로 보냅니다. 이는 수동으로 액션 타입을 지정하는 것보다 오류 발생 가능성을 줄여줍니다.
        onClick={() => {
          // dispatch({ type: "counterSlice/up", step: 2 }); // 이전 방식 (주석 처리됨)
          dispatch(counterSlice.actions.up(2)); // Redux Toolkit 권장 방식: 액션 생성자 사용
        }}

  reducers: {
    up: (state, action) => {
      state.value = state.value + action.payload;
    },
  }

 

카운터슬라이스에서 업으로 디스패치 받았습니다.

페이로드는 액션크리에이터 실행 시 데이터를 받는 프로퍼티 입니다.

즉, 페이로드 데이터 만큼 데이터를 증가시킵니다.

그래서 툴킷은 액션을 직접 코딩하지 않고 자동으로 만들어 주는 이점을 제공 합니다.

  reducers: {
    // 리듀서 함수들을 정의합니다. 각 함수는 특정 액션에 대한 상태 변경 로직을 포함합니다.
    // Redux Toolkit에서는 Immer 라이브러리가 내장되어 있어, 상태를 직접 '변경'하는 것처럼 코드를 작성해도 불변성을 유지합니다.
    up: (state, action) => {
      // 'up' 액션이 디스패치될 때 실행되는 리듀서 로직입니다.
      // `action.payload`는 Redux Toolkit에서 권장하는 표준 방식으로, 액션과 함께 전달되는 주요 데이터를 포함합니다.
      // 여기서는 payload의 값만큼 `state.value`를 증가시킵니다.
      state.value = state.value + action.payload;
    },
  },

test

잘 작동하는지 체크합니다.


Completion

import { useState } from 'react';
import reactLogo from './assets/react.svg';
import viteLogo from '/vite.svg';
import './App.css';
import { createStore } from 'redux';
import { Provider, useSelector, useDispatch } from 'react-redux';
import { configureStore, createSlice } from '@reduxjs/toolkit';

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

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

/*
function reducer(state, action) {
  if (action.type === 'up') {
    return { ...state, value: state.value + action.step };
  }
  return state;
}

const initialState = { value: 0 };
const store = createStore(reducer, initialState);
*/

function Counter() {
  const dispatch = useDispatch();
  const count = useSelector((state) => {
    return state.counter.value;
  });
  return (
    <div>
      <button
        onClick={() => {
          dispatch(counterSlice.actions.up(2));
        }}
      >
        +
      </button>{' '}
      {count}
    </div>
  );
}
export default function App() {
  return (
    <Provider store={store}>
      <div>
        <Counter></Counter>
      </div>
    </Provider>
  );
}

Comment ver

import { useState } from 'react'; // React에서 상태 관리를 위한 useState Hook을 가져옵니다. (현재 코드에서는 직접 사용되지 않습니다.)
import reactLogo from './assets/react.svg'; // React 로고 SVG 파일을 가져옵니다. (현재 코드에서는 사용되지 않습니다.)
import viteLogo from '/vite.svg'; // Vite 로고 SVG 파일을 가져옵니다. (현재 코드에서는 사용되지 않습니다.)
import './App.css'; // 이 컴포넌트에 적용될 CSS 스타일 파일을 가져옵니다. (스타일 관련 파일입니다.)
import { createStore } from 'redux'; // Redux의 핵심 함수인 createStore를 가져옵니다. (현재 코드에서는 Redux Toolkit의 configureStore로 대체되어 사용되지 않습니다.)
import { Provider, useSelector, useDispatch } from 'react-redux'; // React 애플리케이션에 Redux 스토어를 연결하는 react-redux 라이브러리에서 다음을 가져옵니다.
// Provider: Redux 스토어를 React 컴포넌트 트리에 제공하여 모든 하위 컴포넌트가 스토어에 접근할 수 있도록 합니다.
// useSelector: Redux 스토어의 상태에서 필요한 데이터를 선택하여 가져올 수 있게 하는 Hook입니다.
// useDispatch: Redux 스토어로 액션(Action)을 디스패치(Dispatch)할 수 있게 하는 Hook입니다.
import { configureStore, createSlice } from '@reduxjs/toolkit'; // Redux Toolkit에서 다음을 가져옵니다.
// configureStore: Redux 스토어 설정을 간소화하고, 개발 도구 및 미들웨어 통합을 용이하게 하는 함수입니다.
// createSlice: 리듀서, 액션 타입, 액션 생성자 등을 간결하게 정의하고 관리할 수 있도록 돕는 유틸리티 함수입니다.

/**
 * Redux Toolkit의 `createSlice`를 사용하여 'counterSlice'라는 이름의 상태 슬라이스를 정의합니다.
 * 이 함수는 특정 기능(여기서는 카운터)에 대한 상태, 액션, 리듀서를 한 곳에 모아 관리하며 Redux 개발을 간소화합니다.
 */
const counterSlice = createSlice({
  name: 'counterSlice', // 이 슬라이스의 이름입니다. 생성되는 액션 타입의 접두사로 사용됩니다 (예: "counterSlice/up").
  initialState: { value: 0 }, // 이 슬라이스의 초기 상태를 정의합니다. 'value'는 0으로 시작합니다.
  reducers: {
    // 리듀서 함수들을 정의합니다. 각 함수는 특정 액션에 대한 상태 변경 로직을 포함합니다.
    // Redux Toolkit은 Immer 라이브러리를 내장하고 있어, 상태를 직접 '수정'하는 것처럼 코드를 작성해도 내부적으로 불변성을 유지합니다.
    up: (state, action) => {
      // 'up' 액션이 디스패치될 때 실행되는 리듀서 로직입니다.
      // `action.payload`는 Redux Toolkit에서 액션과 함께 전달되는 주요 데이터를 담는 표준 속성입니다.
      // 여기서는 `payload`의 값만큼 `state.value`를 증가시킵니다.
      state.value = state.value + action.payload;
    },
  },
});

/**
 * Redux Toolkit의 `configureStore`를 사용하여 애플리케이션의 메인 Redux 스토어를 설정하고 생성합니다.
 * 이 함수는 Redux 스토어의 복잡한 설정을 자동화하여 개발 편의성을 높입니다.
 */
const store = configureStore({
  reducer: {
    // 애플리케이션의 모든 리듀서들을 통합하여 루트 리듀서를 구성합니다.
    // 'counter'라는 키 아래에 `counterSlice`에서 생성된 리듀서를 할당합니다.
    // 이렇게 함으로써 `state.counter`를 통해 해당 슬라이스의 상태에 접근할 수 있게 됩니다.
    counter: counterSlice.reducer,
  },
});

/**
 * 카운터 값을 표시하고 증가시키는 기능을 가진 React 컴포넌트입니다.
 */
function Counter() {
  // `useDispatch` Hook을 사용하여 Redux 스토어로 액션을 보내는 `dispatch` 함수를 가져옵니다.
  const dispatch = useDispatch();
  // `useSelector` Hook을 사용하여 Redux 스토어의 현재 상태에서 'counter' 슬라이스의 'value' 값을 선택하여 가져옵니다.
  // 이 값은 `count` 변수에 할당되어 컴포넌트 렌더링에 사용되며, 상태가 변경될 때 컴포넌트가 자동으로 리렌더링됩니다.
  const count = useSelector((state) => {
    return state.counter.value;
  });

  return (
    <div>
      <button
        // 버튼 클릭 시 호출되는 이벤트 핸들러입니다.
        // `dispatch(counterSlice.actions.up(2))`는 Redux Toolkit에서 권장하는 액션 디스패치 방식입니다.
        // `createSlice`는 리듀서 함수에 해당하는 액션 생성자들을 `.actions` 객체 안에 자동으로 생성합니다.
        // `counterSlice.actions.up(2)`를 호출하면 `{ type: "counterSlice/up", payload: 2 }`와 같은 액션 객체가 생성되어 반환되며,
        // 이 액션 객체를 `dispatch` 함수가 스토어로 보냅니다. 이는 수동으로 액션 타입을 지정하는 것보다 타입 안정성과 코드 가독성을 높여줍니다.
        onClick={() => {
          dispatch(counterSlice.actions.up(2));
        }}
      >
        + {/* 버튼에 표시될 텍스트입니다. */}
      </button>{' '}
      {count} {/* 현재 Redux 스토어에서 가져온 count 값을 화면에 표시합니다. */}
    </div>
  );
}

/**
 * 애플리케이션의 최상위 컴포넌트입니다.
 * Redux 스토어를 전체 React 컴포넌트 트리에 연결합니다.
 */
export default function App() {
  return (
    // `Provider` 컴포넌트를 사용하여 생성된 Redux 스토어(`store`)를 하위 컴포넌트들에게 제공합니다.
    // `Provider` 내부에 렌더링되는 모든 컴포넌트는 `react-redux`의 Hook(`useSelector`, `useDispatch`)을 통해
    // 스토어 상태에 접근하고 액션을 디스패치할 수 있게 됩니다.
    <Provider store={store}>
      <div>
        <Counter></Counter> {/* Counter 컴포넌트를 렌더링합니다. */}
      </div>
    </Provider>
  );
}


코드를 분리 합니다.


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;
export const { up } = counterSlice.actions;

 

counterSlice.jsx

 

카운터슬라이스를 파일로 분리 합니다.

리덕스툴킷 임포트 합니다.

앱에서 카운터슬라이스를 컷하고 페이스트 합니다.

카운터슬라이스 익스포트 합니다.

App.js 코드를 간결하게 하기 위해 counterSlice.actions를 up 에 담아서 익스포트 합니다.

 

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

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

export default store;

 

stoer.jsx

 

스토어를 파일로 분리 합니다.

리덕스툴킷 임포트 합니다.

카운터슬라이스 임포트 합니다.

앱에서 스토어를 컷하고 페이스트 합니다.

스토어 익스포트 합니다.


import store from "./store";
import { up } from "./counterSlice";

 

App.jsx

 

스토어와 카운트슬라이스 임포트 합니다.

      <button
        onClick={() => {
          dispatch(up(2));
        }}
      >
        +
      </button>{" "}

 

App.jsx


디스패치 코드 체지방을 줄입니다.


test

코드를 분리하고 잘 작동하는지 체크 합니다.


Completion

import { useState } from 'react';
import reactLogo from './assets/react.svg';
import viteLogo from '/vite.svg';
import './App.css';
import { createStore } from 'redux';
import { Provider, useSelector, useDispatch } from 'react-redux';
import { configureStore, createSlice } from '@reduxjs/toolkit';
import store from './store';
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>
  );
}
export default function App() {
  return (
    <Provider store={store}>
      <div>
        <Counter></Counter>
      </div>
    </Provider>
  );
}