처음 만난 리덕스 (Redux) 문서


8.3 Duck

지금부터는 Ducks Pattern에 등장하는 Duck이라는 것의 개념에 대해서 배우고, 실제로 Ducks Pattern을 사용하는 방법까지 배워보도록 하겠습니다.

먼저 Ducks Pattern에서 나오는 Duck은 뭘까요?

Duck

바로 오리죠 오리.
이 사진은 제가 따로 구해온게 아니라 Ducks Pattern 공식 repository에서 가져온 것입니다.
사실 Duck의 공식 정의는 오리가 아니라 Redux Reducer Bundle입니다.
Redux의 reducer와 관련된 구성요소들을 모아놓은 일종의 꾸러미라고 할 수 있습니다.

그렇다면 왜 이름을 Duck이라고 지었을까요?
그 이유를 알고나면 허탈하게 웃으실 수도 있는데, Java에는 jars와 beans가 있고, Ruby에는 gems가 있는 것처럼, Redux의 뒷 발음 dux를 따서 그냥 ducks라고 한 것입니다.
약간의 말장난이긴 한데 조금 웃기지만 그럴듯하죠?

앞에서 Duck을 Redux Reducer Bundle이라고 했습니다.
그리고 이런 각각의 파일을 Duck file이라고 부릅니다.
Duck file의 구조는 다음과 같습니다.

Duck file

가장 먼저 Actions가 나옵니다.
여기서 Action은 실제 Action 객체가 아니라 action의 type을 나타내는 이름입니다.
우리가 앞에서 실습을 할 때 Action Type이라고 정의했던 문자열들이 여기에 들어가게 됩니다.

Action 다음으로는 Reducer가 나옵니다.
각각의 Action을 처리하는 역할을 하는 함수죠.

그 다음으로는 Action Creators가 나옵니다.
Action 객체를 생성하는 함수죠.

마지막으로 Side effects가 나오는데, side effects는 앞에서도 배웠지만 Reducer가 아닌 외부와 연관된 동작들을 의미한다고 보면 됩니다.
대표적인 side effect로 서버로부터 데이터를 받아오는 것이 있었죠.
Duck file에서 Side effects는 필수적인 요소는 아니고 필요한 경우에만 넣으면 됩니다.

이러한 Duck file에는 몇 가지 지켜야 할 규칙들이 있습니다.

  1. MUST export default a function called reducer()
  2. MUST export its action creators as functions
  3. MUST have action types in the form npm-module-or-app/reducer/ACTION_TYPE
  4. MAY export its action types as UPPER_SNAKE_CASE, if an external reducer needs to listen for them, or if it is a published reusable library

영어로 써있어서 어렵게 느껴질 수 있는데 하나씩 설명해보도록 하겠습니다.

먼저 첫 번째 규칙은 reducer를 export default 해야 한다는 것입니다.
JavaScript 모듈을 export 할 때 named exportdefault export 방식이 있는데, Duck file에서는 Reducer를 default export 해야 한다는 규칙입니다.

그리고 두 번째 규칙은 Action Creators를 named export 해야 한다는 규칙입니다.
이렇게 함으로써 외부에서 이 Duck file을 import해서 reducer나 action creators에 접근하게 해주는 것입니다.

세 번째 규칙은 action type의 이름 포맷에 관한 규칙입니다.
모듈 또는 애플리케이션 이름, reducer의 이름, 그리고 대문자로 쓴 ACTION_TYPE을 슬래시로 구분해서 순서대로 써야 한다는 규칙입니다.
Action Type 조차도 어떤 정해진 규칙이 없으면 나중에 관리가 되지 않고 혼란을 유발할 수 있기 때문에 이런 규칙을 만든 것입니다.

마지막 네 번째 규칙은 action type은 영어 대문자만을 사용하여 snake_case로 작성해야 하며, 외부에서 action type을 필요로 할 경우에는 named export를 할 수 있다는 규칙입니다.
참고로, 여기서 snake_case는 각 단어 사이를 underscore라고 부르는 기호로 구분하여 표기하는 방법입니다.
마치 바닥에 뱀이 기어가는 듯한 모습이라고 해서 snake_case라고 부르는 것입니다.

이렇게 보면 Duck file의 규칙이 조금 많아 보이지만, 실제로 작성한 Duck file을 보면 아마 한 눈에 이해가 될 겁니다.
그럼 실제 Duck file을 한 번 볼까요?

// Actions (Action Types)
const SET_COUNT      = 'my-app/counter/SET_COUNT';
const INCREASE_COUNT = 'my-app/counter/INCREASE_COUNT';
const DECREASE_COUNT = 'my-app/counter/DECREASE_COUNT';

// Reducer
export default function reducer(state = 0, action = {}) {
    switch (action.type) {
        case SET_COUNT:
            return action.count;
        case INCREASE_COUNT:
            return state + 1;
        case DECREASE_COUNT:
            return state - 1;
        default:
            return state;
    }
}

// Action Creators
export function setCount(count) {
    return { type: SET_COUNT, count: count };
}

export function increaseCounter() {
    return { type: INCREASE_COUNT };
}

export function decreaseCounter() {
    return { type: DECREASE_COUNT };
}

// 필요한 경우 Side Effects 작성
export function getCount () {
    return dispatch => get('/count').then(count => dispatch(setCount(count)));
}

이 파일은 counter와 관련된 기능을 Redux를 통해서 처리하기 위해 만든 Duck file입니다.
파일을 쭉 살펴보면 앞에서 나왔던 Duck file의 구조와 규칙에 따라서 작성된 것을 볼 수 있습니다.

먼저 각 Action의 고유한 이름들을 정의한 Actions가 나옵니다.
Action은 애플리케이션 이름, reducer이름, action type이 슬래시로 구분되어 작성된 것을 볼 수 있습니다.
그리고 Action Type은 영어 대문자만을 사용하여 snake_case로 작성된 것도 볼 수 있습니다.

그리고 이어서 Reducer 함수가 나오는데, 여기에 export default를 써서 reducer를 default export 한 것을 볼 수 있습니다.
또한 Reducer 내부에 있는 switch/case문에서는 바로 위에서 정의한 Action Type을 사용하는 것도 볼 수 있습니다.

다음으로는 Action Creator 함수들이 나오고 named export를 해주는 것을 볼 수 있습니다.
이렇게 export를 해줌으로써 외부에서 Action을 dispatch하고 싶을 때, 이 Action Creator를 사용해서 Action 객체를 생성할 수 있게 됩니다.

그리고 마지막으로는 side effect가 나옵니다.
Side Effects는 필수는 아니기 때문에 필요한 경우에만 넣으면 됩니다.

이렇게 하나의 기능에 필요한 Action, Reducer, Action Creator, 그리고 Side Effects까지 모아놓은 것이 바로 Duck file이며, 이러한 Duck file들로 Redux에 필요한 요소들을 관리하는 패턴을 바로 Ducks Pattern이라고 합니다.


마지막 업데이트: 2023년 07월 14일 00시 00분

이 문서의 저작권은 이인제(소플)에 있습니다. 무단 전재와 무단 복제를 금합니다.