programming language/react

[React] Redux 개념 좀 더 알기

공대키메라 2022. 6. 21. 22:51

저번 시간에 Redux에 대해서 간단히 사용하는 방법을 공부해보았다.

(궁금하면 여기 클릭!)

 

Redux를 저번에 간단하게만 알아본 것 같아서 이번 시간에는 개념에 대해서 알아볼 것이다. 

 

내가 정리하는 내용은 전부 공식 사이트에 있는 것이다. 이것을 필자는 해석하고 설명한 것 밖에 없다!

 

사실 이러한 행위가 내 학습을 위한 것이기도 하다. 

 

궁금한 분은 출처 사이트에서 모든 내용을 더 자세히 확인할 수 있다. 

 

출처 : 

https://ko.redux.js.org/tutorials/essentials/part-1-overview-concepts

 

1. 리덕스 용어와 개념

1.1상태 관리(State management)

Redux의 용어와 개념에 대해 알기 전에 공식 사이트에서는 다음 Component를 보여주는것으로 시작한다. 

 

function Counter() {
  // State: a counter value
  const [counter, setCounter] = useState(0)

  // Action: code that causes an update to the state when something happens
  const increment = () => {
    setCounter(prevCounter => prevCounter + 1)
  }

  // View: the UI definition
  return (
    <div>
      Value: {counter} <button onClick={increment}>Increment</button>
    </div>
  )
}

 

여기서 State, Action, View가 있다. 

 

  • State(상태) : 우리의 Application을 운영하는 자료의 출처
  • View(뷰) : 현재 상태에 기반한 선언적인 UI의 묘사
  • Action(액션) : 사용자의 입력에 기반해서 Appication에서 발생하는, 그리고 상태에서 update를 일으키는 이벤트

이것은 한 방향 데이터 흐름(one-way data flow)의 작은 예시이다. 

 

  • State(상태)는 그 특정 시간에 앱의 상태를 묘사한다. 
  • UI는 그 상태에 근거해서 화면에 표출된다(Rendered)
  • 버튼 클릭 같은 것이 발생할 때, 상태는 발생한 것에 기반해서 update된다.
  • UI는 새로운 상태에 기반해서 다시 그려진다(re-rendered) 

 

이것을 다시 한번 생각해보면 너무 당연하다.

 

위의 example component에서 state에 따라서 rendering되는 화면이 달라지는 것! 여태 React를 했다면 뭐 너무 쉽게 알 수 있다. 

 

https://ko.redux.js.org/tutorials/essentials/part-1-overview-concepts

 

하지만 이러한 흐름은 같은 State(상태)를 사용하고 공유해야하는 많은 Component가 있을 때, 특히 그 Component들이 application의 다양한 부분에 위치해 있다면 쉽게 깨진다.

 

이게 무슨말인가?

 

React에서 데이터의 흐름은 한방향으로 흐른다. 이것을 one-way data flow(한방향 데이터 흐름)라고 표현하는데 

 

한방향으로만 데이터가 흐르다보니 생기는 문제점은, 각각의 데이터를 여러곳에서 공유할 때 문제라는 것이다.

 

예를 들어서 B,C,D에서 동일하게 사용하고 싶은 데이터가 있다고 하자. 

 

그런 경우 A를 만들어서 B,C,D에 전달해주면 된다.

(one-way data flow! got it?)

 

이 방법을 일반화해서 설명한다고 하면, 이 문제를 해결할 방법은 Components들에서 공유되는 state를 뽑아내고 중앙에 위치한 바깥 component tree에 데이터를 저장하는 것이다.

 

이렇게, 우리 component tree는 큰 view가 되고, 다른 component는 tree의 어디에 위치하던지 state에 접근해서 action을 발생시킬 수  있다!

 

상태 관리(state management)에 포함된 개념을 정의하고 분리하고 view와 state사이의 독립성을 유지하는 규칙을 지키면,

우리는 코드에 더 구조성과 유지성을 줄 수 있다. 

 

이것이 Redux의 기본 개념이다. 

 

하나의 집중된 곳에서 관리하는 데이터를 모든 곳에서 사용 가능한 것이다. 

1.2 불변성(Immutability)

변경성(Mutability)는 바뀔 수 있음(changeable)을 의미한다. 어떤 것이 "Immutable" 하다는 것은 바뀔 수 없다는 것을 의미한다. 

 

Javascript 객체(Object)와 배열(Array)는 모두 기본적으로 mutable하다. 객체를 생성하면, 필드들의 내용도 바꿀 수 있다.

배열을 생성나면, 또한 그 내용을 변경할 수 있다.

 

