front-end/redux

[redux] 개요

Ash_O 2024. 2. 2. 16:24

redux overview

This tutorial will introduce you to the core concepts, principles, and patterns for using Redux.

part1

  • 리덕스 앱 예제 소개

part2

  • 리덕스 앱에서 데이터가 흐르는 방식과 디테일 소개

part3

  • state, action, reducer에 대한 활용
  • redux가 어떻게 작동하는지 설명

이후

  • 표준 패턴, 추상화

what is redux

리덕스는 app 상태를 ‘action’이라고 불리는 이벤트를 이용해서 관리하고 갱신하는 패턴과 라이브러리이다.

상태에 대해 중앙 집중식 저장소 역할을하고, 예측 가능한 방식으로만 업데이트 될 수 있도록 하는 규칙을 제공한다.

why should i use Redux

리덕스는 app의 많은 부분에 걸쳐서 필요한 전역상태 관리를 돕는다

  • 리덕스가 제공하는 패턴과 도구는 app의 상태가 언제, 어디서, 왜, 어떻게 업데이트되는지 그리고 이런 변경 사항이 발생할 때 app 로직이 어떻게 동작할지 이해하기 쉽게 만들어준다
  • 리덕스는 예측 가능하고 테스트 가능한 코드를 작성하도록 안내한다

when should i use redux

리덕스는 공유되는 상태 관리를 다루는데 유용한 도구이지만, 장단점이 있다

배워야할 개념이 많고, 작성할 코드가 많다

또한 코드에 약간 간접성을 추가하고 특정 제한 사항을 따르도록 요구한다

단기적인 생산성과 장기적인 생산성 사이의 절충이 있다

유용한 상황

  • app의 여러 곳에서 필요한 상태가 많은 경우
  • 상태가 시간이 지남에 따라 자주 업데이트되는 경우
  • 상태를 업데이트하는 로직이 복잡한 경우
  • app의 코드 베이스가 중간 이상이며 여러 사람이 작업할 수 있는 경우

모든 앱에 리덕스가 필요한건 아니다

어떤 종류의 앱을 구축하고 있는지 생각해보고, 작업 중인 문제를 해결하는 데 가장 적합한 도구를 결정하는 데 시간을 투자하자

redux libraries and tools

https://ko.redux.js.org/tutorials/fundamentals/part-1-overview#redux-libraries-and-tools

  • react-redux
  • redux toolkit
  • redux devtools extension

Redux Basic

Redux Store

모든 리덕스 앱의 중심은 store 이다

store는 앱의 전역 상태를 저장하는 컨테이너임

store는 일반적인 전역 객체와 다른 몇 가지 특별한 함수와 기능을 가진 JavaScript 객체이다.

  • Redux 스토어 내부에 저장된 상태를 직접 수정하거나 변경하면 안된다.
  • 대신, 상태를 업데이트 하려면 ‘앱에서 발생한 일’ 을 설명하는 action 객체를 만들고, 발생한 일을 알리기 위해 그 action 을 스토어에 dispatch해야한다.
  • action이 dispatch되면 store는 루트 reducer 함수를 실행하고, 이전 상태와 action을 기반으로 새로운 상태를 계산할수 있게 한다
  • store는 상태가 업데이트되었음을 subscriber에 알리면서 UI가 새로운 데이터로 업데이트될 수 있도록 한다.

즉, store에 action을 dipatch하는 식으로 동작

dispatch되면 store는 그에 대응하는 reducer함수를 실행하고, 상태를 업데이트한다

subscribe에 상태 업데이트를 알리면, UI가 그에 대응한다.

redux core example app

counter example

https://github.com/jypark38/redux_tutorial/tree/main/01.counter_application

// cdn
import { createStore } from "<https://unpkg.com/redux@latest/dist/redux.browser.mjs>";

// Define an initial state value for the app
const initialState = {
  value: 0,
};

 

initialState

  • counter의 현재 값을 추척한다
  • 리덕스 앱은 상태의 루트 요소로 js객체를 가지며, 해당 객체 내부에 다른 값들이 들어간다

 

function counterReducer(state = initialState, action) {
  // Reducers usually look at the type of action that happened
  // to decide how to update the state
  switch (action.type) {
    case "counter/incremented":
      return { ...state, value: state.value + 1 };
    case "counter/decremented":
      return { ...state, value: state.value - 1 };
    default:
      // If the reducer doesn't care about this action type,
      // return the existing state unchanged
      return state;
  }
}

 

reducer

  • 리듀서를 정의한다
  • 리듀서는 두 인자를 받는다
    1. state
    2. action : 무슨 일이 일어났는지 설명하는 객체
  • 리덕스 앱이 시작될 때는 어떤 상태를 갖고 있는게 아니라서, 이 리듀서에 대한 기본 값으로 initialState를 제공한다.

