Redux

호두파파·2021년 4월 27일
0

Redux

목록 보기
1/6

React에서는 상태의 속성(props)을 이용한 컴포넌트 단위 개발 아키텍처를 배웠다.
리덕스에서는 컴포넌트와 상태를 분리하는 패턴에 대해서 익숙해져야 한다.

리듀서 들여다보기

리덕스의 가장 눈에 띄는 특징은 '단일 스토어, 다수의 리듀서'정책이다. 시스템의 도메인 레이어라 할 수 있는 스토어에서 모든(대부분) 상태를 관리하는 플럭스 아키텍쳐 위에서는 , 커지는 코드 베이스에 맞춰 스토어 역시 쉽게 비대해진다. 이런 상황이 오면 하나였던 스토어를 다수의 스토어로 분리한다.

리덕스는 단일 스토어를 유지하되, 상태 처리에 대한 책임을 스토어의 하위에 있는 리듀서에게 넘기는 '단일 스토어, 다수의 리듀서' 전략을 택했다. 리덕스에서 스토어는 컴포넌트와 리듀서를 연결하는 아주 얇은 레이어일 뿐이다. 상태와 상태를 처리하는 행위는 스토어가 아닌 리듀서가 책임진다. 스토어는 여러 개의 리듀서를 가질 수 있다. 각각의 리듀서가 처리하는 상태는 최종적으로 최상위 리듀서가 시스템의 상태 트리로 만들어서 스토어를 구독하는 구독자에게 전달한다.

리덕스는 스토어를 좀 더 상위 수준으로 추상화하고 대신 하부 로직을 분리해서 책임을 분산시키니다. 아래의 예제 코드를 보고 알 수 있듯이 리덕스에서 스토어는 내부 실체가 보이지 않는다. 그저 리듀서만 보일 뿐이다. 상태 처리로직만 작성하면 나머지는 리덕스가 알아서 결합한다.

import { createStore } from 'redux'

function counter(state=0, action) {
  	switch (action.type) {
      case 'increment : 
        return state + 1;
        
      case 'squre' :
        return state * state;
        
      default : 
        return state;
    }
}
const store = createStore(counter);

리듀서와 순수 함수에 대한 생각

리덕스의 공식 문서에 있는 예제들은 순수 함수를 적극 활용한다. 순수함수는 아래와 같은 특징을 갖는 함수를 말한다.

  1. 함수 밖에 있는 데이터나 변수를 변경해서 의도치 않은 결과를 발생시키지 않아야 한다.
  2. 동일한 입력 데이터의 집합을 제공받으면 항상 동일한 연산 결과를 반환해야 한다.

리듀서를 순수하게 유지한다는 것은 다시 말해 시스템이 복잡해져도 리듀서를 가능한 단순하게 유지한다는 것이 된다.

resolvers.deleteComponent = (prevState, { targetId }) => {
  const { order, compMap } = prevState;
  const updatedOrder = deleteComponent(order, targetId);
  
  return Immutable
        .fromJS(prevState)
        .merge({
            order: updatedOrder,
            focusCompId: ""
        })
        .deleteIn(["compMap", targetId])
        .toJSON();
};

입력을 받으면 입력 값을 처리해 새로운 상태를 결과로 반환하는게 전부다. 입력 값을 전달하는 것 외에는 함수 실행 중에 외부의 어떤 조건에도 영향을 받지 않는다. 하는일이 단순하니 예측하기 쉽고, 테스트하기도 쉽다. 리덕스가 자랑하는 시간 여행 디버깅이 가능한 것도 리듀서의 이런 특성 덕이다.

it("컴포넌트 목록에서 원하는 컴포넌트 하나를 삭제할 수 있다.", () => {
    // given
    const deleteTarget = 'target';
    const action = compListAction.deleteComponent(deleteTarget);

    // when
    const { order, compMap } = compListReducer(initState, action);

    // then
    order.should.not.include(deleteTarget);
    should.not.exist(compMap[deleteTarget]);
});

하지만 리듀서를 함수로 구성하다 보니 행위와 데이터를 하나로 결합한 도메인 모델을 구성하기가 어렵고, 이로인해 설계에 익숙하지 않은 어색한 지점이 발생(데이터의 정체성을 어떻게 정의할지 모호하다든지, 데이터를 가져오는 AJAX 요청을 도메인 레이어의 앞단에서 처리해야한다든지)하는 단점이 있다.

리듀서가 순수해야 한다는 뜻은 리듀어세 전달하는 값이 동일하다면, 리듀서가 돌려주는 값도 항상 동일해야 함을 의미한다. 그래서 비동기 통시넟럼 사이드 이펙트를 만들 수 있는 행위는 리듀서에 담지 않는다.
비즈니스 로직은 처리 대상인 데이터와 가까운 곳에 위치하는 것이 좋기에, 리덕스 어플리케이션의 비즈니스 로직은 리듀서에 위치하는 것이 적절하다.처리하려는 로직의 성격에 따라 리듀서 내부의 로직도 얼마든지 복잡해질 수 있고, 그러다보면 리듀서 내에서 다른 여러 함수를 호출하는 경우도 자주 있다.

이 지점에서 오해하지 말아야 할 한 가지는, 리듀서를 '순수'하게 만들기 위해서 반드시 함수를 고집해야 하는 것은 아니라는 점이다. 객체를 이용해서도 얼마든지 리듀서를 순수하게 만들수 있다.


출처

원문 보기

profile
안녕하세요 주니어 프론트엔드 개발자 양윤성입니다.

0개의 댓글