[블로깅] Redux

WAYPIL·2023년 4월 23일
0

코드스테이츠 부트캠프 프론트엔드 44기
Section 3 Unit 4 : [React] 상태 관리


1. 개요

Redux란 상태 관리 라이브러리 중 하나로, 관리하기 쉬운 전역 상태 저장소를 제공함으로써 Props의 연속 상속(Props drilling) 문제를 해결하는 역할을 담당한다.

Redux를 사용하려면 redux와 react-redux 둘 다 설치되어 있어야 한다.

Redux의 3원칙

  • [Single source of truth] : 동일 데이터는 항상 같은 곳에서 가져와야 한다. Redux에선 데이터를 저장하는 Store라는 공간이 단 하나뿐이라는 원칙.

  • [State is read-only] : 상태(State)는 읽기 전용. Redux의 상태는 Action 객체가 있어야만 변경 가능하다는 원칙.

  • [Changes are made with pure functions] : 변경은 순수함수로만 가능. 상태가 엉뚱한 값으로 변경되는 일이 없도록 순수함수로 작성되어야 하는 Reducer와 연결되는 원칙.


2. Redux의 데이터 흐름 ★

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

상세하게는 다음과 같다.

  1. 상태가 변경되어야 하는 이벤트가 발생하면, 변경될 상태에 대한 정보가 담긴 Action 객체가 생성.

  2. 해당 Action 객체가 Dispatch 함수의 인자로 전달됨.

  3. 해당 Dispatch 함수가 Action 객체를 Reducer 함수로 전달시킴.

  4. 해당 Reducer 함수는 Action 객체의 값을 확인하고, 그 값에 따라 전역 상태 저장소 Store의 상태를 변경.

  5. 상태가 변경되면, React는 화면을 리렌더링.


3. Redux의 구조

Action

어떤 액션을 취할 것인지 정의해 놓은 객체.

HTTP 프로토콜로 비유하면 type이 헤더(필수), payload가 body 역할이다.

{ type: 'INCREASE' }  // 대문자 & Snake Case로 작성.
{ type: 'SET_NUMBER', payload: 5 }

보통 Action을 직접 작성하기보단, Action 객체를 생성(리턴)하는 함수를 만들어 사용한다. 이러한 함수를 액션 생성자(Action Creator)라고 한다.

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

const setNumber = (num) => {  // payload가 있어야 하는 경우
  return {
    type: 'SET_NUMBER',
    payload: num
  }
}

Dispatch

Reducer로 Action을 전달해 주는 함수. Action 객체를 전달인자로 넣어야 한다.

import { useDispatch } from 'react-redux'

const dispatch = useDispatch();  // Dispatch 사용

// Action 객체
dispatch( { type: 'INCREASE' } );
dispatch( { type: 'SET_NUMBER', payload: 5 } );

// 액션 생성자
dispatch( increase() );
dispatch( setNumber(5) );

Reducer

(Dispatch로부터 전달받은) Action 객체의 type 값에 따라서 상태를 변경시키는 함수. 반드시 순수함수여야 한다.

  • Reducer를 생성할 때에는 초기 상태를 인자로 요구한다. (state = count)
  • Action.type값에 따라 실행문을 switch로 분기한다.
  • 리턴값이 새로운 상태가 된다.
const count = 1

const counterReducer = (state = count, action) => {
  switch (action.type) {
    case 'INCREASE':
      return state + 1;
    case 'DECREASE':
      return state - 1;
    case 'SET_NUMBER':
      return action.payload;  // 새로운 값(숫자)
    default:
      return state;  // 변경되지 않은 기존 상태(숫자)
  }
}

const store = createStore(counterReducer);

여러 개의 Reducer를 사용하는 경우, Redux의 combineReducers 메서드를 사용해서 하나의 Reducer로 합칠 수 있다.

import { combineReducers } from 'redux';

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

Store

Redux 앱의 state가 저장되는 유일한 저장소. createStore 메서드로 Reducer를 연결해서 Store를 생성할 수 있다.

const store = createStore(reducer);

store에 저장되어 있는 데이터값을 불러오고 싶다면, <Provider store={store}>의 하위 컴포넌트에서 useSelector()를 사용한다.

import { useSelector } from 'react-redux'
const state = useSelector((state) => state);  // store에 저장된 내용물

4. Flux ★

Flux 패턴이란, 기존 MVC 패턴의 단점을 보완하기 위해 페이스북에서 발표한 아키텍처를 말한다. 대표적으로 Redux가 Flux 모델로 동작한다.

MVC 패턴

MVC 패턴은 쌍방향으로 동작한다.

Model의 데이터가 변경되면 View로 전달되어 사용자에게 보여지고, 사용자가 View를 통해 데이터를 입력하면 Model을 업데이트할 수 있다.

그런데 만약에 애플리케이션의 규모가 커진다면?

이렇듯 지옥이 펼쳐진다. 뿐만 아니라 어떤 Model이 변경되었을 때 모든 View에 자동 적용되지 않기 때문에 (할 수는 있지만 그러면 최적화 문제가...) 여러 버그를 야기할 수 있다. 예를 들어서 모든 알림을 확인했음에도 푸시 알림 마크가 사라지지 않는다든가 등등.

Flux 패턴

Flex 모델을 적용하면 이러한 문제를 해결할 수 있다. Flux는 MVC와 다르게 단방향으로 데이터가 흐른다.

각종 Model들을 하나의 Store로 묶고, 사용자의 입력을 (Model로 직접 전달하는 게 아닌) Action을 통해 Dispatcher로 우회 전달함으로써 단방향 원칙을 훼손하지 않게 된다.

Redux의 데이터 흐름을 배울 때 '왜 이렇게 거치는 게 많냐', '왜 다이렉트로 안 되고 이렇게 뱅뱅 도냐'라고 생각할 수도 있다. 하지만 'MVC의 단점을 극복하기 위한 부산물'이라고 생각하면 크게 거부감 들진 않을 것이다. 큰 열매를 얻기 위해 작은 수고를 행하는 것, 인생이 다 그런 것 아니겠는가?



<주의 사항>

이 게시물은 코드스테이츠의 블로깅 과제로 제작되었습니다.
때문에 설명이 온전치 못하거나 글의 완성도가 낮을 수 있습니다.

profile
Self-improvement Guarantees Future.

0개의 댓글