리덕스는 왜 생겼을까?

Jinux·2022년 9월 16일
0

리덕스

목록 보기
1/1
post-thumbnail

리덕스를 학습한 과정을 기록했습니다.

리덕스는 왜 생겼을까?

소프트웨어를 설계하면서 자주 발생하는 문제에 대한 모범답안을 디자인 패턴이라고 합니다. 시간이 흐름에 따라 애플리케이션의 규모는 점점 다양해지고 이를 해결하기 위한 여러 디자인 패턴이 나오게 되었습니다. 그 중 모든 복합 패턴의 근간이라고 부를 수 있는 패턴이 있습니다.

MVC 패턴


모든 복합 패턴의 근간이라 불리는 MVC 패턴은 크게 세가지 구성요소로 나눠 설계를 합니다.

  1. Model: 데이터의 형태, 데이터를 수정하는 역할
  2. Controller: 유저의 입력을 받아서 애플리케이션 내에서 어떻게 처리할 지 판단 및 가공해서 모델 또는 뷰를 조작하는 역할
  3. View: 모델을 UI로 표현, 사용자의 입력을 받아서 Controller에 전달하는 역할

MVC 패턴에서 각 구성요소들은 서로에게 접근할 수 있는 양방향 통신을 할 수 있습니다. 하지만 애플리케이션의 규모가 커지고, 세분화되면서 어려움이 생기게 되었습니다.

당시 React를 만든 페이스북에서도 이러한 어려움을 겪습니다. DM 기능은 처음에는 단순하게 시작했지만 점점 기능이 늘어가며 안 읽은 메시지가 존재한다는 표시가 있지만 실제 채팅에 들어가면 없는 버그가 생기게 됩니다.

개발자들은 이러한 버그를 인식하고 해결했지만, 동일한 버그가 계속해서 발생하게 되는데, 이 버그의 근본적인 원인이 MVC 패턴의 양방향성이라고 정의하게 됩니다.

MVC 패턴의 양방향 통신은 점점 규모가 커짐에 따라 Model과 View의 수정사항이 생기면 연쇄적으로 모든 Model과 View가 변화가 발생하게되고 이는 예측할 수 없는 문제가 발생하게 됐던 것 입니다.

Flux

MVC 패턴의 문제를 해결하기 위해 페이스북 개발자들은 새로운 디자인 패턴을 발표했습니다. 바로 Flux 패턴입니다.
Flux 패턴의 핵심은 단방향으로 설계하여 양방향으로 생긴 예측불가능한 문제를 해결하고자 했습니다. 최대한 단순화하고 예측가능하게 하는데 목표를 둔 것이죠.

Flux 패턴은 4가지 구성요소들로 이루어져있습니다. 또, 각 구성요소들은 한가지 방향으로만 상호작용합니다.

각 구성요소들을 알아보겠습니다.

  • Action: 어떤 변화를 발생시킬지 정의하는 type 프로퍼티와 변화에 필요한 데이터를 담고있는 객체
  • Dispatcher: Action을 받아서 모든 Store에 전달하는 역할
  • Store: 데이터를 저장하고, Dispatch에 전달된 Action에 따라 수행
  • View: Store에 저장된 데이터를 받아서 UI로 표현, 유저의 동작에 따라 Action을 생성

아래와 같이 각 구성요소들은 한가지 방향으로만 상호작용하여 개발자들은 변화를 파악하기 쉬워졌으며, 동작의 예측도 쉬워졌습니다.

Redux의 등장

Flux는 라이브러리, 프레임워크가 아닌 디자인 패턴입니다. Flux가 발표된 이후 많은 라이브러리들이 나왔지만 현재 Flux 패턴의 표준이 되는 라이브러리는 Redux로 정립되어있습니다.
Redux는 Flux, CQRS, Event Sourcing의 개념을 사용해서 만든 라이브러리로 JavaScrip 앱을 위한 예측 가능한 상태 컨테이너를 핵심 가치로 삼고있습니다.
Redux는 모든 상태를 관리하는 컨테이너로 역할을 수행하고, Flux 패턴의 단방향성을 차용했기에 상태 변화를 모두 예측 가능합니다.

키워드

Redux를 사용하기 앞서 접하게 될 키워드들을 미리 알아보겠습니다.

액션(Action)

상태에 어떠한 변화가 필요할 때 액션을 발생시킵니다. 이는 하나의 객체로, type필드를 필수로 가지고 그 외의 값을 선택적으로 갖게됩니다.

{
  type: "ADD_TODO",
  data: {
    id: 0,
    text: "리덕스 배우기"
  }
}

액션 생성함수(Action Creator)

말 그대로 액션을 만드는 함수로, 파라미터를 받아와 액션 객체 형태로 만듭니다.

export function addTodo(data) {
  return {
    type: "ADD_TODO",
    data
  };
}

// 화살표 함수로도 만들 수 있습니다.
export const changeInput = text => ({ 
  type: "CHANGE_INPUT",
  text
});

이렇게 생성해서 사용하는 이유는 추후에 컴포넌트에서 액션을 더 쉽게 발생시키기 위함입니다. (필수적이진 않습니다.)

리듀서(reducer)

변화를 일으키는 함수입니다. 두 가지 파라미터를 가집니다.

function reducer(state, action){
  switch (action.type) {
    case 'INCREASE':
      return state + 1;
    case 'DECREASE':
      return state - 1;
    default:
      return state;
  }
}

useReducer에선 default에 일반적으로 throw를 하는 반면 리덕스의 리듀서는 기존 state를 그대로 반환합니다.
리덕스에서 리듀서는 여러 개를 만들어 이를 합쳐 루트 리듀서로 만들 수 있습니다.

스토어(store)

리덕스에서는 한 애플리케이션당 하나의 스토어를 만듭니다. 스토어 안에는 앱 상태와 리듀서, 몇가지 내장함수들이 있습니다.

디스패치(dispatch)

스토어의 내장함수 중 하나로 액션을 발생시키는 것 입니다. dispatch라는 함수에 액션을 파라미터로 전달합니다. 그렇게 호출하면 스토어는 리듀서 함수를 실행시켜 해당 액션을 처리하는 로직이 있다면 액션을 참고하여 새로운 상태로 만들게 됩니다.

구독(subscribe)

스토어의 내장함수 중 하나입니다. 함수 형태의 값을 파라미터로 받아와 액션이 디스패치 되었을 때마다 그 함수가 호출됩니다.
리액트에선 리덕스를 사용하게 될 때 이 함수를 사용하는 일은 별로 없습니다. react-redux 라이브러리에서 제공하는 connect 함수 또는 useSelector Hook을 사용하여 리덕스 스토어의 상태에 구독합니다.

0개의 댓글