리덕스 - 리덕스의 원칙과 패턴, 데이터 바인딩, Recoil과 Context API와 비교

GY·2022년 2월 10일
0

Redux

목록 보기
2/3
post-thumbnail

이전에 리덕스의 기본적인 개념에 대해 공부했었습니다.

🧐 리덕스가 뭘까

전역 상태관리를 위한 라이브러리로, Flux패턴을 따른다.

👉 리덕스의 세가지 원칙

1️⃣ 전체 상태값이 하나의 객체로 표현된다.

하나의 리액트앱에는 하나의 스토어가 있다.

2️⃣ 상태값은 읽기 전용이다.

state 값은 직접 바꾸지 않고, setState 함수를 이용해서만 변경한다.

3️⃣ 상태값은 순수 함수에 의해서만 변경되어야 한다.

  • 상태값을 변경시키려면 상태값을 변경하는 함수가 필요한데, 이 함수를 reducer라고 한다,.
  • 이 reducer는 순수함수여야 한다.

순수함수 (pure function)
항상 같은 인풋은 같은 아웃풋을 반환하는 함수


🧐 리덕스, 왜 사용해야할까?

1️⃣ 리액트는 단순 UI 라이브러리이다.

따라서 데이터를 관리하는데 적합한 라이브러리는 아니다.
로컬 스토리지에 데이터를 저장하고 불러오는 대신 별도로 데이터를 관리하려면 별도의 라이브러리가 필요하다.

2️⃣ 단방향 데이터 바인딩으로 인한 props drilling 보완

뷰와 모델, 즉 화면과 데이터를 연결하는 것을 뜻한다.

예를들어 JQuery나 DOM API는 직접 돔을 조작해 바꾸기 떄문에 화면과 데이터를 연결해 표시된 내용을 바꾸지 않는다.

단방향 데이터 바인딩

모델 -> 뷰로만 데이터가 흐르는 것으로, 리액트가 이에 속한다.
예를들어, 모델, 즉 데이터인 state를 업데이트하면 해당 state를 표시하고 있는 화면의 영역(뷰)이 그에 맞게 리렌더링 되면서 업데이트된다.

양방향 데이터 바인딩

Vue가 양방향 데이터 바인딩 방식을 사용


따라서 상위 컴포넌트에서 하위컴포넌트로 계속해서 props를 내려주거나, 하위 컴포넌트에서 바로 상위 컴포넌트의 state에 접근하여 변경하기 어려운 단점을 보완하기 위해 Redux를 사용하는 것이다.

예시

  • 페이지를 별도의 api호출 없이 유지해야 할 데이터가 있을 경우
  • 페이지의 레이아웃에 영향을 받지 않는 modal, toast, alert 같은 전역 UI 컴포넌트의 상태관리가 필요한 경우
  • 하나의 state를 여러 컴포넌트에서 사용하는 경우
  • 장바구니 (아이콘 숫자갯수 표시)
    - 담을 때마다 백엔드에 요청을 보내서 하나씩 저장
    • 웹에서 보다가 휴대폰에서 갑자기 다시 확인할 수도 있고...

🧐 리덕스의 패턴 - FLUX

1️⃣ MVC 패턴

관심사의 분리를 위해 탄생한 패턴
즉, 코드를 단순화하고 유지보수를 자유롭게 할 수 있도록 하기 위해 개발한 패턴이라고 할 수 있다.

단점

같은 모델이어도 쓰이는 뷰가 여러가지일 경우 1:1매칭이 되지 않아 구조가 복잡해졌다.

이미지 출처

이미지 추가 예정

이러한 단점을 보완하기 위해 새로운 패턴이 등장했는데, FLUX 패턴이다.

2️⃣ FLUX 패턴

단방향 데이터 흐름으로 만든 패턴이다.
Dispatcher > stores > views

Dispatcher

dispatcher를 통해 action을 발행한다.

Store

어플리케이션의 데이터와 비지니스 로직을 가지고 있는 store

