Redux 기초 정리

백은진·2020년 11월 10일
1

TIL (Today I Learned)

목록 보기
94/106
post-thumbnail

Redux란?

"Redux는 애플리케이션 상태를 관리하기 위한 자바스크립트 라이브러리로, 예측 가능한 상태 컨테이너를 통해 형상을 관리하는 기술을 갖고 있다.
사용자 인터페이스 구축을 위해 일반적으로 React, Angular와 같은 라이브러리와 함께 사용된다."

(출처: 위키피디아)

Redux가 제공하는 형상 관리 기술이 data가 더 효율적이고 전체적으로 이동될 수 있게 함으로서, Redux와 연관된 생태계가 활성화되고 있다.
특히, Redux를 React와 함께 사용하는 사람들이 많아지자 Redux에서 공식적으로 React-Redux라는 UI binding 라이브러리를 제공하기 시작했다.

Redux 이전

Redux를 알기 전, 나는 React에서 state, props를 이용하여 상태 관리를 했다.
간단한 웹을 구성할 때는 state, props를 이용하여 data를 주고 받는 방식이 전혀 문제가 없었지만, 웹이 복잡해지고 고도화되면서 아래와 같은 불편함이 발생하기 시작했다.

  • Depth가 깊거나, 서로 멀리 떨어져 있는 컴포넌트끼리 data를 주고 받는 것이 지나치게 복잡하고 비효율적임.
  • 프로젝트 전체에서 참조해야 하는 상태 값이 있을 경우, 이를 모든 컴포넌트에 연결하는 것이 번거롭고 비효율적임.

Software design pattern

MVC

Redux 이전에는 애플리케이션의 규모가 커짐으로 인해 양방향 데이터 흐름 패턴을 가지고 있던 Model-View-Controller(MVC) 방식에 불편함을 느끼게 되었다.


(출처: 강재영님의 블로그 )

Flux

방대한 사용자들이 방대한 데이터를 주고 받는 페이스북의 경우, MVC 패턴으로 데이터 흐름을 관리하며 크고 작은 버그를 마주했는데 이를 탈피하고자 Flux라는 새로운 아키텍처 패턴을 개발했다.

Flux는 단방향 데이터 흐름 패턴을 가지고 있다.

(출처: 강재영님의 블로그 )

위의 사진에서와 같이, View에서 Action(onClick event 등)이 일어나면 이를 Dispatcher(setState와 비슷한 hook)로 넘겨준 후,
Dispatcher를 통해 Store(상태 값을 가지고 있는 곳)를 업데이트 해주고, Store의 업데이트에 따라 View를 업데이트해준다.

모든 View는 Store를 바라보고 있기 때문에 정보가 단방향으로 전달된다.

MVC and Flux in Redux

React에서는 state값이 변경되었을 때와 props 값이 변경되었을 때, 렌더가 일어난다.

기존에 MVC 방식으로 컴포넌트를 직접적으로 연결했을 때는 몇 단계를 걸쳐 props를 전달하는 과정(props drilling)에서 필요하지 않은 component까지 렌더가 이루어지기 때문에 성능에 누수가 있다.

그러나 Flux 방식은 Store와 필요한 component를 바로 연결하여 단방향으로 데이터가 흐르게 하기 때문에 성능이 효율적이다.

Redux의 3원칙

1. 진실은 하나의 소스로부터

  • 데이터의 변경은 리듀서를 통해서 일어나며, 그 데이터는 스토어에 저장된다. 모든 변경 사항은 store에 기록되기 때문에, 데이터의 원천은 항상 store여야 한다.
  • 상황과 필요에 따라 스토어를 여러 개 만들 수도 있다. 그러나 스토어 수가 너무 많으면, MVC 패턴과 큰 차이가 있는지 생각해봐야 한다.

2. state는 읽기 전용이다.

  • 상태를 변화시키는 유일한 방법은 무슨 일이 벌어지는 지를 묘사하는 액션 객체를 전달하는 방법 뿐이다.
  • event가 직접 state의 데이터를 변경해서는 안 된다. event는 action을 reducer로 전달할 뿐이고, 데이터의 변경은 reducer만 할 수 있다.
  • 즉, reducer 이외의 공간에서 state는 읽기 모드인 것이다.

