본문 바로가기
Dev/React

[React] Redux Test Count실습

by 컴포넌트설계자 2026. 4. 10.

📦 Redux & Redux Toolkit 완벽 정리

1. Redux의 본질 (왜 쓰는가?)

Redux는 자바스크립트 앱을 위한 **'중앙 집중식 상태 관리 라이브러리'**. 컴포넌트끼리 복잡하게 데이터를 주고받을 필요 없이, 하나의 거대한 창고(Store)에서 모든 상태를 관리

2. 핵심 용어 정리 (4가지 핵심 개념)

이 흐름을 이해하는 것이 가장 중요합니다:

  • Store (스토어): 상태가 저장되는 단 하나의 중앙 저장소.
  • Action (액션): 상태를 어떻게 변경할지 적어놓은 주문서 (무엇을 할지 type, 어떤 데이터를 보낼지 payload).
  • Reducer (리듀서): 주문서(Action)를 보고 실제로 상태를 갈아끼우는 순수 함수.
  • Dispatch (디스패치): 주문서(Action)를 스토어에 전달하는 함수.

3. React-Redux (리액트와 연결하기)

Redux는 리액트 전용이 아니기 때문에, 리액트에서 편하게 쓰려면 React-Redux라는 다리가 필요합니다.

  • <Provider store={store}>: 리액트 앱 전체에 스토어를 공급합니다.
  • useSelector(): 스토어에서 필요한 데이터를 조회할 때 사용합니다.
  • useDispatch(): 액션을 발생시켜 데이터를 변경할 때 사용합니다.

4. Redux Toolkit (RTK - 최신 권장 방식)

기존 Redux의 코드가 너무 길고 복잡해서 이를 단순하게 만든 것이 RTK입니다.

  • createSlice(): 액션과 리듀서를 한 방에 만듭니다. 이미지 속 설명처럼 스토어를 작은 slice(조각) 단위로 나누어 관리하게 해줍니다.
  • configureStore(): 여러 리듀서를 합치고 스토어를 만드는 설정을 아주 단순하게 줄여줍니다.
  • createAsyncThunk(): API 호출 같은 비동기 작업을 쉽게 처리하게 돕습니다.
  • 1. Redux Toolkit(RTK)이란?
    • 목적: 기존 Redux의 복잡한 설정과 반복되는 코드(보일러플레이트)를 줄이기 위해 Redux 팀에서 공식적으로 만든 권장 도구 모음입니다.
    • 장점: 설정이 단순하고, 불변성 유지(Immutability) 등을 자동으로 처리해 줍니다.
    2. 핵심 API (세 가지 보물)
    • configureStore(): 스토어(Store)를 설정하는 함수입니다. 여러 개의 리듀서를 합치거나 미들웨어를 추가하는 작업을 한 번에 처리합니다.
    • createSlice(): 가장 중요한 부분입니다. 액션 생성자(Action Creator)와 리듀서(Reducer)를 한 곳에서 동시에 생성합니다. 이미지 속 그림처럼 스토어 안의 작은 조각(Slice)들을 만드는 역할을 합니다.
    • createAsyncThunk(): 비동기 작업(API 통신 등)을 처리할 때 사용합니다.
    3. 컴포넌트에서 사용하는 법 (.jsx)
    • useSelector(): 스토어에 있는 데이터를 조회(가져오기) 할 때 사용합니다.
    • useDispatch(): 액션을 발생시켜 데이터를 변경(업데이트) 요청을 보낼 때 사용합니다.
  • 스토어에 저장된 데이터를 가져오거나 변경할 때 다음 두 가지 Hook을 사용합니다.

💡 한 줄 요약 흐름

"RTK(createSlice)로 만든 상태 조각들을 스토어(configureStore)에 모으고, 컴포넌트에서는 useSelector로 읽고 useDispatch로 수정한다!"

 

app.jsx

import { useDispatch, useSelector } from 'react-redux'
import './App.css'
import  { up, down, increamentByNo} from './redux/store'
import Cart from './Cart'



function App() {
  const no = useSelector((state)=>{
    console.log("----")
    console.log(state)
    console.log("---")
    return state.count.no;
  })

  const dispatch = useDispatch();
  console.log(dispatch)
return (
<>
<h1>React-Redux TEST</h1>
{/* 카운트 UI 영역 */}
<div className="Count">
{/* 버튼 클릭 시 각각 액션을 디스패치 */}
<button onClick={()=>dispatch(down())}>빼기</button>
{/* 현재 Redux store에 저장된 상태 값 표시 */}
<span> {no} </span>
<button onClick={()=>dispatch(up())}>더하기</button>
{/* 5만큼 증가시키는 커스텀 액션 실행 */}
<button onClick={()=>dispatch(increamentByNo())}>5씩 증가</button>
</div>

<hr />
<Cart/>
</>
)
}
export default App

store.jsx
countSlice 선언, cartSlice 선언

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


//작은 단위(slice )형태로 state 정보를 정의한다.
const countSlice = createSlice({
 name: "count",
    initialState:{no:0},
    reducers: {
        up(state){
            console.log("up" +state)
            state.no = state.no+1;
        },
        down(state){
            console.log("down" + state)
            state.no = state.no -1;
        },
        increamentByNo(state,action){
            console.log(state, action)
            state.no = state.no +action.payload;
        }
    }
});

const cartSlice = createSlice({
    name: "cart",
    initialState:[ {id:1, name:"수박", count:3},
        {id:3, name:"딸기", count:5},
        {id:2, name:"포도", count:8} ],
        reducers:{
            addCount(state, action){
                let index = state.findIndex((item)=>item.id === action.payload)
                state[index].count++;

            },
            addItem(state, action){
                state.push(action.payload);
            },
            sortByName(state){
                state.sort((a,b)=>a.name >b.name ? 1: -1)
            }
        }

    })

export const {up, down, increamentByNo} = countSlice.actions;
export const {addCount, addItem, sortByName} = cartSlice.actions;
 
export default configureStore({
    reducer:{
        count : countSlice.reducer,
        cart : cartSlice.reducer
    }
})



Cart.jsx

import {useDispatch, useSelector} from 'react-redux'
import { sortByName , addCount} from './redux/store';

const Cart = () => {
let state = useSelector((state) => state)
console.log(state.count);
console.log(state.cart[0].name);

// dispatch는 store.js 로 요청보내주는 함수
let dispatch = useDispatch()
return (
<div>
<h5> 장바구니</h5>
<table style={{border:"1px solid red"}}>
<thead>
<tr>
<th>id</th>
<th>상품명</th>
<th>수량</th>
<th>변경하기</th>
</tr>
</thead>
<tbody>
{
state.cart.map((item, i)=>
<tr key={item.id}>
<td>{item.id}</td>
<td>{item.name}</td>
<td>{item.count}</td>
<td><button onClick={()=>dispatch(addCount(item.id))}>+</button></td>
</tr>
)
}
</tbody>
</table>
<br/>
<button onClick={()=>dispatch(sortByName()) }>이름순정렬</button>{' '}
</div>
);
};
export default Cart;

 

main.jsx

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


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

    <App />
  </Provider>,
)