Redux란 무엇인가?

Simon·2024년 3월 27일
post-thumbnail

리덕스란?

  • 리덕스는 자바스크립트 애플리케이션에서 상태 관리를 위한 오픈 소스 라이브러리
  • 크로스 컴포넌트 또는 앱 와이드 상태를 위한 상태 관리 시스템

리액트 컨텍스트 vs 리덕스

리액트 컨텍스트는 잠재적인 단점이 몇 개 존재한다. 앱이 복잡해 질수록 리액트 컨텍스트를 사용하면 설정이 복잡해 지고 중첩된 JSX코드로 이어질 수 있다. 테마를 변경하거나 인증 같은 저빈도 업데이트에는 아주 좋지만 데이터가 자주 변경되는 경우에는 좋지 않다.

리덕스 동작 방식 개요

  • 단일 저장소: 앱의 모든 상태는 하나의 중앙 저장소에 저장
  • 불변성: 저장된 상태는 절대 직접 변경되지 않는다.
  • 단방향 데이터 흐름: 데이터는 컴포넌트에서 저장소로만 흐른다. 컴포넌트는 직접 저장소를 변경하지 않고 액션을 통해 변경을 요청합니다.
  • 리듀서: 액션을 받아 상태를 변환하는 함수이다. 리덕스는 앱의 모든 상태 변화를 처리하는 단일 리듀서를 사용한다.
  • 액션: 상태를 변환시키려는 의도를 표현하는 객체이며 저장소에 데이터를 넣는 유일한 방법
  • 구독: 컴포넌트는 저장소를 구독하여 상태 변경을 감지하고 UI를 업데이트

리덕스 사용

npm install redux 설치
아래 코드는 지금 redux 사이트에 들어가면 리덕스 툴킷과 리액트 리덕스를 추가하여 리덕스 스토어를 생성하는 다른 방법을 권하기에 권한 방법과 알맞지 않은 코드이지만 기본적인 작동 방식을 이해하기 위해 코드를 작성하고 동작 과정을 기록하려고 한다.

리덕스 기본 실행 코드

const redux = require("redux");

const counterRudecer = (state = { counter: 0 }, action) => {
  return {
    counter: state.counter + 1,
  };
};

const store = redux.createStore(counterRudecer); // 1

const counterSubscriber = () => {
  // 저장소 상태가 변경될 때마다 트리거
  const latestState = store.getState(); // 최신 상태 스냅샷 제공
  console.log(latestState);
};

store.subscribe(counterSubscriber);

store.dispatch({ type: "increment" }); // 액션을 발송
// 리듀서와 구독 함수를 모두 리덕스가 실행
  • redux.createStore()를 이용하여 저장소를 생성
  • counterRudecer 상태를 변환 시킬 리듀서 함수를 생성하고 createStore()에 전달
  • 저장소 상태가 변경될 때마다 실행시킬 counterSubscriber subscribe()에 전달
  • 리듀서 함수를 실행시키기 위한 dispatch함수

실행과정

액션 발생 -> 리듀서 실행 -> 상태 업데이트 -> 구독 함수 실행 -> UI 업데이트(이 코드는 console 출력)
실제 코드 실행시 출력되는 값은 2가 출력된다. 왜냐하면 처음 저장소가 초기화될 때 리덕스가 리듀서를 처음으로 실행하기 때문이다. state = { counter: 0 } 해준 이유도 처음에 리듀서가 실행될때 초기값이 정의되어 있지 않으면 오류가 발생한다. 따라서 초기에 리듀서 함수가 실행되어counter 값이 1이 되고 dispatch()로 인해 2가 된다.

react-redux 패키지 사용

npm install react-redux 추가 설치
기존에 코드와 다른점은 subscribe함수와 dispatch를 사용하지 않고 외부로 export하였다.

import { createStore } from "redux";

const counterReducer = (state = { counter: 0 }, action) => {
  if (action.type === "increment") {
    return {
      counter: state.counter + 1,
    };
  }

  if (action.type === "decrement") {
    return {
      counter: state.counter - 1,
    };
  }

  return state;
};

const store = createStore(counterReducer);

export default store;

react-redux 패키지 사용

앞서 정의한 리덕스 저장소를 리액트 앱에서 사용하기 위해서는 다음과 같은 과정이 필요하다.

  1. App 컴포넌트를 렌더링하는 index.js 파일 상단에서 import {Provider} from 'react-dedux';
  2. 리덕스 저장소를 제공하기 위해 import store from './store/index.js' 후 App Component에 제공

react-redux 패키지 사용 (useSelector, useDispatch)

