Redux

이윤택·2022년 8월 29일
0

리덕스란

Redux는 자바스크립트 앱을 위한 예측 가능상태 컨테이너입니다. (Redux is a predictable state container for JavaScript apps.)

리덕스는 자바스크립트 앱에서의 상태관리를 위한 라이브러리이다.

💡 리덕스는 React만의 라이브러리는 아니다. 자바스크립트 앱이라면 어디에서든 사용할 수 있다.

Redux Toolkit (RTK)

RTK는 리덕스 앱을 만들기 위해 필수적인 패키지와 함수들을 포함한다. RTK를 통해 리덕스 작업을 단순화하고, 리덕스 앱을 쉽게 만들 수 있게 해준다.

npm install @reduxjs/toolkit

React Redux 앱 만들기

리액트와 리덕스를 함께 사용하고자 할때는 create react app을 위한 Redux+JS 템플릿을 사용하는 것이 좋다. 이를 통해 RTK와 React Redux가 리액트 컴포넌트와 통합된다.

npx create-react-app [앱 이름] --template redux
💡 **Redux의 기본 원칙**

1. Single source of truth
: 동일한 데이터는 항상 같은 곳에서 가지고 온다. 즉, 스토어라는 하나뿐인 데이터 공간이 있다는 뜻이다

2. State is read-only
: 리액트에서의 상태 변경은 setState 로 가능하다. 리덕스에서는 액션이라는 객체를 통해서만 상태를 변경할 수 있다.

3. Changes are made with pure functions
: 변경은 순수함수로만 가능하다.
Store(스토어) → Action(액션) → Reducer(리듀서)

Flux 아키텍처

리덕스는 Flux 아키텍처를 기반으로 하고 있다.

Flux 아키텍처는 규모가 큰 어플리케이션의 상태를 관리하기 위한 패턴이다. Flux 패턴의 핵심은 단방향 데이터 흐름이다. 데이터는 한 방향으로만 흐른다. 단방향 흐름과는 별개로, Flux 아키텍처는 4개의 필수적인 컴포넌트 Action, Dispatcher, Store, View로 구성된다.

View는 기본적으로 컴포넌트 트리이다. 리액트는 이러한 View를 구현할 수 있다. 유저는 Action을 트리거하기 위해 View와 상호작용한다. Action은 Store에 있는 상태를 업데이트 하기 위한 모든 필수적인 정보를 캡슐화한다. Dispatcher는 이러한 Action을 Store에 전달한다. 그리고나서, 새로운 상태가 Store에서 View로 전송하여 View를 업데이트한다. 마지막으로 단방향 데이터 흐름의 루프를 닫는다.

1. 유저가 View와 상호작용
2. 이벤트 발생 시 View에서 Action 트리거
3. Action = 상태를 업데이트 하기 위한 캡슐화된 정보
4. Dispatcher는 Action을 Store에 전달
5. 새로운 상태가 Store에서 View로 전송되고 View가 업데이트된다

Pure Redux

리덕스와 Flux 아키텍처

공통점

  • Actions에서 상태 업데이트를 위한 정보를 캡슐화한다.
  • Store에서 상태를 저장한다

차이점

  • 리덕스에서는 Store가 단 하나(singleton)이다.
  • Flux 아키텍처에서는 하나의 Dispatcher만이 존재했으나, 리덕스에서는 여러개의 Reducer를 사용한다. 이 Reducer는 액션에서 캡슐화된 정보를 가져와 Store에 있는 이전 상태에서 새 상태로 정보를 'reduce'한다.
    // Flux 아키텍쳐
    View -> Action -> Dispatcher -> Store -> View
    
    // 리덕스
    View -> Action -> Reducer(s) -> Store -> View

리덕스(Redux)는 Reducer와 Flux를 합친 말이다. 리덕스의 요점은 상태가 더이상 View에만 머무르지 않고, View와 연결만 된다는 것이다. 연결된다는 것은, 단방향 데이터 흐름의 양 끝인 두가지 엔드포인트 사이에 연결되어 있다는 것이다. 첫번째 엔드포인트는 상태를 변화시키는 액션을 트리거하는 View이며, 두번째 엔드포인트는 스토어에서 상태를 수신하는 View이다. 결론적으로, View는 상태가 변경됨에 따라 업데이트되기도 하지만 상태 변경을 트리거할수도 있다.

