티스토리 뷰

Why Redux?

  • 리덕스는 자바스크립트 앱을 위한 예측 가능한 상태 컨테이너이다.
  • 간단한 앱이라면 별도로 스테이트를 관리할 필요가 없을 수 있다. 하지만 컴포넌트가 많아지면 어떨까? A, B, C, D, E, F, G … 등 많은 컴포넌트가 있고, 이 중 G 컴포넌트에 있는 함수가 A 컴포넌트에 있는 상태 값에 영향을 준다고 하자. 컴포넌트가 어떻게 연결되어 있느냐에 따라 다르겠지만, 복잡한 경우 G 컴포넌트의 함수가 호출된 결과 값이 A에게 전달되기까지 많은 컴포넌트들을 불필요하게 거쳐야될 수 도 있다.그러나 리덕스가 있다면 앱이 지니고 있는 상태와, 상태 변화 로직이 들어있는 스토어를 통하여 컴포넌트 A에 직접 상태값과 함수를 주입해줄 수 있다.
  •  
  • 즉 상태와 직접 관련이 없는 컴포넌트들을 거치게 되면서 비효율성이 증가하고, 이 관계가 복잡해지면 프로그래머조차 언제 어디서 어떻게 상태가 변화하고 있는지 알 수 없게 된다.

Redux의 Core Concept(액션, 스테이트, 리듀서)

앱의 상태(state)가 아래와 같다고 가정해보자. 리덕스는 앱의 모든 상태를 하나의 store스토어 안에 하나의 객체 트리 구조로 저장한다.

객체에 todos, visibilityFilter - 두 개의 프로퍼티가 있고 todos는 오브젝트의 배열을 갖고 있다. 배열 원소인 오브젝트는 text(할일의 내용), completed(달성 여부)를 프로퍼티로 갖는다.

console.log(store.getState());

{
  todos: [{
    text: 'Eat food',
    completed: true
  }, {
    text: 'Exercise',
    completed: false
  }],
  visibilityFilter: 'SHOW_COMPLETED'
}

어플의 스테이트를 변경하고자 할 때에 리덕스는 action액션이라는 개념을 도입한다.

액션 예시는 아래와 같이 나타낼 수 있는데, 평범한 자바스크립트 객체이다. 액션은 타입 프로퍼티는 반드시 가져야 하고, 나머지는 자유롭게 설정할 수 있다.

{ type: 'ADD_TODO', text: 'Go to swimming pool' }
{ type: 'TOGGLE_TODO', index: 1 }
{ type: 'SET_VISIBILITY_FILTER', filter: 'SHOW_ALL' }

스테이트 변경 내용을 위와 같은 액션 객체로 나타내게 되면 그 변화가 어떤 내용인지 명확하게 이해할 수 있다. 예를 들어 액션 객체 중 type이 'ADD_TODO'이고 text가 'Go to swimming pool'이라면, 어플의 상태를 변경할 건데 그 변화는 할일을 추가하는 것이고 그 내용은 수영장에 가는 것이다, 라는 내용을 아주 명확하게 이해할 수 있는 것이다. 이러한 액션을 생성하는 액션 생성자(Action Creator)도 따로 작성된다. 단순히 데이터 파라미터를 받아서 액션 객체 형태로 반환하는 함수이다.

// Action Creator returns an action
function addTodo(data) {
  return {
    type: 'ADD_TODO',
    text: data
  }
}

스테이트와 액션을 하나로 묶기 위해서, reducer리듀서라는 함수를 작성한다. 이 또한 평범한 함수인데, 이 함수는 스테이트와 액션을 아규먼트로 받고 앱의 다음 상태를 반환한다. 앱이 커질수록 스테이트의 일부를 관리하는 작은 함수들을 여러 개 만든다.

// reducer about visibilityFilter property
function visibilityFilter(state = 'SHOW_ALL', action) {
  if(action.type === 'SET_VISIBILITY_FILTER') {
    return action.filter
  } else {
    return state
  }
}

// reducer about todos property 
function todos(state = [], action) {
  switch(action.type) {
    case 'ADD_TODO':
      return state.concat([{ text: action.text, completed: false }])
    case 'TOGGLE_TODO':
      return state.map(
        (todo, index) => 
        action.index === index 
        ? { text: todo.text, completed: !todo.completed }
        : todo
          )
    default: 
      return state
  }
}

마지막으로 대응하는 스테이트 키에 대해 두 리듀서를 호출하여 앱의 전체 스테이트를 관리할 또 다른 리듀서를 작성한다. 대응하는 프로퍼티의 스테이트만 아규먼트로 pass한 앞의 리듀서들과 달리 전체 앱 스테이트를 아규먼트로 받는다. 그러고는 대응하는 리듀서를 호출하여 앱의 전체 스테이트를 업데이트한다.

// Finally, last reducer! 
function todoApp(state = {}, action) {
  return {
    todos: todos(state.todos, action),
    visibilityFilter: visibilityFilter(state.visibilityFilter, action)
  }
}

 

지금까지 상태 변화를 묘사하는 액션, 이를 생성하는 액션생성자, 액션과 스테이트를 하나로 묶고 액션 타입에 따라 새로운 상태를 반환하는 리듀서를 보았다.

그래서 결국 스테이트 변화를 어떻게 일으킨다는 것인가? 스테이트 변화를 일으키는 유일한 방법은 액션을 실행(dispatch)하는 것이다.

모든 스테이트를 저장하는 스토어에는 dispatch라는 내장함수가 있다. 이 함수에 아규먼트로 액션을 pass하면 스토어가 리듀서를 호출하여 새로운 스테이트를 반환한다.

// Dispatch actions -> only way to change state
store.dispatch({
  type: 'TOGGLE_TODO',
  index: 1
})

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

이 것이 리덕스가 앱 스테이트를 관리하는 기본 아이디어이다. (위 설명에서 리덕스 API는 전혀 사용되지 않았다)

댓글
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2024/04   »
1 2 3 4 5 6
7 8 9 10 11 12 13
14 15 16 17 18 19 20
21 22 23 24 25 26 27
28 29 30
글 보관함