Redux | Redux Overview and Concepts

이진웅·2022년 4월 22일
0

Redux

목록 보기
1/3
post-thumbnail

공식문서를 공부하기 위해 번역한 내용이기 때문에 의역이나 오역이 존재할 수 있습니다.

리덕스는 무엇인가?

  • 리덕스는 액션이라는 이벤트를 사용해 앱의 상태를 관리 & 업데이트하는 라이브러리 혹은 패턴이다.
  • 스토어라는 곳을 통해 전체 어플리케이션의 상태를 관리할 수 있다.

왜 리덕스를 사용해야하는가?

  • 리덕스가 제공하는 패턴과 도구는 상태가 언제, 어디서, 왜, 어떻게 업데이트 되는지 이해하기 쉽게 만들어 준다.
  • 또한, 상태 값의 변화가 일어날때 어플리케이션 로직이 동작하는지 이해하기 쉽게 만들어준다.

언제 리덕스를 사용해야하는가?

  • 많은 양의 상태 값을 여러 컴포넌트에서 사용해야할 때
  • 상태 값이 빈번하게 업데이트 되야할 때
  • 상태 값을 업데이트 하는 로직이 복잡할 때
  • 파일의 규모가 크면서 다수의 인원이 협업해야 할 때

리덕스의 용어와 개념

상태 관리

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>
  )
}
  • 상태는 앱을 이끌어 나가는 요소
  • 뷰는 현재 상태를 보여주는 UI기반 선언적 묘사
  • 액션은 사용자 입력에 기반해 일어나는 이벤트, 상태를 업데이트 시키는 트리거

단방향 데이터 흐름 (one-way-data-flow)

  1. 상태는 흐름에 맞는 구체적인 상태를 묘사
  2. UI는 상태에 기반해 렌더링이 된다
  3. 상태값에 무언가 변화가 일어날 때, 상태 값에 어떤 변화가 일어남에 따라 값이 업데이트 된다
  4. UI는 새로운 상태 값에 기반해 새로 랜더링이 된다.

다수의 컴포넌트에서 같은 상태 값을 공유하거나 사용해야한다면 단방향 데이터 흐름을 지키지 못할 수 있다. 특히나 컴포넌트들이 다른 폴더들에 위치했을 때 더 일어나기 쉽다. 부모 컴포넌트로 상태를 끌어올려 이 문제를 해결할 수 있겠지만 항상 가능한 것은 아니다.

위 문제를 해결할 수 있는 방법은 컴포넌트들 속 공유된 상태 값을 뽑아내 컴포넌트 트리 구조 바깥에 마련된 장소에 모으는 것이다. 위 과정을 통해 컴포넌트 트리는 하나의 큰 뷰 단위가 되고 어떠한 컴포넌트들도 상태 값이나 트리거가 될 수 있는 액션에 접근할 수 있게된다.

위와 같은 일련의 과정을 통해 뷰와 상태를 분리시켜 독립적으로 유지할 수 있게된다. 이는 코드가 더욱 구조적이며 유지보수하기 쉽게 만들어 준다.

쉽게 말해 하나의 장소에서 상태를 관리하고, 상태를 액션에 따라 예측가능하게 업데이트하는 것이 리덕스의 기초 아이디어다.

불변성

자바스크립트의 객체나 배열들은 항상 가변성을 가지고 있다.

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'

위와 같이 간단하게 데이터의 내용을 수정할 수 있다.

불변성을 유지하기 위해선, 코드를 반드시 복사해 그 복사본을 가공해야한다.

리덕스는 모든 상태 값 업데이트가 불변성을 유지해야한다고 생각한다.

용어

Actions

  • type을 키 값으로 가진 순수한 자바스크립트 객체
    • 주로 domain/eventName으로 정의
  • 이벤트를 묘사한 단어를 사용해 정의한다
  • type외에도 payload라는 키 값이 존재한다
    • 무엇이 일어나는지 추가적인 정보를 기입한다