action

  • action 객체는 항상 type 필드를 가져야 한다.
  • 동작의 고유한 이름으로 작용하는 문자열이다.
  • 동작에 대해 가독성 있게 짓는 것이 중요하다
    • 이 문제의 경우, counter/incremented 의 식으로 정의한다
  • 액션의 타입에 따라 새로운 상태 결과가 될 새로운 객체를 반환해야하거나, 아무것도 변경되지 않아야 하는 경우에 기존 상태를 반환해야 한다.
  • 주의할 점은, 기존 객체를 직접 수정하는 대신에 복사본을 업데이트하는 방향으로 작성하는 것이다.

 

// Create a new Redux store with the `createStore` function,
// and use the `counterReducer` for the update logic
const store = createStore(counterReducer);

 

store

  • 리듀서 함수가 준비됐으니까, redux library의 createStore API를 호출해서 스토어 인스턴스를 생성한다
  • createStore에 리듀서 함수를 전달한다
  • createStore는 이 리듀서 함수를 사용해서 초기 상태를 생성하고 추후 업데이트를 계산한다.
// Our "user interface" is some text in a single HTML element
const valueEl = document.getElementById("value");

// Whenever the store state changes, update the UI by
// reading the latest store state and showing new data
function render() {
  const state = store.getState();
  valueEl.innerHTML = state.value.toString();
}

 

이 예제에서는 UI로서 몇 가지 기본 HTML 요소만 사용한다.

그래서 redux 스토어에서 가장 최신의 상태를 가져오는 함수를 작성한다. => store.getState

그리고, 그 값으로 UI를 업데이트해서 값을 표시한다.

 

// Update the UI with the initial data
render();
// And subscribe to redraw whenever the data changes in the future
store.subscribe(render);
  • 또한 redux store는 store.subscribe를 호출하고 스토어가 업데이트될 때마다 호출될 subscribe 콜백 함수를 전달한다.
  • 그래서, 렌더 함수를 subscribe에 전달하여, 스토어가 업데이트 될때마다 최신 값으로 UI를 업데이트할 수 있게 한다.
// Handle user inputs by "dispatching" action objects,
// which should describe "what happened" in the app
document.getElementById("increment").addEventListener("click", function () {
  store.dispatch({ type: "counter/incremented" });
});

document.getElementById("decrement").addEventListener("click", function () {
  store.dispatch({ type: "counter/decremented" });
});

document.getElementById("incrementIfOdd").addEventListener("click", function () {
  // We can write logic to decide what to do based on the state
  if (store.getState().value % 2 !== 0) {
    store.dispatch({ type: "counter/incremented" });
  }
});

document.getElementById("incrementAsync").addEventListener("click", function () {
  // We can also write async logic that interacts with the store
  setTimeout(function () {
    store.dispatch({ type: "counter/incremented" });
  }, 1000);
});

 

dispatch

  • 사용자 입력에 응답하기 위해 무슨 일이 발생했는지 설명하는 액션 객체를 만들고, 이를 스토어에 dispatch 해주어야 한다.
  • store.dispatch(action) 을 호출하면 스토어가 리듀서를 실행하고, 업데이트된 상태를 계산하며 subscribe를 실행해서 UI를 업데이트 한다.
  • 특정 조건에 따라 action을 dispatch하는 코드를 작성하거나 (incrementIfOdd) 지연 후에 액션을 dispatch하는 비동기 코드를 작성할수도 있다 (incrementAsync).

data flow

redux app의 data flow

  • 클릭같은 유저 인터랙션에 대한 액션이 디스패치된다
  • 스토어가 리듀서 함수를 실행해서 새로운 상태를 계산한다
  • UI가 새로운 상태를 읽어서 새로운 값들을 표시한다.

summary

  • redux는 전역 상태를 관리하는 라이브러리다
  • 일반적으로 redux는 react와 같이 사용되는 react-redux 라이브러리와 함께 사용된다.
  • redux toolkit은 리덕스 로직을 작성하는 권장 방법이다
  • action은 type 필드를 갖는 객체로 무슨 일이 일어났는지 설명한다
  • reducer는 이전 상태와 액션을 기반으로 새로운 상태를 계산하는 함수다
  • store는 action이 dispatch될 때마다 root reducer를 실행한다.

root reducer?

  • 루트 리듀서는 Redux 애플리케이션에서 사용되는 모든 리듀서를 결합하는 역할을 합니다. Redux에서 여러 개의 리듀서를 사용하는 경우, 각 리듀서는 애플리케이션의 특정 부분에 대한 상태를 관리합니다. 그리고 이러한 개별 리듀서들을 하나로 합쳐 전체 애플리케이션의 상태를 처리하는 것이 루트 리듀서입니다.