Dev/SW Engineering

25. Redux - Redux의 필요성과 써야 할 때를 구분할 줄 알기

HJChung 2020. 7. 25. 20:15

+ Redux에 대해 공부한 것을 정리한 것입니다. 배우는 중이라 잘못된  내용이 있을 수 있으며 계속해서 보완해 나갈 것입니다. :))

Intro & Redux란

앞서서 22-24번 포스팅을 통해 React를 배웠고, React Sprint -유튜브 클론 코딩을 진행했다.

아래는 미로 에서 제가 짠 유튜브 클론 코딩의 컴포넌트 구성을 나타낸 것이다. (컴포넌트 시각화를 트리 구조로 짜는 것이 편하다는 것을 몰랐다는 것을 감안하더라도 너무 복잡..)

 

실제로 유튜브 클론 코딩을 하면서 가장 많은 시간을 쓰고 고민하고 힘들었던 점이 'state 끌어올리기'였다. 

한 예를 들면, App에 있는 state인 currentVideo와 함수인 handleVideoListEntryTitleClick을 props형태로 -> VideoList 컴포에 전달 -> props 형태로 VideoListEntry에 전달 => 그러면 VideoListEntry의 제목이 onClick되면 -> VideoList->App으로 거슬러 올라가 App에서 handleVideoListEntryTitleClick이 실행되고 setState로 currentVideo의 상태가 변경되면->재 render되는 것이다.

 

이렇게 하위 component에서 state를 변경하기 위해서 불필요하게 props를 통한 전달이 이루어지는 것을 Props drilling 문제라고 한다.

 

그러면 어떻게 이 Props drilling 문제를 해결할 수 있을까? 컴포넌트가 접근 할 수 있는 공용스토어(Store)가 있다면? 그럼 하위 컴포넌트든 상위 컴포넌트든 상관없이 각 컴포넌트가 자신이 필요할 때 스토어에 접근해서 가져오면 되지 않을까?

그리고 state 변경이 이루어질 수 있게 하는 것(Action) 과 state 변경을 해주는 액션을 스토어까지 가져와주는 스토어 관리자(Dispatcher)가 스토어의 상태 변경을 관리하면 불필요한 혼란이 줄지 않을까?

이러한 역할을 해주는 것이 Redux이다. 

 

이렇게 컴포넌트와 store가 독립되어서 

1. state 수정을 위해선 dispatcher나 reducer을 통해서만 가능하고, 절대 직접 state를 변경시키지 못하도록 한다. 

2. 역시 state 데이터를 가져갈 때도 getState를 통해서만 가져가 수 있게 철저히 접근을 제한한다. 

이렇게 데이터를 외부에서 직접 제어하는 것을 제한합으로써 의도치않게 state값이 바뀌는 문제를 사전에 차단하고, 이게 곧 예측 가능성으로 이어진다. 

3. 그리고 state가 변경되면 그에 영향을 받는 컴포넌트에 변경사항을 알려줌으로써 각 컴포넌트는 다른 컴포넌트에서 독립되어 딱 자신에게 주어진 일만 하면 된다는 편리함이 있다. 

이런 특성 때문에 'Redux 는 a predictable state container for Javascript applications' 이다. 

 

Redux - A predictable state container for JavaScript apps. | Redux

A predictable state container for JavaScript apps.

redux.js.org

Redux의 필요성

위에서 살펴보긴 했지만 다시한번 React에서 Redux없이 state를 어떻게 관리하고 변경했는지를 살펴보면,

형제 컴포넌트간에 데이터를 주고 받으려면 부모 컴포넌트를 거쳐서 주고받아야했다. 이렇게 공통 부모가 바로 위에 있다면 그렇게 할 수도 있겠지만 만약 공통의 조상인 상위 부모가 너무 위에 있다면? 아니면 친척 컴포넌트에 데이터를 보내야 한다면? 

너무 복잡해진다. 

즉, React 만으로는 항상 변하는 state를 관리하기란 어렵다. 즉, state를 언제, 왜, 어떻게 업데이트 할지는 제어하기가 매우 힘들어지며 이러한 복잡함은 변화(mutation)과 비동기(asyncronicity)같이 어려운 개념을 섞어서 사용함에서 비롯된다. 

그러나 Redux는 state 변화가 일어나는 시점에 제약을 두어 상태변화를 예측 가능하게 하며,  위의 복잡한 문제를 해결할 수 있고 이것이 Redux의 필요성과 직결된다. 

 

그러나 Redux가 모든 React 앱에 필요한 것은 아니다. React만으로도 충분한 것을 괜히 Redux를 사용하여 관리해야하는 파일이 늘어나고.. 할 수 있기 때문이다. 

