[리액트] Redux와 사용법

HE OH
11 min readMay 19, 2023

--

React 상태관리 라이브러리 Redux의 설명과 사용법

❓Redux란 무엇인가요?

Redux(리덕스)란 JavaScript(자바스트립트) 상태관리 라이브러리이다.

Redux(리덕스)의 본질은 Node.js 모듈이다.

💡Redux를 사용하는 이유

👉 먼저 상태란?

  • React에서 State는 component 안에서 관리되는 것이다.
  • 자식 컴포넌트들 간의 다이렉트 데이터 전달은 불가능 하다.
  • 자식 컴포넌트들 간의 데이터를 주고 받을 때는 상태를 관리하는 부모 컴포넌트를 통해서 주고 받는다.
  • 그런데 자식이 많아진다면 상태 관리가 매우 복잡해진다.
  • 상태를 관리하는 상위 컴포넌트에서 계속 내려 받아야한다. => Props drilling 이슈

💡리덕스를 쓰면, 상태 관리를 컴포넌트 바깥에서 한다!

리덕스를 사용하면 상태값을, 컴포넌트에 종속시키지 않고, 상태 관리를 컴포넌트의 바깥에서 관리 할 수 있게 된다.

1. 전역 상태 저장소 제공

2. Props drilling 이슈 해결

🔖Redux 흐름

🧺 Store (스토어)

Store(스토어)는 상태가 관리되는 오직 하나의 공간이다.

  • 컴포넌트와는 별개로 스토어라는 공간이 있어서 그 스토어 안에 앱에서 필요한 상태를 담는다.
  • 컴포넌트에서 상태 정보가 필요할 때 스토어에 접근한다.

📃 Action (액션)

  • Action(액션)은 앱에서 스토어에 운반할 데이터를 말한다. (주문서)
  • Action(액션)은 자바스크립트 객체 형식으로 되어있다.

🎉 Reducer (리듀서)

  • Action(액션)을 Store(스토어)에 바로 전달하는 것이 아니다.
  • Action(액션)을 Reducer(리듀서)에 전달해야한다.
  • Reducer(리듀서)가 주문을 보고 Store(스토어)의 상태를 업데이트하는 것이다.
  • Action(액션)을 Reducer(리듀서)에 전달하기 위해서는 dispatch() 메소드를 사용해야한다.

Action(액션) 객체dispatch() 메소드에 전달된다.

dispatch(액션)를 통해 Reducer를 호출한다.

Reducer는 새로운 Store 를 생성한다.

예를 들어서 B 에서 일어나는 변화가 G 에 반영된다고 가정을 해보자.

1.스토어 설정

https://velopert.com/3528

리덕스를 프로젝트에 적용하게 되면 이렇게 스토어가 생긴다. 스토어 안에는 프로젝트의 상태에 관한 데이터들이 담겨있다.

2. 컴포넌트의 스토어 구독

https://velopert.com/3528

G 컴포넌트는 스토어에 구독을 한다. 구독을 하는 과정에서, 특정 함수가 스토어한테 전달이 된다. 그리고 나중에 스토어의 상태값에 변동이 생긴다면 전달 받았던 함수를 호출해준다.

3. 스토어에 상태 변경하라고 알려주기

이제 B 컴포넌트에서 어떤 이벤트가 생겨서, 상태를 변화 할 일이 생긴다. 이 때 dispatch 라는 함수를 통하여 액션을 스토어한테 던져준다. 액션은 상태에 변화를 일으킬 때 참조 할 수 있는 객체다. 액션 객체는 필수적으로 type 라는 값을 가지고 있어야 한다.

4. 리듀서를 통하여 상태를 변화시키기

https://velopert.com/3528

액션 객체를 받으면 전달받은 액션의 타입에 따라 어떻게 상태를 업데이트 해야 할지 정의를 해줘야 한다. 이러한 업데이트 로직을 정의하는 함수를 리듀서라고 부른다.

리듀서 함수는 두가지의 파라미터를 받습니다.

  1. state: 현재 상태
  2. action: 액션 객체

그리고, 이 두가지 파라미터를 참조하여, 새로운 상태 객체를 만들어서 이를 반환합니다.

5. 상태가 변화가 생기면, 구독하고 있던 컴포넌트에게 알림

https://velopert.com/3528

상태에 변화가 생기면, 이전에 컴포넌트가 스토어한테 구독 할 때 전달해줬었던 함수 listener 가 호출된다. 이를 통하여 컴포넌트는 새로운 상태를 받게되고, 이에 따라 컴포넌트는 리렌더링을 한다.

