Redux with Redux toolkit

홍준섭·2023년 12월 8일

react

목록 보기
29/29

what is Redux

Redux는 앱의 상태를 actions이라는 이벤트를 통해 관리하고 업데이트하는 패턴이자 라이브러리다.

why should i use Redux?

리덕스로 만들어진 패턴과 도구들은 변화가 발생했을 때 언제, 어디서, 왜, 어떻게 앱의 상태가 업데이트 되는지 로직이 어떻게 행동할지 이해하기 더 쉽게 만든다.

when should i use Redux?

  • 앱의 여러 위치에 필요한 많은 양의 상태가 있는 경우
  • 앱의 상태가 자주 업데이트 되는 경우
  • 업데이트 로직이 복잡한 경우
  • 앱에 중간 또는 대규모 코드베이스가 있으며 많은 사람들이 작업해야 하는 경우

Redux Terms and Concepts

state management

일방향 데이터 흐름

상태는 특정 시점의 앱 상태를 설명한다. ui는 해당 상태를 기준으로 렌더링 된다. 사용자가 버튼을 클릭하는 것과 같은 일이 발생하면 발생한 일에 따라 상태가 업데이트된다. ui가 새 상태를 기준으로 다시 렌더링된다.

같은 상태를 공유하고 사용해야 하는 여러 컴포넌트가 있는 경우

단순성이 깨질 수 있다. 그렇기에 컴포넌트에서 공유 상태를 추출하고, 컴포넌트 트리 외부의 중앙 집중식 위치에 넣어 해결한다. 이를 통해 우리의 컴포넌트 트리는 큰 "뷰"가 되고, 어떤 컴포넌트든 트리의 위치에 상관없이 상태에 접근하거나 동작을 트리거 할 수 있다. 뷰와 상태의 독립성을 유지하므로써 코드에 더 많은 구조와 유지 가능성을 부여한다.

Immutability

immutable은 바꿀 수 없음을 의미한다. 불변성을 유지하기 위해 기존 개체/배열의 복사본을 만든 다음 복사본을 수정한다.

Terminology

Actions

action은 type field를 가진 일반적인 자바스크립트 객체이다. 액션은 앱에서 일어난 일을 설명하는 이벤트라고 생각할 수 있다. type field는 "todos/todoAdded"와 같이 이작업을 설명하는 이름을 부여하는 문자열이어야 한다. 일반적으로 "domain/eventName"과 같은 유형의 문자열을 작성한다. 첫 번째 부분은 이 작업이 속한 기능 또는 범주이고 두 번째 부분은 발생한 특정 작업이다.

Action Creator

액션 생성자는 액션 오브젝트를 생성하고 반환하는 함수이다.

reducer

reducer는 현재 상태와 액션 오브젝트를 받아 필요에 따라 상태를 업데이트할 방법을 결정하고 새로운 상태를 반환하는 함수이다. reducer는 수신한 액션(이벤트) 유형에 따라 이벤트를 처리하는 이벤트 리스너라고 생각할 수 있다.
reducer는 몇가지 조건을 지켜야 한다.

  • 새로운 상태를 이전의 상태와 action 객체를 이용해서만 계산해야 한다.
  • immutable updates를 해야한다.
  • side effect를 만드는 로직이 있으면 안된다.

store

현재 Redux앱의 상태는 store라는 객체에 존재. store는 reducer를 통과하여 생성되며, 현재 상태 값을 반환하는 getState라는 메서드가 있다.

Dispatch

Redux store는 dispatch라는 함수를 가진다. state를 업데이트하는 유일한 방법은 store.dispatch()를 호출하고 액션 객체를 전달하는 것이다.

selectors

selectors는 store 상태 값에서 특정 정보를 추출하는 함수이다.

Redux Application Data Flow

initial setup

  • Redux store가 reducer function을 사용하여 생성됨
  • store는 root reducer를 한 번 호출하고, 반환 값을 초기상태로 저장
  • UI가 처음 렌더링 되면 Redux store의 현재 상태에 액세스 하고 해당 데이터를 사용하여 렌더링할 항목을 결정. 또한 상태가 변경되었는지 알 수 있도록 구독을 한다