const obj = { a: 1, b: 2 }
// still the same object outside, but the contents have changed
obj.b = 3

const arr = ['a', 'b']
// In the same way, we can change the contents of this array
arr.push('c')
arr[1] = 'd'

 

이것을 mutating object or array(객체 혹은 배열 변화시키기)라고 부른다. 메모리상으로는 같은 object, array reference이지만 객체 내부의 content는 변화한 상태이다. 

 

불변적으로(immutably) 값을 update하기 위해서는 코드가 존재하는 객체들/배열들의 복사본을 만들어서 그 복사본을 수정해야한다. 

 

여기서 redux의 중요한 핵심이 나오는데 불변성을 유지하기 위해 객체, 배열을 복사해 그 복사본을 수정해야 한다는 것이다. 

 

다음과 같이 말이다.

 

const obj = {
  a: {
    // To safely update obj.a.c, we have to copy each piece
    c: 3
  },
  b: 2
}

const obj2 = {
  // copy obj
  ...obj,
  // overwrite a
  a: {
    // copy obj.a
    ...obj.a,
    // overwrite c
    c: 42
  }
}

const arr = ['a', 'b']
// Create a new copy of arr, with "c" appended to the end
const arr2 = arr.concat('c')

// or, we can make a copy of the original array:
const arr3 = arr.slice()
// and mutate the copy:
arr3.push('c')

 

Redux는 모든 상태의 update들이 불변적으로 행해진것으로 기대한다. 

1.3 용어(Termonology)

Action(액션)

 

action은 type 필드를 가진 평범한 javascript 객체이다. 액션을 어플리케이션에서 발생한 어떤 것을 묘사하는 이벤트라고 생각해도 된다. 

 

type 필드는 이 action을 묘사하는 이름으로 string 형식이어야 한다. 

 

전형적인 action 객체는 다음과 같다. 

 

const addTodoAction = {
  type: 'todos/todoAdded',
  payload: 'Buy milk'
}

 

 하지만 이렇게 일일이 선언하지 않고 대부분 action creator를 사용한다. 

 

const addTodo = text => {
  return {
    type: 'todos/todoAdded',
    payload: text
  }
}

 

Reducer(리듀서)

 

리듀서는 현재 state(상태)와 action(액션) 객체를 받은 함수이다. 

 

필요하면 state를 update하는 방법을 결정한다. 

 

받은 action type에 따라서 이벤트를 관리하는 event listener라고 생각할 수 있다. 

 

reducer는 다음의 규칙을 지켜야 한다. 

 

  • 오직 state와 action argument를 따라서만 새로운 상태 값을 계산해야 한다. 
  • 존재하는 state를 수정할 수는 없다. 대신에, 존재하는 state를 복사하고 복사된 값을 변경해서 만든 불변의 update를 만들어야한다. 

 

const initialState = { value: 0 }

function counterReducer(state = initialState, action) {
  // Check to see if the reducer cares about this action
  if (action.type === 'counter/increment') {
    // If so, make a copy of `state`
    return {
      ...state,
      // and update the copy with the new value
      value: state.value + 1
    }
  }
  // otherwise return the existing state unchanged
  return state
}

 

store(스토어)

 

현재 Redux application state는 store라고 불리는 객체에 존재(live)한다. 

=> live를 해석하면 살아있다고 읽을 수 있는데 이게 우리가 state라는 것을 관리하고 수정하는 과정에서 이것이 현재 상황에 맞게 생생하게 존재하기에 live라고 표현한 것 같다. 

 

store는 reducer에서 넘기는 것으로 생성되고, getState라는 method를 가진다. 그리고 현재의 state value를 반환한다. 

 

Dispatch(디스패치)

 

Redux store는 dispatch라는 method를 가진다. state를 update하는 유일한 방법은 store.dispatch()를 불러고 action 객체를 넘기는 것이다. 

 

store는 reducer 함수를 작동시키고, new state값을 내부에 저장하고, 업데이트된 값을 받기 위해 getState()를 호출할 수 있다. 

 

이 dispatching action을 event trigger라고 생각할 수 있다. 

 

Selector(선택자)

 

Selector는 store state value로 특정 정보의 조각을 추출하기 위한 방법을 아는 함수이다. 

 

appllication이 커짐에 따라, 이 함수가 같은 로직을 같은 데이터를 읽기 필요로하는 app의 다른 부분에서 반복됨을 막는데 일조한다. 

 

https://ko.redux.js.org/tutorials/essentials/part-1-overview-concepts


이렇게 오늘은 Redux의 세부적인 개념에 대해 알아보았다. 

 

다음 시간에는 Redux-thunk와 Redux-saga를 사용하는 방법에 대해 알아볼 것이다.