Redux

dang2·2023년 7월 22일
1

React

목록 보기
5/5

Redux(리덕스)란?

Redux(리덕스)란 자바스크립트 상태관리 라이브러리이다.

사용하는 이유


state 종속성 탈피

우리는 useState를 사용할 경우 컴포넌트 내부에 state를 만들고, 함수로 state를 바꾼다. 그렇기 때문에 state는 컴포넌트에 종속되는 것은 당연한 결과이다. redux는 컴포넌트에 종속되지 않고, 상태관리를 컴포넌트 바깥에서 한다. 프로젝트 루트 레벨에서 store라는 곳에 state를 저장하고, 모든 컴포넌트는 store에 구독을 하면서 state와 그 state를 바꾸는 함수를 전달 받게 된다. 함수를 바꿈으로 state가 바뀌면 해당 state를 바라보고 있는 컴포넌트는 모두 리렌더링 된다.

우리는 useState를 사용할 경우 컴포넌트 내부에 state를 만들고, 함수로 state를 바꾼다. 그렇기 때문에 state는 컴포넌트에 종속되는 것은 당연한 결과이다. redux는 컴포넌트에 종속되지 않고, 상태관리를 컴포넌트 바깥에서 한다. 프로젝트 루트 레벨에서 store라는 곳에 state를 저장하고, 모든 컴포넌트는 store에 구독을 하면서 state와 그 state를 바꾸는 함수를 전달 받게 된다. 함수를 바꿈으로 state가 바뀌면 해당 state를 바라보고 있는 컴포넌트는 모두 리렌더링 된다.

props → props → props 지옥 탈출

우리가 원하는 state가 자식의 자식의 자식에서 사용한다면 props를 내리고 또 내리고 또 내려야한다. 또 그 state를 바꾸기 위한 함수를 또 내리고 내린다. 이렇게 되면 코딩을 실수하기 쉽고 잘하고 있는 것인지 의문이 들것이다. redux의 store는 프로젝트 루트레벨에 위치하고, 해당 store를 구독하는 컴포넌트는 모두 state와 state를 바꾸는 함수를 받을 수 있게 된다. 어느 위치에 있든 상관 없이 단 한번에 상태를 받을 수 있게 된다.

redux 기본 원리


redux는 기본적으로 flux 패턴을 따른다.

Action -> Dispatch -> Store -> View

redux의 데이터 흐름은 동일하게 단방향으로 view(컴포넌트) 에서 Dispatch(store에서 주는 state를 바꾸는 함수) 라는 함수를 통해 action(디스 패치 함수 이름) 이 발동되고 reducer에 정의된 로직에 따라 store의 state가 변화하고 그 state를 쓰는 view(컴포넌트) 가 변하는 흐름을 따른다.

Redux의 세가지 원칙

  1. Single source of truth
  • 동일한 데이터는 항상 같은 곳에서 가지고 온다
  • 즉, 스토어라는 하나뿐인 데이터 공간이 있다는 의미이다
  1. State is read-only
  • 리액트에서는 setState 메서드를 활용해야만 상태 변경이 가능하다.
  • 리덕스에서도 액션이라는(Action) 객체를 통해서만 상태를 변경할 수 있다.
  1. Changes are made with pure functions
  • 변경은 순수함수로만 가능하다.
  • 리듀서와 연관되는 개념이다.
  • Store(스토어) - Action(액션) - Reducer(리듀서)

리액트 리덕스 실행

yarn add redux react-redux
//또는
npm install redux react-redux

Store, Action, Dispatcher, Reducer의 의미와 특징


Store(스토어)

Store는 상태가 관리되는 오직 하나의 공간이다.

  • 컴포넌트와는 별개로 스토어라는 공간이 있어서 그 스토어 안에 앱에서 필요한 상태를 담는다.
  • 컴포넌트에서 상태 정보가 필요할 때 스토어에 접근한다 .

Action(액션)

Action(액션)은 앱에서 스토어에 운반할 데이터를 말한다.(주문서) Action(액션)은 자바스크립트 객체 형식으로 되어있다.

  • Simple JavaScript Object
{
  type: 'ACTION_CHANGE_USER', // 필수
  payload: { // 옵션
    name: '유정',
    age: 30
  }
}

Dispatcher(디스패처 함수)

액션을 전달하는 함수.

  • 디스패처(Dispatcher) 함수는 액션 값과 상태에 관한 데이터를 리듀서(Reducer) 함수에 전달하는 역할을 한다.

