[(React)상태관리] Redux

도현수·2022년 9월 1일
0

기존 React 데이터 흐름을 따랐을 경우의 문제점

기존 React의 데이터 흐름을 따르자면, 하위 컴포넌트들에서 사용되는 상태는 상위 컴포넌트에 위치시키는 것이 적절하다. 그러나 이 방법은 몇가지 문제가 있다.

  • 중간에 있는 컴포넌트들은 단순히 상태를 전달하기 위해 상태를 가져야 한다는 점
  • 어플리케이션의 복잡도와 비례해 데이터 흐름도 복잡해지는 점
  • 컴포넌트 구조를 바꿀 경우, 현재의 데이터 흐름을 싹 다 고쳐야 한다는 점

store를 제공하는 Redux

Redux는 위와 같은 문제를 전역 상태를 저장하는 store를 제공해서 해결한다. 상태가 store를 거쳐 전달되므로, 다른 컴포넌트들에게 불필요하게 전달되거나 데이터 흐름을 싹 고쳐야 할 일이 발생하지 않는다.

Redux의 구조

React가 상태를 관리하는 순서는 다음과 같다.

  1. 상태가 변경되어야 하는 이벤트가 발생하면, 변경될 상태에 대한 정보가 담긴 Action 객체가 생성
  2. Action 객체는 Dispatch 함수의 인자로 전달됨
  3. Dispatch 함수는 Action 객체를 Reducer 함수로 전달해줌
  4. Reducer 함수는 Action 객체의 값을 확인하고, 그 값에 따라 전역 상태 저장소 Store의 상태를 변경해줌
  5. 상태가 변경되면, React는 화면을 다시 렌더링해줌

즉, Redux에서는

Action → Dispatch → Reducer → Store
순서로 데이터가 단방향으로 흐른다.

store

store는 상태가 저장되는 단 하나뿐인 저장소. 즉, store에 상태(state)가 저장되어 있다.

  • Store를 생성하기 위해 createStore 메서드를 통해 Reducer를 연결한다.
import { createStore } from 'redux';

const store = createStore(rootReducer);
//store를 생성할 때 createStore에 인자로 Reducer 함수를 전달해준다.
  • react-redux에서 Provider를 불러온다. Provider는 store를 사용하기 쉽게 만들어주는 컴포넌트이며, store를 사용하고 싶은 컴포넌트를 Provider로 감싸고 store를 props로 전달한다.
import { Provider } from 'react-redux'; 

import { createStore } from 'redux';

const store = createStore(rootReducer);

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

Reducer

새로운 상태를 만들고, createStore로 전달되는 Reducer함수(위에서는 rootReducer)는 Dispatch에게서 전달받은 Action 객체의 type 값에 따라서 상태를 변경시키는 함수이다. 이 때 Reducer는 외부 요인으로 인해 기대값이 아니라 엉뚱한 값이 변화되지 않도록 순수함수여야 한다.

const count = {수량: 1};

const reducer = (state = count, action) => {
  // reducer의 첫번째 인자로 초기상태를 전달해줌
  // Action 객체의 type 값에 따라 분기하는 switch 조건문.
  switch (action.type) {

    //action === 'INCREASE'일 경우
    case 'INCREASE':
			return state[수량] + 1

    // action === 'DECREASE'일 경우
    case 'DECREASE':
			return state[수량] - 1

    // action === 'SET_NUMBER'일 경우
    case 'SET_NUMBER':
			return action.payload

    // 해당 되는 경우가 없을 땐 기존 상태를 그대로 리턴
    default:
      return state;
	}
}
  • 여러 개의 Reducer를 사용하는 경우, Redux의 combineReducers 메서드를 사용해서 하나의 Reducer로 합칠 수 있다.
import { combineReducers } from 'redux';

const rootReducer = combineReducers({
  counterReducer,
  anyReducer,
  ...
});

Action

Action은 말 그대로 어떤 액션을 취할 것인지 정의해 놓은 객체이다.

// payload가 필요 없는 경우
{ type: 'INCREASE' }

// payload가 필요한 경우
{ type: 'SET_NUMBER', payload: 5 }

type은 Action이 어떤 동작을 하는지 명시해주기 때문에 필수로 작성한다(대문자와 SNAKE CASE이용). 여기에 payload를 이용해 구체적인 값을 전달해준다.
보통은 Action을 직접 작성하는 것이 아니라, Action 객체를 생성하는 함수를 만들어 사용한다. 이러한 함수를 액션 생성자라고 한다.

// payload가 필요 없는 경우
const increase = () => {
  return {
    type: 'INCREASE'
  }
}

// payload가 필요한 경우
const setNumber = (num) => {
  return {
    type: 'SET_NUMBER',
    payload: num
  }
}

Dispatch

Dispatch는 Reducer로 Action을 전달해주는 함수이다. Dispatch의 전달인자로 Action 객체가 전달된다. 즉, Dispatch함수를 호출하면 Reducer에 Action이 전달되고, 그 Action.type에 따라 Reducer가 실행된다.

// Action 객체를 직접 작성하는 경우
dispatch( { type: 'INCREASE' } );
dispatch( { type: 'SET_NUMBER', payload: 5 } );

// 액션 생성자(Action Creator)를 사용하는 경우
dispatch( increase() );
dispatch( setNumber(5) );

Action 객체를 전달받은 Dispatch 함수는 Reducer를 호출한다.

Redux Hooks

Store, Reducer, Action, Dispatch들을 Redux Hooks를 이용해서 연결해줘야 한다.
Redux Hooks는 React-Redux에서 Redux를 사용할 때 활용할 수 있는 Hooks 메서드를 제공한다.
그 중에서 크게 useSelector(), useDispatch() 가 특히 중요하다.

useDispatch()

useDispatch() 는 Action 객체를 Reducer로 전달해 주는 Dispatch 함수를 반환하는 메서드이다. 위에서 Dispatch를 설명할 때 사용한 dispatch 함수도 useDispatch()를 사용한 것이다.

import { useDispatch } from 'react-redux'

const dispatch = useDispatch();

dispatch( increase() ); // Reducer 함수에 Action객체가 전달됨.
dispatch( setNumber(5) ); //Reducer 함수에 Action객체가 전달됨.

useSelector()

useSelector()는 컴포넌트와 state를 연결하여 Redux의 state에 접근할 수 있게 해주는 메서드이다.

import { useSelector } from 'react-redux'
const counter = useSelector(state => state)
console.log(counter) // 1

useSelector의 콜백 함수의 인자(위에선 state)에는 저장했던 모든 state가 담기게 된다. 이를 그대로 리턴하면 저장된 모든 state를 사용할 수 있다.

Redux의 3가지 원칙

  1. Single source of truth
    동일한 데이터는 항상 같은 곳에서 가져와야 함. 이는 상태가 저장되는 단 하나의 공간인 Store와 연결되는 원칙임

  2. State is read-only
    상태는 읽기 전용이므로 직접적으로 변경할 수 없고, Action객체를 통해서만 전달할 수 있음을 의미하는 원칙이다.

  3. Changes are made with pure functions
    상태의 변화는 순수함수로만 만들어진다는 원칙. 상태가 엉뚱한 값으로 변경되는 일이 없도록 순수함수로 작성되어야하는 Reducer와 연결되는 원칙이다.

0개의 댓글