저는 아직 잘 결정할 수 있을지 자신이 없지만 아래의 글들을 참고하고 여러 과제들을 해나가면서 insight를 얻는 것의 필요성을 느꼈다. 

 

어쩌면 당신에게 Redux가 필요하지 않을 수도 있다

 

orezytivarg.github.io

 

Redux가 필요하다는 것을 언제 알 수 있나요?

이 글은 Simon Schwartz의 "When do I know I’m ready for Redux?"를 번역한 글입니다.

medium.com

Redux의 3가지 원칙

위에서 Redux는 state 변화가 일어나는 시점에 제약을 두어 상태변화를 예측 가능하게 한다고 하였다. 여기서 '제약'이라는건 Redux 공식문서에도 나와있는 Redux의 '3가지 원칙'을 말한다.

 

3가지 원칙 | Redux

소개 > 3가지 원칙: Redux 사용의 3가지 중요 원칙

ko.redux.js.org

1. Single source of truth 

: 나의 애플리케이션의 모든 state는 하나의 store에 하나의 객체 트리 구조로 저장된다. 

store의 내용을 getState()로 확인해보면,  아래와 같이 하나의 객체 트리 구조로 되어있는 것을 알 수 있다. 

하나의 객체 트리 구조의 장점은 공식문서에 따르면 다음과 같다. 


"이를 통해 범용적인 애플리케이션(universal application, 하나의 코드 베이스로 다양한 환경에서 실행 가능한 코드)을 만들기 쉽게 만들 수 있습니다. 서버로부터 가져온 상태는 시리얼라이즈되거나(serialized) 수화되어(hydrated) 전달되며 클라이언트에서 추가적인 코딩 없이도 사용할 수 있습니다. 또한 하나의 상태 트리만을 가지고 있기 때문에 디버깅에도 용이할 것입니다. 빠른 개발 사이클을 위해 개발중인 애플리케이션의 상태를 저장해놓을 수도 있습니다. 하나의 상태 트리만을 가지고 있기 때문에 이전에는 굉장히 구현하기 어려웠던 기능인 실행취소/다시실행(undo/redo)을 손쉽게 구현할 수 있습니다." 
console.log(store.getState());

{
  visibilityFilter: 'SHOW_ALL',
  todos: [{
    text: 'Consider using Redux',
    completed: true,
  }, {
    text: 'Keep all state in a single tree',
    completed: false
  }]
}

2. State is Read Only

:state는 읽기 전용이다. 그래서 state를 변화시키는 유일한 방법은 무슨 일이 일어날지를 묘사하는 action객체를 전달하는 것 뿐이다. 

//객체 형태인 action이 state를 이렇게 변화시키란는 정보를 가지고 있으며 이것이 전달된다. 
store.dispatch({
  type: 'COMPLETE_TODO',
  index: 1
})

store.dispatch({
  type: 'SET_VISIBILITY_FILTER',
  filter: 'SHOW_COMPLETED'
})

3. Changes are mode with pure functioning

: Action을 보내면 Reducer라는 순수함수를 통해 다음 state의 반환이 되고,  만들어낸 변화의 결과로 다음 state가 생성된다. 중요한건 이전 상태를 변경하는게 아니라 새로운 상태 객체를 생성해서 반환해야한다는 점이다. 

function visibilityFilter(state = 'SHOW_ALL', action) {
  switch (action.type) {
    case 'SET_VISIBILITY_FILTER':
      return action.filter
    default:
      return state
  }
}

function todos(state = [], action) {
  switch (action.type) {
    case 'ADD_TODO':
      return [
        ...state,
        {
          text: action.text,
          completed: false
        }
      ]
    case 'COMPLETE_TODO':
      return [
        ...state.slice(0, action.index),
        Object.assign({}, state[action.index], {
          completed: true
        }),
        ...state.slice(action.index + 1)
      ]
    default:
      return state
  }
}

import { combineReducers, createStore } from 'redux'
let reducer = combineReducers({ visibilityFilter, todos })
let store = createStore(reducer)

Redux의 장점

Redux란 무엇이고 어떤 경우에 사용하는 것이 적절한지 살펴보았다. 그래서 Redux의 장점은!

1. state를 예측 가능하게 만들어준다. 

2. 유지보수

3. 디버깅에 유리하다. 

4. 테스트를 붙이기 쉽다. 

 

라고 한다.

 

 

+ Redux에 대해 공부한 것을 정리한 것입니다. 배우는 중이라 잘못된  내용이 있을 수 있으며 계속해서 보완해 나갈 것입니다. :))