3. 변화는 순수 함수로 작성되어야 한다.

  • 액션에 의해 상태 트리가 어떻게 변화하는 지 지정하기 위해, 개발자는 순수 리듀서를 작성해야 한다.

    순수함수: 함수가 실행되는 곳이 어디고 언제든, 외부의 상태를 변경하지 않으면서 동일한 입력값에는 동일한 결과값을 반환해야 한다.

  • 순수함수의 특징으로 인해 순수 reducer는 2가지 특징을 더 갖는다.
    1. reducer는 반드시 이전의 state와 action을 입력값으로 받는다.
    2. reducer는 이전의 데이터를 변경시키지 않으면서, 결과값으로 새로운 데이터를 만들어 반환한다.

    (state, action) => nextState

  • 리듀서는 그저 이전 상태와 액션을 받아 새로운 다음 상태를 반환하는 순수 함수이다.
  • 다만, fetch 등이 비동기 로직이나 new Date(), Math.random() 처럼 값이 불확실한 값은 같은 input에 다른 return 값을 내기 때문에 store에 저장하면 안된다. 확실한 값만 store에 저장해야 하기 때문에 해당 경우에는 return 값을 먼저 받아서, 그 값 자체를 store에 저장하면 된다.

Flow on Page loading with Redux


(출처: 스택오버플로우 )

아직 Middleware에 대해서는 공부하지 않았기 때문에, 이를 제외하고 다룰 예정이다.

위의 Flux 부분에서 본 사진에서 Reducer 단계가 추가되었다.

Reducer

Reducerstoreaction 객체(action 함수를 통해 출력된 값)를 입력받아, 새로운 상태 값을 출력하는 순수 함수이다.

  1. action 객체는 반드시 type 속성 값을 가져야 한다. payload(이름은 상관 없다.) 객체는 있어도, 없어도 괜찮다.
  2. action 객체는 useDispatch 메소드(setState와 비슷한 기능)에 넣어 호출한다.
  3. useDispatch 메소드를 통해 전달된 action 객체reducer를 거쳐 store를 업데이트한다.

예를 들어 장바구니를 위한 reducer가 있다고 할 때 액션객체의 값에 따라 다른 로직을 처리해야하는 경우, 아래와 같이 함수를 작성할 수 있다.

// cartReducers.js
function cartReducer(state = INITIAL_STATE, action) {
  switch (action.type) {
    case "ADD_ITEM":
      return [...state, action.payload]; // 스토어의 이전 상태에 새로운 item을 추가
		case "DELETE_ITEM":
			return [...action.payload]
    default:
      return state; // 해당 사항 없으면 이전 상태를 그대로 리턴
  }
}

이 함수를 store에 저장할 때는 아래와 같이 combineReducers 메소드를 사용한다.

// store/reducer/index.js
import { combineReducers } from "redux";
import cartReducer from "./cartReducer";

export default combineReducers({ cartReducer });

useDispatch()

useDispatch는 store의 내장 함수로 액션 객체를 전달하는 함수다. 따라서 인자로 액션 객체가 들어가야 한다.

Store

스토어는 하나의 거대한 객체로, 모든 state 정보를 담고 있다.

createStore와 Provider 기능을 통해 아래와 같이 store 객체를 넘겨줄 수 있다.

import React from "react";
import ReactDOM from "react-dom";
import Routes from "./Routes";

import { Provider } from "react-redux";
import { createStore } from "redux";
import rootReducer from "./store/reducers";

const store = createStore(rootReducer);

ReactDOM.render(
  <Provider store={store}>
    <Routes />
  </Provider>,
  document.getElementById("root")
);

useSelector()

React의 각 컴포넌트에서는 useSelector 메소드를 위해 store로부터 원하는 값을 가져올 수 있다.

const items = useSelector((store) => store.cartReducer);

profile
💡 Software Engineer - F.E

0개의 댓글