실제로 store에 저장되있는 데이터를 필요로 하는 컴포넌트에서 사용하는 방법은 아래와 같다.

  • 사용할 파일의 상단에서 react-redux에서 useSelector와 useDispatch Hook을 import
  • useSelector Hook을 사용하여 리덕스 스토어의 counter state를 counter 변수에 할당
  • useDispatch Hook을 사용하여 컴포넌트에서 리덕스 액션을 전달

useSelector

  • useSelector Hook은 리덕스 스토어에서 state를 가져오는 데 사용
  • 컴포넌트에서 리덕스 state에 직접 접근할 수 없기 때문에 useSelector Hook을 사용하여 state를 가져와야 한다
  • useSelector Hook은 함수를 받아 인수로 state를 전달
  • 함수는 state의 일부 또는 전체를 선택적으로 반환 가능
  • 컴포넌트는 반환된 state 값을 사용하여 UI를 렌더링

useDispatch

  • useDispatch Hook은 리덕스 스토어에 액션을 dispatch하는 데 사용
  • 컴포넌트는 직접 액션을 dispatch할 수 없기 때문에 useDispatch Hook을 사용하여 dispatch해야 한다
  • useDispatch Hook은 dispatch 함수를 반환
  • 컴포넌트는 dispatch 함수를 사용하여 액션 객체를 전달하여 스토어를 업데이트
import { useSelector, useDispatch } from "react-redux";

import classes from "./Counter.module.css";

const Counter = () => {
  const dispatch = useDispatch();
  const counter = useSelector((state) => state.counter);
  // react-redux는 이 컴포넌트를 위해 리덕스 저장소에 자동으로 구독을 설정
  // 따라서 리덕스 저장소에서 데이터가 변경될 때마다 자동으로 업데이트되고 최신 counter를 받는다.

  const incrementHandler = () => {
    dispatch({ type: "increment" });
  };

  const decrementHandler = () => {
    dispatch({ type: "decrement" });
  };

  const toggleCounterHandler = () => {};

  return (
    <main className={classes.counter}>
      <h1>Redux - Counter</h1>
      <div className={classes.value}>{counter}</div>
      <div>
        <button onClick={incrementHandler}>Plus</button>
        <button onClick={decrementHandler}>Minus</button>
      </div>
      <button onClick={toggleCounterHandler}>Toggle Counter</button>
    </main>
  );
};

export default Counter;

dispatch 함수로 전달하는 action 객체 type은 사전에 정의 해놓은 redux store의 리듀서 함수의 타입과 일치하는 type을 전달하여 일치하는 조건에 문을 실행하게 함

payload 연결

지금까지는 단순히 dispatch를 이용하여 액션 개체를 전달하여 고정된 값만큼만 counter를 증가시켰는데 액션 객체로 type만 전달하는 것이 아니라 페이로드를 같이 전달함으로써 고정된 값이 아닌 가변적으로 데이터 값을 조작할 수 있다.

페이로드
페이로드는 액션에 포함된 데이터를 의미한다. 리덕스에서 액션은 단순히 무슨 일이 일어났는지 알려주는 역할만 하는 것이 아니라, 액션과 함께 추가적인 데이터를 전달할 수 있습니다. 이러한 추가적인 데이터가 바로 페이로드

counterReducer를 정의한 파일로 이동하여 아래 코드 추가

if (action.type === "increase") {
    return {
      counter: state.counter + action.amount,
    };
  }

Counter 컴포넌트에 페이로드를 전달하는 함수추가, amount라는 전달되는 데이터의 이름은 임의적인 것이고 고정된 것이 아니다.

  const increaseHandler = () => {
    dispatch({ type: "increase", amount: 5 });
  };

리덕스 action

  • 액션은 상태를 변환시키려는 의도를 표현하는 객체이며 저장소에 데이터를 넣는 유일한 방법(모든 데이터는 액션으로 보내짐)
  • 액션은 어떤 형태의 액션이 행해질지 묘사하는 type 필드를 가져야 한다.
  • 문자열은 직렬화될 수 있기 때문에 문자열 사용 권장

리덕스 Reducer

  • 리듀서는 누적값과 값을 받아서 새로운 누적값을 반환하는 함수
  • Redux에서 누적값은 상태 객체이고, 누적될 값은 액션이다.
  • 반드시 같은 입력이 있으면 같은 출력을 반환하는 순수 함수여야만 한다.

순수함수 : 순수 함수는 입력값에 대해 항상 동일한 출력값을 반환하고, 외부 상태를 변경하지 않는 함수를 의미합니다. 즉, 함수의 실행 결과는 오직 입력값에만 의존하며, 외부 요인에 영향을 받지 않습니다.

profile
포기란 없습니다.

0개의 댓글