views

화면에 표시


🧐 리덕스, 어떻게 사용할까?

  • store: 리덕스의 state을 갖는 객체
  • action: state 변화를 일으킬 수 있는 행동정보, 현상
  • dispatcher: action이 일어나면 dispatcher를 통해서 store의 state가 업데이트된다.
  • view: state가 변경되면 view에서 감지하고 화면에 반영된다.(리렌더링)

이미지출처


1️⃣ Action

사용자의 입력이나 UI 조작, 웹 요청 완료 같이 어떠한 상태변화를 일으킬 수 있는 형상
어떠한 조작을 일으킬 것인지에 대한 정보를 갖고 있는 자바스크립트 객체

특징

  • action은 객체이다.
  • action은 반드시 type 프로퍼티가 있어야 하고, 일반적으로 type은 어떤 행동을 설명하는 문자열이다. 즉 state를 어떻게 조작할 것인지를 나타내는 문자열이다.
  • type 이외에 다른 프로퍼티를 가져도 된다. 주로 action에 필요한 부가적인 데이터를 전달하고 싶을 때 추가로 다른 프로퍼티를 넣는다.

action 사용법

action은 객체이지만 객체리터럴로 선언하지는 않는다.
별도의 액션 생성함수(action creator)를 통해 만든다.
액션 생성함수가 별도로 있는 것은 아니고, action을 리턴하는 함수를 액션 생성함수라고 부른다.

아직 와닿지 않는다.. 예시를 보자.

function showModal({ title }) {
	return { type: 'SHOW_MODAL', title }
}
showModal({ title: '로그인' });

2️⃣ Reducer

reducer는 action이 발생했을 때 state를 변화시키기 위한 함수이다.
즉 reducer는 새로운 state를 반환하는 함수

state와 action을 인자로 받아 이 action의 type에 따라 state를 변화시킨다.

function modal(state, action) {

  switch(action.type) {
    case 'SHOW_MODAL':   // SHOW_MODAL 이라는 action이 발행되면
      return {
        ...state,
        showModal: true  // 전역 state 중에서 showModal의 값을 true로 바꾼다.
      };
    case 'CLOSE_MODAL':
      return {
        ...state,
        showModal: false
      };
    default:
      return state;
  }

}

3️⃣ store

store는 앱에 오직 하나만 있고, 이 store를 사용해 app의 전체 상태를 관리한다.
store안에는 현재 상태들, 리듀서, 그리고 몇 가지의 내장함수를 포함하고 있다.

사용법

  • redux에서 제공하는 createStore함수를 사용한다.
  • reducer를 인자로 전달해 생성한다.
import { createStore } from 'redux'
import modalReducer from './ModalReducer';

const store = createStore(modalReducer);

4️⃣ dispatch

디스패치는 스토어의 내장함수 중 하나로, 액션을 발생시키는 역할을 한다.
이 디스패치가 액션을 발생시켜 스토어에게 상태변화가 필요하다는 것을 알린다.
이렇게 액션이 호출되면 이 액션은 리듀서 함수를 호출시키고, 액션에 맞는 로직대로 상태를 변화시키는 과정을 거친다.

리액트 리덕스에서 제공하는 useDispatch 메서드를 임포트해 사용하면 생성한 액션을 발생시켜 액션 객체를 바로 내보낼 수 있다. 이렇게 되면 dispatch를 통해 reducer에서 액션타입에 따라 정의해둔 동작을 실행하게 되고, 바뀐 state가 store에 저장된다.

useSelector
connect함수를 이용하지 않고 리덕스 스토어의 상태를 조회할 수 있는 훅이다. 이 훅을 사용해 상태를 조회하면 상태값이 변경되지 않으면 리렌더링이 발생하지 않는다.

🧐 리덕스 미들웨어

