Redux란?
등장 배경
리덕스가 무엇인지 알아보기 전에 리덕스가 왜 필요할까? 고민해볼필요가 있습니다.
리액트에선 일반적으로 State를 관리 할 때는 props를 통해 부모에서 자식 컴포넌트의 방향(단방향 데이터)으로 데이터 흐름이 일어난다.
💡 State
- 상태란 의미는 단순히 정상, 비정상인 상태를 뜻하기 보다는 데이터 의미에 가깝다.
즉, 리액트 컴포넌트의 변경 가능한 데이터(DATA)를 뜻한다.
1. 양방향 흐름 지양
부모와 자식 컴포넌트 모두가 데이터를 직접 변경이 가능하다
간단한 화면에선 단순하게 부모와 자식의 데이터간에 데이터를 직접 호출해서 동기화함으로 좋아 보일수 있다.
하지만 복잡한 프로젝트에 컴포넌트들의 잦은 변경이 존재한다면 데이터 흐름을 예상하기 어렵다는 큰 단점이 존재한다.
2. 자식을 향한 불필요한 props 전달 탈출
원하는 자식 컴포넌트에서 원하는 State를 변경하기 위해 props를 해당 자식 컴포넌트까지 쭉~ 내리게 됩니다.
사용하지 않는 자식컴포넌트들은 자손을 위해 대물림(금 or 흙)?을 받게 되는거죠
예를들어 부모와 자식 사이에 몇 백개 컴포넌트가 존재한다고 가정한다면 해당 자식 컴포넌트까지 state를 전달해야하는 불상사가 생깁니다.
이러한 단점의 해결책인 리덕스(Redux)에 관해 알아 보도록 하겠습니다.
Redux 정의
리덕스는 중앙 데이터 저장소를 통해 전역으로 상태(State)를 쉽게 관리할 수 있게 해주는 라이브러리입니다.
리덕스를 사용하게 되면 하나의 중앙 데이터 저장소에서 State를 관리하기 때문에, State를 공유하고자 할때 부모-자식 관계로 데이터를 전달하지 않아도 되며, 중간에 의미 없어 데이터를 거치지 않아도 됩니다.
Redux 개념
데이터흐름은 절대 반대방향으로 흐르지 않는 단방향 데이터 흐름을 적용한다.
Component(Trigger) ⇒ Action ⇒ Reducer ⇒ Store ⇒ Component(UI 업데이트)
- 사용자가 Component의 어떤 Trigger를 통해 Action을 발생시키면
- Component안에 있는 Dispatcher가 Reducer로 객체 형태의 데이터를 전달하고
- Reducer함수는 Dispatch로 부터 들어온 데이터를 action type에 맞춰 Store에 있는 state를 업데이트 한다.
- 업데이트가 필요한 Components는 useSelector를 통해서 변경된 Store의 State를 가져올 수 있고, 이를 통해 UI를 업데이트할 수 있다.
Redux 적용
먼저 리덕스를 사용하기 위해선 구조 셋팅이 필요합니다.
- redux : 리덕스와 관련된 코드를 모두 모아 놓을 폴더
- config : 리덕스 설정과 관련된 파일들을 놓을 폴더 입니다.
- configStore : 중앙 데이터 저장소인 Store를 만드는 설정 코드들이 있는 파일 입니다.
- modules : 우리가 만들 State들의 그룹. 예를 들어 Counter를 만든다고 한다면, Counter에 필요한 state들이 모두 모여있을 counter.js를 생성하게 되텐데요, 이 counter.js 파일이 곧 하나의 모듈이 됩니다.
1. Store 생성
src/configStore.js
import { createStore } from "redux";
import { combineReducers } from "redux";
/*
1. createStore()
- 스토어를 만드는 메소드(함수)
2. combineReducers()
- 리덕스는 action —> dispatch —> reducer 순으로 동작
- 이때 애플리케이션이 복잡해지게 되면 reducer 부분을 여러 개로 나눠야 하는 경우가 발생
- combineReducers은 여러 개의 독립적인 reducer의 반환 값을 하나의 상태 객체로 만들어줍니다.
*/
const rootReducer = combineReducers({});
const store = createStore(rootReducer);
export default store;
2. Provider를 통해 store 주입
index.js
import React from "react";
import ReactDOM from "react-dom/client";
import App from "./App";
import reportWebVitals from "./reportWebVitals";
// 추가할 import
import store from "./redux/config/configStore";
import { Provider } from "react-redux";
const root = ReactDOM.createRoot(document.getElementById("root"));
root.render(
//App을 Provider로 감싸주고, configStore에서 export default 한 store를 주입.
<Provider store={store}>
<App />
</Provider>
);
reportWebVitals();
자 이제 큰 골자는 완성이 되었으니 Counter 예제를 통해서 데이터 흐름에 대해 자세히 확인해보겠습니다.
Redux 예제
앞전에 Redux의 흐름은 아래와 같다고 했습니다.
Component(Trigger) ⇒ Action ⇒ Reducer ⇒ Store ⇒ Component(UI 업데이트)
1. Components(Trigger) ⇒ Action
- Action
- 데이터 전달형식으로 자바스크립트의 객체로 표현한다.
- useDispatch를 통해서 Reducer에 데이터를 전달한다.
- type필드는 필수적이며, 그 외의 값들은 원하는데로 수정 가능하다.
import React from "react";
/* Dispatch */
import { useDispatch } from "react-redux";
const App = () => {
/* Dispatch */
const dispatch = useDispatch();
return (
<div>
<button
onClick={() => {
dispatch(
{
type: "INCREMENT_COUNT",
payload: { incrementValue: 1 },
}
);
}}
>
PLUS
</button>
</div>
);
};
export default App;
2. Action ⇒ Reducer
- Reducer
- 첫번째 인자(state) 초기값을 받으며, 두번째 인자(action)는 dispatch 넘어온 type들의 데이터.
- 데이터를 조작하기 위해서는 리듀서(Reducer) 함수를 이용한다. 리듀서는 넘어온 데이터 조작( state를 변화)을 위해 사용되는 함수
- 아래 코드는 "INCREMENT_COUNT" 타입으로 들어왔고, payload로 증가값(incrementValue)을 현재 state에 더해줬다.
/* 초기 상태값 */
const initialState = { number: 0 };
/* Reducer */
const counter = (state = initialState, action) => {
switch (action.type) {
case "INCREMENT_COUNT":
return { ...state, number: state.number + action.payload.incrementValue };
default:
return state;
}
};
3. Reducer ⇒ Store
- Store
- Reducer에 의해 변경된 State가 저장되어 있는 단 하나의 전역 공간. (절대 2개 이상 갖지 않는다.)
- 이 하나의 Store에 전체 App의 모든 상태를 저장한다. 예) 인증상태, 입력 상태, 테마 등
- 앞전에 src/configStore.js에 Counter Reducer를 연결시켜준다.
import { createStore } from "redux";
import { combineReducers } from "redux";
import counter from "../modules/counter";
/* Counter Reducer 연결 */
const rootReducer = combineReducers({
counter: counter,
});
const store = createStore(rootReducer);
export default store;
4. Store ⇒ Component(UI 업데이트)
- 업데이트가 필요한 Components는 useSelector를 통해서 변경된 Store의 State를 가져올 수 있고, 이를 통해 UI를 업데이트를 할 수 있다.
import React from "react";
/* useSelector 추가 */
import { useDispatch, useSelector } from "react-redux";
const App = () => {
const dispatch = useDispatch();
/* useSelector를 이용해 store 접근 */
const counterStore = useSelector((state) => state.counter);
return (
<div>
/* UI 업데이트 */
<div>{counterStore.number}</div>
<button
onClick={() => {
dispatch(
{
type: "INCREMENT_COUNT",
payload: { incrementValue: 1 },
}
);
}}
>
PLUS
</button>
</div>
);
};
export default App;
'Web' 카테고리의 다른 글
반응형 디자인을 위한 개발자 TIP (0) | 2024.08.16 |
---|---|
SOP, CORS 개념 (0) | 2023.03.02 |
[CSS] Selector 정리 (0) | 2022.12.18 |
[CSS] 벤더 프리픽스(Vendor Prefix)란? (0) | 2022.12.09 |
[CSS] Reset CSS - 브라우저에 따라 다르게 보이는 차이 (0) | 2022.12.09 |