Redux 알아보기 -현대식 상태관리-

김민규·2022년 12월 31일
0
post-thumbnail

상태 관리란(state)

리액트의 핵심 개념으로는 컴포넌트, 가상 DOM, JSX 등이 존재한다.
하지만 그중에서도 중요하다고 할 수 있는 개념이 바로 상태관리이다.

React는 상태의 변화를 통해 컴포넌트의 렌더링 여부를 결정한다.
또한 직접적인 DOM으로의 접근 없이 오로지 자바스크립트를 통해서 상태를 관리하여 DOM 중심의 코드에서 탈출할 수 있는 길이 생기고 성능면에서도 뛰어난 효과를 볼 수 있었다.


상태 관리 라이브러리의 등장

기존 React에서 로컬 상태가 아닌 서로 다른 컴포넌트간 상태를 공유하기 위해서는 하위 컴포넌트로 상태를 반복적으로 전달하는 Props Drilling상태 끌어올리기(lifting state up) 를 이용하여 컴포넌트간의 상태를 공유할 수 있었다.

하지만 컴포넌트간의 거리가 멀 경우 과도한 Props Drilling과 상태 끌어올리기로 인하여 단순한 상태 공유를 위해 코드가 복잡해지는 상황이 발생하게 된다.
(예를 들어서 쇼핑몰의 상품 컴포넌트의 데이터를 장바구니 컴포넌트로 넘겨주려면 상태를 메인 APP 컴포넌트까지 끌어올린 후 장바구니 컴포넌트까지 drilling 해야 한다.)

- 좋지 못한 상태관리 예시 -

또한 SPA의 발전으로 인하여 너무나도 많은 상태를 관리해야하는 상황이 등장했고 이는 개발자가 상태의 변화를 예측하고 제어하는데 큰 어려움을 주었다.

이러한 전역 상태를 조금 더 수월하게 관리하기 위하여 Redux, Recoil, MobX, Jotai, Justand 등 다양한 상태 관리 전용 라이브러리들이 세간에 등장하였다.

Recoil이나 Context-API 등 대체제가 많이 나온 시점에서 Redux가 조금 뒤쳐졌을지는 몰라도 아직까지도 Redux는 씬에서 높은 점유율을 가지고 있고, 많은 상태 관리 라이브러리들이 Redux를 바탕으로 설계되었기에 우리는 Redux에 대해 알아야할 필요가 있다.


- Redux를 통해 단순해진 상태관리 -


Redux 3원칙

Redux는 상태 관리의 복잡성을 줄이기 위해 다음과 같은 3가지 원칙을 세웠다.

진실은 하나의 근원으로부터

애플리케이션의 모든 상태는 하나의 저장소 안에 하나의 객체 트리 구조로 저장된다

모든 상태는 하나의 store에 보관된다.
이를 통해 범용적인 어플리케이션을 만드는데 수월해지고 디버깅에도 용이해진다.

상태는 읽기 전용

상태를 변화시키는 유일한 방법은 무슨 일이 벌어지는 지를 묘사하는 액션 객체를 전달하는 방법뿐이다

상태는 오로지 dispatch를 통해 중앙에 전달된 action을 통해서 순서대로 엄격하게제어되면 그 외의 경로를 통해 제어되지 않는다.

변화는 순수 함수로 작성

액션에 의해 상태 트리(store)가 어떻게 변화하는 지를 지정하기 위해 프로그래머는 순수 리듀서를 작성해야한다.

action의 값에 따라 상태를 변경시키는 일은 reducer가 담당한다.
reducer는 순수 함수로서 네트워크 요청 등 외부적인 변수로 인해 아웃풋이 달라져서는 안되며 새로운 상태를 만들어 반환한다.


Redux 용어

상태(state)

상태는 저장소(store)에 의해 관리되는 getState()로 반환되는 하나의 객체를 의미한다.

const count = useSelector((state) => state.count);

Redux의 내부 메소드인 useSelector((state) => state)를 통해서 저장소에 존재하는 상태를 꺼내와 구독 할 수 있으며 이에 따른 리렌더링이 실시된다.

저장소(store)

저장소는 상태를 가지고 있는 객체로서 단 하나만 존재한다.
redux의 내장 메서드인 createStore를 통해 생성되며 인자로 reducer를 받는다.

Redux의 내부 컴포넌트인 Providerstore 어트리뷰트에 생성한 저장소를 지정하여 하위 컴포넌트의 상태 구독을 설정할 수 있다.

액션(action)

{
  type: "increase",
  value: 5
}

액션은 상태를 어떻게 변화시킬지에 대한 정보를 담은 객체로서 type 필드를 갖는다. 타입은 주로 문자열로 작성한다.
dispatch를 통해 리듀서에 값을 보낸다.

객체의 불필요한 반복과 type 필드에서의 오탈자를 방지하기 위해 action을 생성하는 action creator 메서드를 만들어 사용할 수도 있다.

function increaseNumber(number) {
  return {
    type: "increase",
    value: number,
  }
}

dispatch(increaseNumber(5));

디스패치 함수(dispatch)

const dispatch = useDispatch();

dispath({ type: 'increase', value: 4 });

디스패치 함수는 리듀서를 향해 액션을 전달하는 함수이다.
Redux의 내부 메서드인 useDispatch를 통해 사용할 수 있으며 인자로 액션을 지정하면 Provide에 지정된 저장소의 리듀서로 액션이 전달된다.

리듀서(reducer)

// 일반적인 카운터 프로그램의 상태관리

const initialState = { count: 0 };

// state: 기존의 누적된 상태, action: dispatch를 통해 입력된 값
function reducer(state = initialState, action) {
  if(action.type === 'increase') {
    return { ...state, count: state.count + action.value };
  }
  
  if(action.type === 'decrease') {
    return { ...state, count: state.count - action.value };
  }
  
  return { ...state };
}

리듀서는 기존 상태와 액션을 받아서 새로운 상태를 반환하는 함수를 의미한다. 리듀서는 순수 함수여야하며 다른 부수효과를 가져서는 안된다.

리듀서에서 첫번째 인자로 들어오는 값이 우리가 알고있는 상태로서 관리된다.

또한 기존의 상태를 직접적으로 변경해서는 안된다. 상태를 비동기적으로 처리하는 과정에서 오류가 날 수 있으며 이는 베이직 리액트에서도 기존의 상태를 기반으로 변경할 때 콜백으로 상태를 업데이트 하는 이유와 같다.

이러한 이유로 인해 Redux Toolkit에서는 immer 라이브러리를 활용해 기존 상태의 직접적인 변경을 제한시키도록 설계됐다.


Redux Flow


Redux Install

redux의 설치는 프로젝트 경로에 npm을 사용하여 설치가 가능하다.

// react-redux
npm install react-redux

// redux toolkit
npm install @reduxjs/toolkit

referenced by Redux

profile
Error Driven Development

0개의 댓글