Reducer(리듀서)

  • reducer는 store에 들어갈 state와 state를 바꿀 함수를 정의 하는 곳
  • Action(액션)을 바로 Store(스토어)에 바로 전달하는 것이 아니다.
  • Action(액션)을 Reducer(리듀서)에 전달해야한다.
  • Reducer(리듀서)가 주문을 보고 Store(스토어)의 상태를 업데이트 하는 것이다.
  • Action(액션)을 Reducer(리듀서)에 전달하기 위해서는 dispatch() 메소드를 사용해야한다.
  • 기본적으로 순수함수로 코딩하고, 불변성을 지켜야한다.

불변성을 지켜야하는 이유

불변성을 지켜야하는 이유는 redux는 이전 state와 바뀐 state를 구분하는 방법이 참조값이 바뀌었는지 학인하고, 참조값이 바뀌면, state가 바뀌었다고 redux가 인식하여, 해당 state를 사용하는 컴포넌트에게 리렌더링을 요청하기 때문이다. 그렇기 때문에, state.test = action.test와 같이 직접 state를 변경하면 참조값이 변하지 않아서 redux는 값이 바뀌었다고 인식하지 않고 리렌더링 되지 않는다. 불변성을 유지 하여야한다. 따라서 state.test={ …test, action.test }

또는 immer 라는 라이브러리를 사용하여 불변성을 유지할 수 있다.

Redux의 장점

  • 상태를 예측 가능하게 만든다. (순수함수를 사용하기 때문)
  • 유지보수 (복잡한 상태 관리와 비교)
  • 디버깅에 유리 (action과 state log 기록시) → redux dev tool (크롬 확장)
  • 테스트를 붙이기 용이 (순수함수를 사용하기 때문)

예시

  1. combinereducer로 리듀서 함수들 통합하기
import { combineReducers } from "redux"
import counter from "./counter"

const rootReducer = combineReducers({
  counter,
})

export default rootReducer

2-1 .세부 reducer 정의

import { INCREASE, DECREASE, CounterActionTypes } from "src/types/actions"

export const increaseCount = (): CounterActionTypes => ({ type: INCREASE })
export const decreaseCount = (): CounterActionTypes => ({ type: DECREASE })

const initialState = {
  count: 0,
}

const counter = (state = initialState, action: CounterActionTypes) => {
  switch (action.type) {
    case INCREASE:
      return {
        ...state,
        count: state.count + 1,
      }
    case DECREASE:
      return {
        ...state,
        count: state.count - 1,
      }
    default:
      return state
  }
}

export default counter

2-2. type 정의

interface CounterState {
  counter: {
    count: number
  }
}

export default CounterState

2-3. action정의

export const INCREASE = "COUNT/INCREASE"
export const DECREASE = "COUNT/DECREASE"

interface IncreaseAction {
  type: typeof INCREASE
}

interface DecreaseAction {
  type: typeof DECREASE
}

export type CounterActionTypes = IncreaseAction | DecreaseAction
  1. Provider 적용

Provider란 ? react-redux 라이브러리 안에 있는 컴포넌트. 리액트 앱에 스토어를 쉽게 연결하기 위한 컴포넌트이다.

import { Provider } from "react-redux"
import store from "../store"
import { AppProps } from "next/app"

function MyApp({ Component, pageProps }: AppProps) {
  return (
    <Provider store={store}>
      <Component {...pageProps} />
    </Provider>
  )
}

export default MyApp
  1. 컴포넌트에서 redux 사용
import React from "react"
import { useDispatch, useSelector } from "react-redux"
import { increaseCount, decreaseCount } from "../reducers/counter"
import CounterState from "src/types/state"

const Counter = () => {
  //component
  const dispatch = useDispatch()
  const count = useSelector((state: CounterState) => state.counter.count)

  const increment = () => {
    dispatch(increaseCount())
  }

  const decrement = () => {
    dispatch(decreaseCount())
  }

  return (
    <div>
      Count: {count}
      <button onClick={increment}>Increase Count</button>
      <button onClick={decrement}>Decrease Count</button>
    </div>
  )
}

export default Counter

다음과 같이 store에서 useDispatch, useSelector 로 state와 함수 가져와서 필요시 호출해주면 된다.

useSelector란?

-redux의 state 조회 (즉, 스토어에 있는 데이터들 조회)

useDispatch란?

-생성한 action 실행

0개의 댓글

관련 채용 정보