redux 기본 개념과 사용 예시(counter 만들기)

soo's·2023년 4월 17일
0

TIL

목록 보기
40/53
post-thumbnail

0.redux란?

Redux는 React를 위한 상태 관리 라이브러리.
React에서는 컴포넌트 간에 데이터를 전달하기 위해 Props와 State를 사용한다. 컴포넌트 계층 구조가 깊어지고, 컴포넌트 간의 데이터 전달이 복잡해지면 props drilling이 일어나게 된다. 이런 상황에서 Redux는 전역 상태 관리를 가능하게 해주며, 컴포넌트 간의 데이터 전달을 간단하게 해준다.

Redux는 Flux 패턴의 구현체 중 하나.
Flux 패턴은 데이터 흐름을 단방향으로 유지하는 아키텍처 패턴으로, 데이터의 흐름이 일관성을 유지하면서 어플리케이션의 복잡성을 줄이는 데에 효과적임

📌 Redux에서는 애플리케이션의 상태(State)를 하나의 Store에 저장하고, 컴포넌트는 이 Store에서 상태를 읽어오거나, 상태를 변경하는 액션(Action)을 Dispatch하는 방식으로 상태를 관리한다.

이렇게 하면, 상태 변화에 대한 일관성을 유지하면서도 컴포넌트 간의 데이터 전달을 간편하게 처리할 수 있습니다.

1. redux 사용 이점

전역 상태 관리: Redux를 사용하면, 전역 상태를 하나의 Store에 저장하기 때문에, 애플리케이션의 모든 컴포넌트에서 동일한 상태를 공유할 수 있습니다.

불변성 유지: Redux에서는 상태를 변경할 때, 기존 상태를 직접 수정하지 않고, 새로운 상태 객체를 생성하여 변경합니다. 이를 통해, 상태의 불변성을 유지하면서, 예측 가능한 상태 변화를 구현할 수 있습니다.

디버깅 용이성: Redux는 상태 변화를 모두 기록하고, 디버깅 도구와 함께 사용할 수 있어서, 애플리케이션의 문제를 빠르게 해결할 수 있습니다.

확장성: Redux는 라이브러리 자체가 매우 작고, 상태 관리에 대한 명확한 아키텍처를 제공하기 때문에, 애플리케이션의 규모

2. redux를 사용해보자

2-1. redux 설치

프로젝트 폴더에서 터미널을 열고

yarn add redux react-redux

이렇게 두 가지 패키지를 띄어쓰기를 통해 설치해주자.

2-2. 프로젝트 구조 만들기

redux라는 폴더를 src안에 생성해서 각각 config 폴더와 modules 폴더를 만든다. config안에 configStore.js라는 파일을 생성한다. src의 트리는 아래와 같다.

📦src
┣ 📂redux
┃ ┣ 📂config
┃ ┃ ┗ 📜configStore.js
┃ ┗ 📂modules
┣ 📜App.css
┣ 📜App.js
┣ 📜index.css
┗ 📜index.js

폴더 설명을 하자면
redux : 리덕스 관련한 가장 최상위 폴더
config : 리덕스 설정 관련 파일을 모두 담은 폴더
modules : state들의 그룹을 담은 폴더 (예를 들어 투두리스트를 만든다고 한다면, 투두리스트에 필요한 state들이 모두 모여있을 todos.js를 생성하게 되는데, 이 todos.js 파일이 곧 하나의 모듈)

파일 설명을 하자면
configStore.js : “중앙 state 관리소" 인 Store를 만드는 설정 코드들이 있는 파일

2-3. configStore.js 설정


import { createStore } from "redux";
import { combineReducers } from "redux";

/*
1. createStore()
리덕스의 가장 핵심이 되는 스토어를 만드는 메소드(함수) 입니다. 
리덕스는 단일 스토어로 모든 상태 트리를 관리한다고 설명해 드렸죠? 
리덕스를 사용할 시 creatorStore를 호출할 일은 한 번밖에 없을 거예요.
*/

/*
2. combineReducers()
리덕스는 action —> dispatch —> reducer 순으로 동작한다고 말씀드렸죠? 
이때 애플리케이션이 복잡해지게 되면 reducer 부분을 여러 개로 나눠야 하는 경우가 발생합니다. 
combineReducers은 여러 개의 독립적인 reducer의 반환 값을 하나의 상태 객체로 만들어줍니다.
*/

const rootReducer = combineReducers({});
const store = createStore(rootReducer);

export default store;

2-4. index.js 설정

// 원래부터 있던 코드
import React from "react";
import ReactDOM from "react-dom/client";
import App from "./App";

// 우리가 추가할 코드
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>
);

이 상태에서 counter를 진행하는 함수를 redux의 global state로 관리를 해준다면 코드는 아래와 같다.

// counter.js
// 초기 상태값
const initialState = {
  number: 0,
};

// reducer 함수 - anction 객체를 받아서 그 type에 따라 state 변경시키는 함수
const counter = (state = initialState, action) => {
  switch (action.type) {
    case "plus":
      return { ...state, number: state.number + 1 };
    case "minus":
      return { ...state, number: state.number - 1 };
    default:
      return state;
  }
};
export default counter;

counter.js를 생성해서 다음과 같이 reducer 함수를 만들어준다. 이 counter라는 reducer 함수는 action.type에 따라 다른 state를 반환하는데, state 값에 state+1 이런식으로 적는게 아니라 { ...state, number: state.number + 1 }; 이런식으로 작성한 이유는 지금 state가 객체라서 그렇다.

이렇게 작성한 counter라는 모듈을 (모듈이기 때문에 modules 폴더에 있음) configStore 그러니까 중앙저장소에 연결을 해줘야한다.

// configStore.js
import { createStore } from "redux";
import { combineReducers } from "redux";
import counter from "../modules/counter";

const rootReducer = combineReducers({
  counter, // <-- 새롭게 추가한 부분
});
const store = createStore(rootReducer);

export default store;

이렇게 CombineReducers api안에 객체형태로 작성해주면 된다. key value가 같으므로 그냥 counter 하나만 작성한 형태임
이렇게 만든 store를 내 App.js에 사용해보자

// App.js
import React from "react";
import { useDispatch, useSelector } from "react-redux";

const App = () => {
  const counterStore = useSelector((state) => state.counter);
  const dispatch = useDispatch();
  const handleClickPlus = () => {
    dispatch({ type: "plus" });
  };
  const handleClickMinus = () => {
    dispatch({ type: "minus" });
  };
  return (
    <>
      <div>현재 넘버 : {counterStore.number}</div>
      <button onClick={handleClickPlus}>+</button>
      <button onClick={handleClickMinus}>-</button>
    </>
  );
};

export default App;

useDispatch hook을 사용해서 dispatch 기능을 사용할 수 있다.
dispatch는 action 객체를 reducer 함수에 전달하는 역할을 한다. reducer는 이 action 객체를 받아서 action.type에 따라 state를 변화시킨다.

0개의 댓글