const addTodoAction = {
  type: 'todos/todoAdded',
  payload: 'Buy milk'
}

Action Creators

  • action객체를 생성하거나 반환하는 함수
  • 주로 이렇게 사용하기 때문에 매번 action객체를 수시로 작성할 필요가 없다
const addTodo = text => {
  return {
    type: 'todos/todoAdded',
    payload: text
  }
}

Reducers

  • 현재 stateaction을 인자로 받는 함수
  • 어떻게 state를 업데이트 할지 결정
  • 새로운 state를 반환
  • action 기준으로 작동하는 event listener로 이해하면 편하다
  • 아래의 규칙을 반드시 따라야한다
    • 새로운 state는 인자로 받는 stateaction을 기반으로 작동된다
    • 기존 state를 변경하는 것을 허용하지 않는다. 대신 state를 복사해 바꾸는 형식으로 변화를 일으킨다 (불변성 유지)
    • 비동기 로직을 사용하거나 랜덤한 값 사용, 사이드 이펙트를 발생시키지 않아야 한다
  • Reducer 함수의 로직
    • 해당 action을 감지할 것인지 체크한다
    • 만약 그렇다면 상태를 복사해 새로운 값으로 만들어 반환한다
    • 그렇지 않다면 바뀌지 않은 기존의 값을 반환한다
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의 state값이 모이는 곳
  • reducer를 통해 store가 생성되며, getState라는 메서드를 통해 현재 상태 값을 반환한다
import { configureStore } from '@reduxjs/toolkit'

const store = configureStore({ reducer: counterReducer })

console.log(store.getState())
// {value: 0}

Dispatch

  • Redux의 storedispatch라는 메서드를 가지고 있다.
  • 상태를 업데이트하는 유일한 방법은 store.dispatch를 통해 action 객체를 전달하는 것
  • 이를 통해 storereducer함수를 작동시켜 새로운 state를 만들어 getState를 통해 state를 업데이트시킨다
store.dispatch({ type: 'counter/increment' })

console.log(store.getState())
// {value: 1}
  • actiondispatch하는 것이란 이벤트를 트리거화 하는 것이라고 생각하면 좋다.
    1. 무언가 변화가 일어났을때 store에게 알린 뒤
    2. reducerevent listener처럼 작동하고 action을 캐치한 뒤
    3. 변한 state 값을 업데이트 한다
  • 통상적으로 올바른 actiondispatch하기 위해 action creator를 사용한다.
const increment = () => {
  return {
    type: 'counter/increment'
  }
}

store.dispatch(increment())

console.log(store.getState())
// {value: 2}

Selectors

  • store에서 알맞은 정보를 가져오기 위한 함수
  • 프로젝트 규모가 커질수록 로직의 반복을 방지할 수 있다
const selectCounterValue = state => state.value

const currentValue = selectCounterValue(store.getState())
console.log(currentValue)
// 2

Redux 데이터 흐름

  • Initial setup
    • storeroot reducer함수에 의해 생성된다
    • storeroot reducer에 의해 한 번 호출되고, 그 값을 초기 state로 저장해 값을 반환한다
    • UI가 처음 렌더링 될때 UI 컴포넌트들은 store에 저장된 현재 state에 접근해 렌더링 될 때 사용된다
    • 또한 state 값이 바뀔 때 감지할 수 있는 상태가 된다
  • Updates
    • state 값의 변화가 발생한다
    • dispatch({type: 'counter/increment'})처럼 storeaction에게 dispatch가 된다
    • storeprevious statecurrent action을 인자로 받는 reducer함수를 실행시켜 new state를 반환한다
    • store은 관련된 UI들에게 값이 업데이트 됐다고 알린다
    • 각각의 UI들은 state값이 바뀌었는지 체크한다
    • 값이 바뀌었다면 새로운 state값으로 재렌더링되어 새로운 값이 스크린에 노출되게 된다.

참조

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

0개의 댓글