클라이언트에서 요청받고 응답하는 사이에 위와 같이 거쳐가는 여러함수들을 의미
예를 들면 CORS처리, 라우트 처리, 오류 처리 등등 응답과 요청 사이에 다양한 기능을 추가할 수 있다.
리덕스의 미들웨어는 action과 reducer사이, action을 dispatch하고 reducer로 state가 변경되기 전 그 사이에서 여러 작업을 한다.

로그를 남기거나 요청을 비동기로 처리하는 등의 작업을 할 수 있다.

redux toollkit과 함께 리덕스 미들웨어 중 redux-saga를 사용하는 방법에 대해서도 공부했는데, 해당 내용은 다음 포스팅에 정리해두었습니다.
👉 포스팅 보러가기


👉 Redux-thunk vs Redux-saga

Redux-thunk와 Redux-saga는 대표적인 미들웨어들로, 비동기 요청을 처리하는 작업에 많이 쓰이는데, 이 들의 차이에 대해서는 다음 포스팅에 정리해두었습니다.
👉 포스팅 보러가기


🧐 다른 상태관리 라이브러리와 비교하기

1️⃣ Recoil과 비교

Recoil은 쉽고 직관적이다.

Recoil은 atoms로 state를 관리하고 각 컴포넌트에서 필요한 atom을 구독한다.
또 useState을 사용하듯이 간편하게 읽어온 state를 업데이트 할 수 있기 때문에 러닝커브가 상대적으로 낮고 빠르게 도입할 수 있다.

Redux는 상대적으로 보일러플레이트가 많이 필요하다.

만약 단순한 상태관리를 할 목적이라면 context API나 Recoil을 사용하는 것이 더 좋은 선택이라고 생각한다. 2주간의 프로젝트를 하면서 후반부에 전역상태관리를 도입해야했을 때 이러한 이유로 Recoil을 선택하기도 했었다.

다만, Redux-toolkit을 사용해보니 기존 리덕스보다 간편하게 사용할 수 있어 이러한 부분은 조금 해소가 될 것 같다.

여기까지가 직접 사용해보면서 느낀 장단점인데, 프로젝트 규모가 크지 않아 그 이상의 차이점에 대해서는 느끼기 어려웠다.

이 외에도 캐싱지원, 에러 핸들링 등의 측면에서 Redux와 비교해 Recoil이 아쉬운 점이 있다고 하는데, 아직 경험해보지 않아 와닿지는 않는다. 추가로 프로젝트에서 사용할 기회가 생긴다면 둘의 차이점을 비교해보아야겠다.

2️⃣ constext API와 비교

둘 다 전역 상태관리를 위한 도구이며, Redux 또한 Context API를 가지고 만든 라이브러리이다.

Context API보다 Redux가 잦은 상태변경에도 성능을 유지할 수 있다.

비동기적으로 전체 state를 갖고 와야할 때나, 복잡하게 state를 변경해야한다거나, 프로젝트 규모가 커질 때 context API를 사용하는 것은 쉽지 않다.
(리덕스와 contextAPI를 같이 쓰는 경우도 있기는 하다고 한다.)

단순 전역 상태 관리 이외의 추가기능 제공

리덕스는 Context API와 다르게 전역상태관리외에도 다양한 기능을 제공한다.
특히 redux-saga, redux-thunk와 같은 미들웨어와 redux-devtools 등 다양한 추가 라이브러리를 사용해 상태관리를 수월하게 하고 부가적인 작업을 효율적으로 처리할 수 있다.

즉, 이렇게 정리해볼 수 있을 것 같다.

  • 단순히 전역 상태관리만 필요할 경우 간단하게 Context API를 사용해도 충분하다.
  • 복잡하거나 잦은 상태변경이 이루어질 경우 성능저하가 우려되므로 Redux를 사용하는 것이 더 좋다.
  • 상태관리 이외에도 다양한 기능을 필요로 할 경우 Redux를 사용해야 한다.


Reference

profile
Why?에서 시작해 How를 찾는 과정을 좋아합니다. 그 고민과 성장의 과정을 꾸준히 기록하고자 합니다.

0개의 댓글