Redux는 JavaScript 상태 관리 라이브러리 입니다.
필요성:
react 에서 state 는 component 안에서 관리됩니다.
리액트 특성상 단방향 데이터 바인딩이기 때문에, 하위 컴포넌트에게 state를 전달 할 수 있습니다.
이를 props 라고 하며, component 구조가 깊어지면 props 를 계속 전달해야 하는 'Props drilling' 이슈가 있습니다.
또한 props 되는 component 가 많아지면 state 관련 오류 발생시 조치하기 어려워지는 문제점도 있습니다.
이때, redux를 사용하여 state를 관리하면 위와 같은 문제를 개선 할 수 있습니다.
예시코드 .gpt
Redux 를 사용해서 버튼 숫자 count 를 state 로 관리하고 '좋아요' 버튼 클릭시 count 가 1 증가하며, '싫어요' 버튼 클릭시 count 가 1 감소하는 코드 만들어줘
// 커널
npm install redux react-redux
폴더 구조
my-app/
├── public/
│ ├── index.html
│ └── ...
├── src/
│ ├── actions/
│ │ ├── actionTypes.js
│ │ └── actions.js
│ ├── components/
│ │ └── ButtonCounter.js
│ ├── reducers/
│ │ └── reducers.js
│ ├── store/
│ │ └── store.js
│ ├── App.js
│ └── index.js
└── package.json
// actionTypes.js
export const INCREMENT_COUNT = 'INCREMENT_COUNT';
export const DECREMENT_COUNT = 'DECREMENT_COUNT';
// actions.js
import { INCREMENT_COUNT, DECREMENT_COUNT } from './actionTypes';
export const incrementCount = () => ({
type: INCREMENT_COUNT
});
export const decrementCount = () => ({
type: DECREMENT_COUNT
});
// reducers.js
import { INCREMENT_COUNT, DECREMENT_COUNT } from '../actions/actionTypes';
const initialState = {
count: 0
};
const countReducer = (state = initialState, action) => {
switch (action.type) {
case INCREMENT_COUNT:
return {
...state,
count: state.count + 1
};
case DECREMENT_COUNT:
return {
...state,
count: state.count - 1
};
default:
return state;
}
};
export default countReducer;
// store.js
import { createStore } from 'redux';
import countReducer from '../reducers/reducers';
const store = createStore(countReducer);
export default store;
// ButtonCounter.js
import React from 'react';
import { connect } from 'react-redux';
import { incrementCount, decrementCount } from '../actions/actions';
const ButtonCounter = ({ count, incrementCount, decrementCount }) => {
return (
<div>
<p>Count: {count}</p>
<button onClick={incrementCount}>좋아요</button>
<button onClick={decrementCount}>싫어요</button>
</div>
);
};
const mapStateToProps = state => ({
count: state.count
});
const mapDispatchToProps = {
incrementCount,
decrementCount
};
export default connect(mapStateToProps, mapDispatchToProps)(ButtonCounter);
// App.js
import React from 'react';
import { Provider } from 'react-redux';
import store from './store/store';
import ButtonCounter from './components/ButtonCounter';
const App = () => {
return (
<Provider store={store}>
<div className="App">
<ButtonCounter />
</div>
</Provider>
);
};
export default App;
Redux Toolkit 사용해서 다시 알려줘
npm install @reduxjs/toolkit
my-app/
├── public/
│ ├── index.html
│ └── ...
├── src/
│ ├── components/
│ │ └── ButtonCounter.js
│ ├── reducers/
│ │ └── counterSlice.js
│ ├── store/
│ │ └── store.js
│ ├── App.js
│ └── index.js
└── package.json
action 이 없어졌음!
Redux Toolkit을 사용하면 코드를 간결하게 유지할 수 있으며, createSlice가 액션 생성자 함수를 자동으로 생성해주기 때문에 명시적으로 정의하지 않아도 되는 장점이 있습니다.
적용 과정
- store 설정
- reducers 작성
- component 에서 사용
- App.js 에 Redux 스토어 적용
1. 스토어 설정
Redux Toolkit에서는 configureStore 함수를 사용하여 Redux 스토어를 설정합니다.
이 함수는 기본적으로 Redux DevTools Extension과 함께 동작하며, 필요한 경우 미들웨어를 추가할 수 있습니다.
store.js
import { configureStore } from '@reduxjs/toolkit';
import counterReducer from '../reducers/counterSlice'; // 예시로 counterSlice 리듀서를 사용하겠습니다.
const store = configureStore({
reducer: {
counter: counterReducer
}
});
export default store;
- configureStore 함수는 Redux 스토어를 설정하는 역할을 합니다.
- reducer 객체를 통해 각 슬라이스(리듀서)를 등록합니다.
- 위 예시에서는 counterReducer를 counter라는 이름으로 등록했습니다.
- counterReducer는 counterSlice에서 생성한 리듀서입니다.
2. 리듀서 작성
Redux Toolkit에서는 createSlice 함수를 사용하여 리듀서와 액션을 한 번에 정의할 수 있습니다.
이 함수는 Immer 라이브러리를 사용하여 불변성을 유지하면서 상태를 업데이트할 수 있습니다.
reducers/counterSlice.js
import { createSlice } from '@reduxjs/toolkit'; // Redux Toolkit에서 createSlice 함수를 import합니다.
const initialState = { // 초기 상태를 정의합니다.
count: 0 // count라는 필드를 가진 초기 상태 객체입니다. 처음 시작할 때 count는 0입니다.
};
const counterSlice = createSlice({ // createSlice 함수를 사용하여 slice를 생성합니다.
name: 'counter', // slice의 이름을 'counter'로 설정합니다.
initialState, // 초기 상태를 사용합니다.
reducers: { // 리듀서 함수들을 정의합니다.
increment: state => { // increment 액션의 경우, 현재 상태를 변경하는 함수입니다.
state.count += 1; // count를 1 증가시킵니다.
},
decrement: state => { // decrement 액션의 경우, 현재 상태를 변경하는 함수입니다.
state.count -= 1; // count를 1 감소시킵니다.
}
}
});
export const { increment, decrement } = counterSlice.actions; // 액션 생성자 함수들을 추출하여 export합니다.
export default counterSlice.reducer; // 리듀서 함수를 default로 export합니다.
- createSlice 함수를 사용하여 Redux 슬라이스를 생성합니다.
- name 속성으로 슬라이스의 이름을 지정합니다. 여기서는 'counter'라는 이름을 사용했습니다.
- initialState로 초기 상태를 정의합니다.
- reducers 객체 내부에 각 액션에 대한 리듀서 함수를 정의합니다. 이 함수들은 Immer를 사용하여 상태를 직접 수정할 수 있습니다.
- createSlice는 자동으로 액션 생성자 함수들을 생성하고, counterSlice.actions 객체를 통해 외부에서 액션을 접근할 수 있습니다.
3. 컴포넌트 작성
Redux Toolkit을 사용하면 useSelector와 useDispatch 훅을 사용하여 컴포넌트에서 Redux 스토어의 상태를 읽고 액션을 디스패치할 수 있습니다.
components/ButtonCounter.js
import React from 'react';
import { useSelector, useDispatch } from 'react-redux';
import { increment, decrement } from '../reducers/counterSlice';
const ButtonCounter = () => {
const count = useSelector(state => state.counter.count);
const dispatch = useDispatch();
return (
<div>
<p>Count: {count}</p>
<button onClick={() => dispatch(increment())}>좋아요</button>
<button onClick={() => dispatch(decrement())}>싫어요</button>
</div>
);
};
export default ButtonCounter;
- useSelector 훅을 사용하여 Redux 스토어의 상태에서 counter 슬라이스의 count 필드를 읽어옵니다.
- useDispatch 훅을 사용하여 dispatch 함수를 가져옵니다. 이 함수를 통해 액션을 디스패치할 수 있습니다.
- 버튼 클릭 시 dispatch(increment()) 또는 dispatch(decrement())를 호출하여 액션을 디스패치합니다.
4. 앱 루트 컴포넌트에서 Redux 스토어 적용하기
애플리케이션의 루트 컴포넌트에서 Redux 스토어를 Provider 컴포넌트를 사용하여 적용합니다.
App.js
import React from 'react';
import { Provider } from 'react-redux';
import store from './store/store';
import ButtonCounter from './components/ButtonCounter';
const App = () => {
return (
<Provider store={store}>
<div className="App">
<ButtonCounter />
</div>
</Provider>
);
};
export default App;
- Provider 컴포넌트를 사용하여 애플리케이션의 전체에서 Redux 스토어를 사용할 수 있도록 설정합니다.
- store prop에는 앞서 생성한 Redux 스토어를 전달합니다.
- ButtonCounter 컴포넌트는 Provider 컴포넌트 하위에 위치하므로 Redux 스토어의 상태와 액션을 사용할 수 있습니다.
굉장히 예제를 잘 알려줘서 마음에든다. 이해보다는 하나의 문법처럼 생각하는게 나을듯 싶음
용어 정리
- configureStore 함수는 Redux 스토어를 설정하는 역할을 합니다.
- createSlice 함수를 사용하여 Redux 슬라이스를 생성
- useSelector 훅을 사용하여 Redux 스토어의 상태(state) 값 추출 (스토어.슬라이스.값)
- useDispatch 훅을 사용하여 Redux의 dispatch 함수 사용 (정의해둔 메서드? 같은 기능)
- Provider 컴포넌트를 사용하여 애플리케이션의 전체에서 Redux 스토어를 사용할 수 있도록 설정
Redux 슬라이스(Slice)
createSlice 함수를 사용하여 생성된 하나의 객체로 상태(state)와 그 상태를 변경하는 리듀서(reducer)를 포함한다.
컴포넌트에서는
import { useSelector, useDispatch } from 'react-redux';
import { increment, decrement } from '../reducers/counterSlice';
useSelector 와 useDispatch 를 react-redux 에서 가져오고
만들어 둔 리듀서를 가져와서 사용하게 된다.
useState 에서는 setCount 와 같은 함수에 값을 직접 넣는 방식인데,
useDispatch 를 사용하면 사용자가 작성한 리듀서(reducer) 함수를 호출
스토어 생성시 미들웨어를 추가 할 수 있다. -> 추후 보충
Q. dispatch 는 슬라이스로 구분이 되지않는? 것 같은데, 리듀서 이름이 중복이 될수도 있지않나.?? -> 아니래
https://chatgpt.com/
https://medium.com/@heoh06/%EB%A6%AC%EC%95%A1%ED%8A%B8-redux%EC%99%80-%EC%82%AC%EC%9A%A9%EB%B2%95-731853fc3cd4