🔥Redux 간단하게 시작해보기

# NPM
npm install redux
npm install react-redux

# Yarn
yarn add redux react-redux

먼저 리덕스를 설치한다.

1. RootReducer 정의

  • 여러 reducer을 사용하는 경우 reducer을 하나로 묶어주는 메소드
// reducers/index.js

import { combineReducers } from "redux";
import counter from "./counter";

const rootReducer = combineReducers({
counter
});

export default rootReducer;

2. 세부 reducer 정의

// reducers/counter.js

export const INCRESE = "COUNT/INCRESE";

export const increseCount = count => ({ type: INCRESE, count });

const initalState = {
count: 0
};

const counter = (state = initalState, action) => {
switch (action.type) {
case INCRESE:
return {
...state,
count: action.count
};

default:
return state;
}
};

3. store 만들기

import { compose, createStore, applyMiddleware } from "redux";
import rootReducer from '../reducers/index';
import thunk from "redux-thunk";

const composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ ?
window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__({})
: compose;
const store = createStore(rootReducer, composeEnhancers(applyMiddleware(thunk)));

export default store;

4. app에 store 넣고, 만든 reducer 반영

<Provider> 안써서 엄청나게 눈물을 흘리면서 왜 안될까 찾았던…

// index.js
import React from "react";
import ReactDOM from "react-dom";
import { createStore, applyMiddleware, compose } from "redux";
import { Provider } from "react-redux";
import logger from "redux-logger";
import { composeWithDevTools } from "redux-devtools-extension";

import App from "./App";
import rootReducer from "./reducers";

// 배포 레벨에서는 리덕스 발동시 찍히는 logger를 사용하지 않습니다.
const enhancer =
process.env.NODE_ENV === "production"
? compose(applyMiddleware())
: composeWithDevTools(applyMiddleware(logger));

// 위에서 만든 reducer를 스토어 만들때 넣어줍니다
const store = createStore(rootReducer, enhancer);

ReactDOM.render(
// 만든 store를 앱 상위에 넣어줍니다.
<Provider store={store}>
<App />
</Provider>
document.getElementById('root'),
);

5. 컴포넌트에서 redux 사용하기

import { useSelector, useDispatch } from "react-redux";
import { increseCount } from "reducers/count";

// dispatch를 사용하기 위한 준비
const dispatch = useDispatch();

// store에 접근하여 state 가져오기
const { count } = useSelector(state => state.counter);

const increse = () => {
// store에 있는 state 바꾸는 함수 실행
dispatch(increseCount());
};

const Counter = () => {
return (
<div>
{count}
<button onClick={increse}>증가</button>
</div>
);
};

export default Counter;

➕추가 ) Redux-thunk?

요약 :

redux-thunk는 리덕스에서 비동기 작업을 처리 할 때 가장 많이 사용하는 미들웨어입니다.

이 미들웨어를 사용하면 액션 객체가 아닌 함수를 디스패치 할 수 있습니다.

함수를 디스패치 할 때에는, 해당 함수에서 dispatchgetState 를 파라미터로 받아와주어야 합니다. 이 함수를 만들어주는 함수를 우리는 thunk 라고 부릅니다.

➕추가 ) Redux-saga?

요약 ) redux-saga는 redux-thunk 다음으로 가장 많이 사용되는 라이브러리입니다.

  1. 비동기 작업을 할 때 기존 요청을 취소 처리 할 수 있습니다
  2. 특정 액션이 발생했을 때 이에 따라 다른 액션이 디스패치되게끔 하거나, 자바스크립트 코드를 실행 할 수 있습니다.
  3. 웹소켓을 사용하는 경우 Channel 이라는 기능을 사용하여 더욱 효율적으로 코드를 관리 할 수 있습니다
  4. API 요청이 실패했을 때 재요청하는 작업을 할 수 있습니다.

이 외에도 다양한 까다로운 비동기 작업들을 redux-saga를 사용하여 처리 할 수 있습니다.

❗️결론 : Redux의 장점

  • 상태를 예측 가능하게 만든다. (순수함수를 사용하기 때문)
  • 유지보수 (복잡한 상태 관리와 비교)
  • 디버깅에 유리 (action과 state log 기록 시) → redux dev tool (크롬 확장)
  • 테스트를 붙이기 용의 (순수함수를 사용하기 때문)

Reference

--

--