update

  • event 발생
  • 앱의 코드는 Redux store에 액션을 dispatch한다
  • store reducer function에 이전 상태와 액션을 이용하여 새로운 상태를 만든다.
  • store는 구독중인 UI의 모든 부분에 store가 업데이트 됬음을 알려준다
  • store의 데이터가 필요한 각 UI의 컴포넌트는 필요한 state부분이 변경되었는지 확인한다.
  • 데이터가 변경된 각 컴포넌트는 새 데이터로 강제로 다시 렌더링되므로 화면에 표시되는 내용을 업데이트 할 수 있다.

Redux App Structure

Creating the Redux Store

Redux Store는 configureStore를 이용해 만들어진다. configureStore에는 reducer인수를 전달해야 한다

import { configureStore } from '@reduxjs/toolkit'
import counterReducer from '../features/counter/counterSlice'

export default configureStore({
  reducer: {
    counter: counterReducer
  }
})

우리의 앱은 다양한 기능으로 구성될 수 있다.
그리고 각각의 기능은 자신의 reducer function을 가지고 있다. 우리가 configureStore를 호출할 때 객체의 다양한 리듀서를 모두 전달할 수 있다. 객체의 키 이름은 최종 상태 값의 키를 결정한다.

Redux Slices

slice는 Redux reducer logic과 액션들의 모음이다.

Creating Slice Reducers and Actions

import { createSlice } from '@reduxjs/toolkit'

export const counterSlice = createSlice({
  name: 'counter',
  initialState: {
    value: 0
  },
  reducers: {
    increment: state => {
      // Redux Toolkit allows us to write "mutating" logic in reducers. It
      // doesn't actually mutate the state because it uses the immer library,
      // which detects changes to a "draft state" and produces a brand new
      // immutable state based off those changes
      state.value += 1
    },
    decrement: state => {
      state.value -= 1
    },
    incrementByAmount: (state, action) => {
      state.value += action.payload
    }
  }
})

export const { increment, decrement, incrementByAmount } = counterSlice.actions

export default counterSlice.reducer

Rules of Reducers

  • state와 액션객체를 기반으로 새로운 상태를 계산한다.
  • immutable updates를 해야한다.
  • 비동기 논리나 sideEffect를 일으키는 논리는 금지.

Why are those rules important?

  • 코드를 예상 가능하게 만들기 위해
  • 만약 함수가 다른 값을 수정하는 경우 앱이 예기치 않게 작동할 수 있다. 이것은 state는 변경했지만 UI가 변경 안 되는 상황을 만들 수 있다.

Why you must not mutate state in Redux

  • 최신값을 보여주는 UI가 제대로 업데이트 안 될수 있다.
  • state가 업데이트 된 이유와 방법을 이해하기 어려워진다.
  • 테스트 작성이 어려워진다.
  • time travle debugging 기능을 쓰는걸 어렵게한다.
  • Redux의 사용패턴에 어긋난다.

Writing Async Logic with Thunks

  • Thunk는 비동기 논리를 포함할 수 있는 특별한 Redux함수이다. Thunk는 두가지 함수를 사용하여 작성된다.
    - dispatch및 getState를 인수로 가져오는 내부 thunk 함수
    - Thunk함수를 생성하고 반환하는 외부 생성자 함수
    Thunk를 사용하려면 redux-thunk 미들웨어가 생성될 때 Redux 저장소에 추가되어야 한다. Redux toolkit의 configureStore 기능은 이미 자동으로 설정되어 있으므로 여기에서 thunk를 사용할 수 있다.

Reading data with useSelector

  • useSelector 훅을 사용하면 컴포넌트가 Redux store state에서 필요한 데이터 조각을 추출할 수 있다
export const selectCount = state => state.counter.value
const count = useSelector(selectCount)

컴포넌트는 Redux store와 직접 통신할 수 없다. 왜냐하면 Redux Store를 컴포넌트 파일에 import 할 수 없기 때문에. 그 대신 useSelector는 우리를 위해 뒤에서 Redux store와 통신을 처리한다. selector함수를 전달하면 someSelector(store.getState())가 호출되고 결과가 반환된다.

작업이 전달되고 Redux store가 업데이트 될 때마다 useSelector는 선택기 기능을 다시 실행한다. 선택기가 지난번과 다른 값을 반환하면 useSelector는 컴포넌트가 새 값으로 다시 렌더링 되는지 확인한다.

profile
개발 공부중입니다

0개의 댓글