Actions

리덕스에서의 액션은 자바스크립트 객체이다. 액션은 type과 다른 옵션 페이로드를 가지고 있다. 타입은 주로 action type이라고 불리며, 문자열 값을 가진다. 그 외의 다른 페이로드는 여러 형식을 가질 수 있다. 딱히 필요없다면 페이로드를 않아도 된다(대신 type은 필수).

가령, to-do list를 만드는 앱에서 새로운 todo를 추가하는 action은 다음과 같다.

{
	type: 'TODO_ADD',
	todo: {id: '0', name: 'learn redux', completed: false}
}

액션을 실행하는 것을 dispatching이라고 말한다. 리덕스 스토어에 있는 상태를 변경하기 위해 액션을 디스패치할 수 있다. 또한 View에서 액션 디스패치를 트리거할 수 있다.

Reducers

View가 액션을 디스패치하면 연결된 모든 리듀서에 액션이 넘겨진다.

리듀서는 순수 함수로, 같은 입력값에 대해 같은 출력값을 주고, 사이드 이펙트가 없다.

리듀서는 상태와 액션이라는 두가지 입력값을 받는다. 상태는 리덕스 스토어에 있는 전역 상태 객체이다. 액션은 type과 다른 페이로드가 들어있는 디스패치된 액션이다. 리듀서는 이전의 상태값과 액션를 조작해 새로운 상태를 만든다.

(prevState, action) => newState

또한, 리듀서는 입력값들에 대한 불변성을 유지한다. 따라서 새로운 상태를 만들 때 이전 상태값을 변경시키지 않는다.

// Wrong!! 
function(state, action) {
  state.push(action.todo); // 이전 상태값을 변경시킴
  return state;
}

// Great :)
function reducer(state, action) {
	// 이전 상태값을 변경시키지 않고 조작하여 새로운 상태를 리턴
  return state.concat(action.todo);
}

액션이 디스패치되면 리듀서는 가장 먼저 해당 액션의 type부터 확인한다. 그리고 type에 따라 어떻게 상태를 조작할지 결정한다.

function reducer(state, action) {
  switch(action.type) {
    case 'TODO_ADD' : {
      return state.concat(action.todo);
    }
    case 'TODO_TOGGLE' : {
      // do something and return new state
    }
    default : return state; // type이 없으면 이전 상태를 그대로 리턴
  }
}

Redux Store

리덕스 스토어는 다음의 작업을 한다.

  • 작업을 트리거하는 공간 제공
  • 리듀서에게 액션을 넘긴다
  • 업데이트된 상태를 View에 붙여준다

리덕스 사용법

createStore()

  • 리덕스 스토어 생성
  • 첫번째 인자: 리듀서, 두번째 인자: (Optional) 상태 초기값

import {createStore} from 'redux';

// 스토어 생성
const store = createStore(reducer, []);

store.dispatch()

  • 액션을 디스패치
  • 인자: 액션 객체
store.dispatch({
  type: 'TODO_ADD',
  todo: { id: '0', name: 'learn redux', completed: false },
});

store.getState()

  • 스토어에서 상태를 가져온다
store.getState();

store.subscribe()

  • 상태가 업데이트될 때마다 실행시킬 함수
  • 인자: 콜백함수
store.subscribe(() => {
  console.log(store.getState());
});

useSelector() (Redux Hooks)

  • 컴포넌트와 상태를 연결. 컴포넌트에서 useSelector 메서드를 통해 스토어의 상태에 접근할 수 있다.
  • 인자: 콜백함수. 콜백의 인자로는 상태가 들어간다.
import { useSelector } from 'react-redux'
const isLoggedIn = useSelector((state) => state.user.isLoggedIn);

useDispatch() (Redux Hooks)

  • dispatch 함수를 반환한다. dispatch 함수는 액션을 리듀서로 넘겨준다.
import { useDispatch } from 'react-redux'

export const CounterComponent = ({ value }) => {
  const dispatch = useDispatch()

  return (
    <div>
      <span>{value}</span>
      <button onClick={() => dispatch({ type: 'increment-counter' })}>
        Increment counter
      </button>
    </div>
  )
}
profile
데이터 엔지니어로 전향중인 백엔드 개발자입니다

0개의 댓글