상세 컨텐츠

본문 제목

[React] React Redux

Coding/React

by hwlink 2021. 12. 23. 02:03

본문

리덕스란 무엇인가

기초배경

:: MVC

기존에는 1979년에 고안된 MVC 디자인 패턴을 사용하였다. 관심사의 분리에 따라 분업화하는 디자인 패턴이다.

  • Model: 정보, 데이터 리액트(State)
  • Controller: 특정 동작에 따라 Model, View 조작, 변경을 위해 Model, View 모니터링, 이벤트감지 및 처리
  • View: 브라우저 화면, 유저 인터페이스, Controller에 변경 통지

https://developer.mozilla.org/ko/docs/Glossary/MVC

하지만 단점도 존재하는데 아래 그림처럼 하나의 컨트롤러가 여러 모델에 걸쳐 데이터를 조작하고 있는 상황. 모델은 여러 뷰를 업데이트 하고, 뷰 또한 컨트롤러를 통해 모델을 업데이트 합니다(양방향 데이터 바인딩) 바인딩: 뷰와 데이터를 연결하는 것
컨트롤러가 일으킨 조작이 정확히 어떤 요소들에 대한 업데이트를 일으켰는지 파악하기 어렵다.

http://facebook.github.io/flux/docs/in-depth-overview

:: Flux

매우 효율적인 디자인 패턴이지만 점점 고도화 되는 웹어플리케이션에는 부적합하여 안정적인 상태관리가 필요해짐.

Redux

http://facebook.github.io/flux/docs/in-depth-overview

리덕스 사용 원칙

  1. 전체의 상태값은 1개의 JavaScript 객체로 표현돼야 한다.
  2. 상탯값은 읽기 전용의 불변 객체로 관리한다. 리덕스의 상탯값 수정은 액션 객체와 함께 dispatch, 업데이트 방식과 시점 제한함으로써 변화에 대한 에측 가능성 얻을 수 있다.
  3. 오직 순수 함수에 의해서만 상탯값을 변경해야 한다. 순수함수: 외부개입이 없이 정해진 값만 반환하는 함수. 비동기 로직 fetch, new Date(), Math.random 등은 안된다.

리덕스 개념

  1. View
  2. Action/Action Creator
  • type이라는 속성값을 가진 자바스크립트 객체. dispatch method에 넣어서 호출하여 사용한다.
    액션 생성함수가 생성한 객체는 reducer를 거쳐 store를 업데이트한다. Action을 reducer로 보내는 역할을 하는 건 아님.
export const addCart = (item) => { // 액션 "생성 함수"
  return {
    type: "ADD_ITEM", // 액션 "객체"
    payload: item,
  };
};
  1. Dispatcher
  • Action 객체를 Reducer에 보내는 역할을 하는 함수 store.dispatch() 형태로 제공된다.
  • 기본 dispatch 함수는 반드시 동기적으로 처리되어야한다.
  • 만약 비동기 Action이 필요하다면 비동기 처리가 완료된 이후에 Action을 Dispatch 하거나, 미들웨어(Action이 Reducer로 들어가기 전에 거쳐가는 곳)를 활용해 비동기 처리를 해줘야 함.
  1. Reducer
  • (prev, action) => newState형태, 순수함수이며 예측가능
  • Reducer는 업데이트 되기 이전의 Store를 기반으로 새로 받은 Action이 발생했을 때 새로운 상탯값을 만드는 함수이다.
  • 불변성을 지켜 업데이트 하여야한다. setState
    Reducer 내부에서 비동기, Side Effect는 처리하면 안된다.
const INITIAL_STATE = [];
// redux/cart.js  
export default function cartReducer(state = INITIAL\_STATE, action) {  
switch (action.type) {  
case "ADD\_ITEM":  
return \[...state, action.payload\]; // 이전 상태에 새로운 item을 추가  
case "DELETE\_ITEM":  
return state.filter((product) => product.id !== payload.id);  
default:  
return state; // 해당 사항 없으면 이전 상태를 그대로 리턴  
}  
}

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

export default combineReducers({ cartReducer });
  1. Store
  • Redux 앱 전체의 상태로 보통 깊게 중첩되어 있는 객체입니다.
  • store에서 관리되고, store.getState()로 읽어올 수 있습니다.
  • 항상 직렬화(Serialization) 가능해야 하기 때문에 JSON으로 쉽게 변환할 수 없는 것들은 제외하는게 좋습니다(ex. 함수, Promise 등)
  • useSelector를 통해 Store 내용 가져올 수 있다.
  • import { Provider } from "react-redux"; import { createStore } from "redux"; import rootReducer from "./store/reducers"; const store = createStore(rootReducer); ReactDOM.render(
,
document.getElementById("root")
);
```

6. Middleware

  • 비동기 API, Side Effect를 처리하거나 Reducer가 액션을 처리하기 전에 실행되는 함수
  • 미들웨어를 설정하지 않으면 dispatch한 액션은 바로 리듀서로 간다.

정리하자면 Reducer란.

  1. View에서 유저가 일으키는 행동에 맞게 Action 객체가 생성되고,
  2. Action은 Dispatcher를 통해 Reducer로 전달되고,
  3. Action의 type에 따라 Reducer 내에 미리 정해져 있던 로직이 Store를 변경하고,
  4. 변경된 Store의 내용이 View로 반영된다.

리덕스를 사용하는 이유

리덕스를 사용하는 핵심 이유는 기존 props drilling이 아닌 외부에 어느 위치에서든 다이렉트 접근 가능한 store라는 저장소를 설정해서 훨씬 간결해지고,
또한 성능에도 영향을 끼치는데 리액트는 props, state가 바뀌었을 때 리렌더링을 하게된다. 예를 들어 기존 props로 A

Z까지 전달하게 된다면(B ~

Y구간에는 이 props를 사용하지 않는다.) A의 state, props 바뀌면 불필요한 리런데링으로 사이트 성능이 안좋다.

  1. 데이터의 흐름이 복잡하기 때문에 데이터의 흐름을 제약할 필요가 있음 (ex. 빈번하고 복잡한 변화, 비동기)
  2. 상태 변화가 일어나는 시점과 형태에 제약을 두어 상태 변화를 예측 가능하게 만들고자한다.
  3. 단방향 데이터 바인딩을 하는 Flux 패턴의 자바스크립트

기본 리액트의 기능만으로 대응하기 힘들 정도로 애플리케이션의 상태 관리가 복잡해졌다는 확신이 들 때 리덕스를 사용한다.

관련글 더보기