CS 대비/FrontEnd Part

[React] 전역 상태 관리 - Redux

JellyApple 2023. 11. 3. 12:56

앞서 글에서 언급한대로 React에서는 상태(State)로 데이터를 관리하고 수정한다. 이렇게 상태를 다룰 때 가장 큰 문제점은 데이터가 많아질수록 관리해야 하는 상태가 늘어나고 컴포넌트끼리 props로 불필요한 상태 전달을 해줘야 하고 그로인해 상태 관리하기 힘들다는 점이다.  React를 사용할 때는 이러한 문제점을 극복하기 위해 다양한 라이브러리를 사용한다. 그 중 Redux와 Recoil을 살펴볼 예정이다.

 

1. Redux
Redux는 리액트에서만 지원하는 라이브러리는 아니지만 리액트에 특화된 전역 상태 관리 라이브러리이다. 

Flux 패턴 기반으로 단방향 데이터 흐름을 지향한다. state 들을 스토어(store)에 상태를 담고 이를 참조하게 하는 방식이다.

. 이 스토어에서 관리되는 상태 값은 일반적인 방법으로 꺼내오거나 변경하면 안된다. 내부 상태값의 안정성을 위해 정해진 방식으로만 가져오거나 변경할 수 있다.  Redux는 다음과 같은 순서로 데이터가 흐르게 한다.

Action → Dispatch → Reducer → Store 

0) 설치

npm install redux
npm install react-redux


1) Store

 리덕스에서 가장 중요한 객체이다. 이 안에 데이터를 모아둔다. redux 라이브러리에서 createStore 메소드를 사용해서 Store 객체를 생성해주고 react-redux 라이브러리에서 Provider 컴포넌트를 가져와서 우리가 스토어를 통해 상태를 관리해줄 컴포넌트를 감싸준다. 우린 Redux를 전역 상태 관리 라이브러리로 쓰고 싶기 때문에 App 컴포넌트를 감싸준다.

// index.js
import React from 'react'
import { Provider ] from 'react-redux' 
import App from './App'
import { createStore } from 'redux'
import { createRoot } from 'react-dom/client'

const rootElement = document.getElementById('root');
const root = createRoot(rootElement);

const store = createStore();

root.render(
  <Provider>
    <App store={store} />
  </Provider>
);

 

2) Reducer

상태를 변경시키는 함수이다. 상태를 변경시키는 일종의 결과물을 만든다. 외부 요인으로 인해 값이 바뀌면 안되기 때문에 순수 함수로 작성해야 한다. 첫 번째 매개변수로 현재 state 값을 받고 두 번째 매개변수로 Action이라는 객체를 받는다. 전달 받은 Action의 type에 따라 state 값을 변경 시킨다. 코드로는 다음과 같이 사용 가능하다. 

아직 작성하진 않았지만 action 객체의 type을 받아와서 이 type이 무엇인지에 따라 state를 바꿔준다. 이를 createStore에 전달해주면 상태를 변경 할 준비가 끝난 것이다. 

// reducer.js
const initialState = {
  text:""
}

export const reducer = (state=initialState , action = {} ) => {
 switch(action.type) {
    case: "CHANGE_TEXT"
      return {...state , {text: action.payload}}
    default: 
      return state;
}

 

// index.js
import React from 'react'
import { Provider ] from 'react-redux' 
import App from './App'
import { createStore } from 'redux'
import { createRoot } from 'react-dom/client'
// reducer import
import {reducer} from './reducer.js'
const rootElement = document.getElementById('root');
const root = createRoot(rootElement);

const store = createStore(reducer);

root.render(
  <Provider>
    <App store={store} />
  </Provider>
);


만약 여러 개의 Reducer가 있는 경우 Redux의 combineReducers 메서드를 사용해서 묶어 줄 수 있다.

// reducer가 여러 개 일 때
import { combineReducers } from 'redux';

const rootReducer = combineReducers({ reducer1 , reduecer2 })

3) Action
Action 은 사용자의 동작이라 할 수 있으며 Reducer 함수에 인자로 전달된다. Action 객체에는 두 가지가 들어가는데 무조건 들어가야 하는 type이라는 키와 그 외 데이터를 담을 payload로 구성된다. payload는 없는 경우도 있을 수 있다.

// action.js
export const action = {
   type : "NO_PAYLOAD"
 }
 
 export const action2 = (text) => ({
   type : "WITH_PAYLOAD"
   payload: text
 })


4) Dispatch
Reducer로 Action을 전달해주는 함수이다. Action 객체가 Dispatch의 전달인자가 된다. 

// 직접 객체를 작성하는 방식
dispatch({ type: 'NO_PAYLOAD'});
dispatch({ type: 'WITH_PAYLOAD'});

// 생성자를 만든 경우
dispatch( action() );
dispatch ( action2('hello') );

 

5) Redux Middleware 
미들웨어 : 동작이 일어나게 하는 트리거이다. Action 이 발생하면 Dispatch를 통해 Store에게 상태를 전달하는데 이 액션을 스토어로 전달하기 전에 다양한 작업을 하고 싶을 때 사용한다. 예를 들면 로깅 , 액션 취소 , 비동기 등을 하고 싶을 때 말이다. 

대표적으로 몇 가지가 있다
- redux-loagger : 액션 정보를 콘솔에 출력해주는 미들웨어. 이전 정보를 알 수 있다.
- redux-thunk : 비동기 작업을 처리할 때 가장 많이 사용하는 미들웨어다. 객체가 아닌 함수 혀앹의 액션을 디스패치 해준다. 

- redux-saga : 비동기 작업 관련 미들웨어 라이브러리다. 특정 액션이 디스패치 될 때 정해진 로직에 따라 다른 액션을 디스패치 시키는 규칙을 작성할 수 있다. 

// 미들웨어 기본 템플릿
const middleware = store => next => action => {
	// 처리 로직
};

// 일반 함수 형식
const middleware = function middleware(store) {
	return function(next) {
		return function(action) {
			// 처리 로직
		}
	}
}

 

(참고 링크 : https://velog.io/@gyumin_2/React.js-redux-middleware%EB%A6%AC%EB%8D%95%EC%8A%A4-%EB%AF%B8%EB%93%A4%EC%9B%A8%EC%96%B4-redux-thunk-redux-saga )