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


5.3 Immutable Update

앞에서 Reducer의 규칙들에 대해서 배웠습니다.
그리고 그 규칙에서는 Immutable Update라는 것이 등장했었죠.
지금부터는 Immutable Update에 대해 조금 더 자세히 살펴보도록 하겠습니다.

Immutable Updates를 한글로 풀어보면 불변적 업데이트라는 뜻이 됩니다.
그리고 이 불변적 업데이트라는 것은, 현재 State를 변경하지 않고, 새로운 State를 만들어 업데이트 하는 방식을 의미합니다.

Reducer and Immutability

앞에서 살펴봤던 이 그림처럼, 기존 State는 그대로 두고 기존 State를 기반으로 새로운 State를 만들어서 업데이트 하는 방식을 의미하는 것이죠.
그리고 이러한 불변적 업데이트 방식을 영어로 Immutable Update라고 하는 것입니다.


그럼 실제로 Immutable Update를 사용하는 코드를 한 번 볼까요?

잘못된 업데이트 ❌

function sampleReducer(state, action) {
    switch (action.type) {
        case "SAMPLE_ACTION":
            state.value = action.value;
            return state;
        default:
            return state;
    }
}

정상적인 업데이트 ✅

function sampleReducer(state, action) {
    switch (action.type) {
        case "SAMPLE_ACTION":
            return {
                ...state,
                value: action.value
            };
        default:
            return state;
    }
}

여기에 두 개의 sampleReducer 코드가 있습니다.
각각 다른 형태로 state를 업데이트 하는 코드가 들어가 있습니다.

먼저 위쪽 코드를 보면, 현재 state에 있는 value값에 action 객체에 있는 새로운 value 값을 대입하는 것을 볼 수 있습니다. 현재 state값을 변경하고 있는 것이죠.

반대로 아래쪽 코드를 보면, 현재 state를 변경하는 것이 아니라 새로운 state 객체를 만들어서 리턴하는 것을 볼 수 있습니다.

여기서 으로 된 부분은 JavaScript의 Spread 문법인데, 기존 state 객체의 데이터를 복사해온다고 생각하시면 됩니다.
그리고 그 아래에서 value라는 키에 action 객체에 있는 새로운 value값을 넣게 되면 복사해온 데이터에서 중복된 키에 해당되는 값이 덮어써지게 됩니다.
결과적으로 여기에서는 새로운 value값이 들어간 객체가 새로 만들어지는 것이죠.

이 두 가지 방식 중에서 위쪽은 잘못된 업데이트 방식이고, 아래쪽이 정상적인 업데이트 방식입니다.
Redux의 Reducer에서는 기존 state 값을 변경하는 것을 허용하지 않기 때문이죠.
그리고 아래쪽과 같이 새로운 state를 생성해서 업데이트 하는 방식을 바로 immutable update라고 부릅니다.

여기서 이 immutable update를 하는 방법에 대해서 잘 기억해두기 바랍니다.
왜냐하면, 위쪽 방식처럼 현재 state의 값을 직접 변경하는 것처음 Redux를 사용하는 개발자들이 생각보다 많이 저지르는 실수이기 때문입니다.

immutable update 방식은 생각보다 코드양도 많아지고 비효율적으로 보이기 때문에, 평소에 익숙한 방식으로 값을 변경하려고 하다보면 위쪽과 같은 방식으로 개발하게 되는 경우가 많습니다.
그래서 Reducer를 만들 때는 이러한 점을 꼭 유의하면서 코드를 작성해야 합니다.

자, 그렇다면 만약 state 객체가 굉장히 복잡한 객체일 경우에는 어떻게 immutable update를 해야 할까요?

function sampleReducer(state, action) {
    switch (action.type) {
        case "SAMPLE_ACTION":
            return {
                ...state,
                depth1: {
                    ...state.depth1,
                    depth2: {
                        ...state.depth1.depth2,
                        depth3: {
                            ...state.depth1.depth2.depth3,
                            value: action.value
                        }
                    }
                }
            };
        default:
            return state;
    }
}

이 코드는 복잡한 state 객체를 업데이트 하는 경우를 나타낸 것입니다.
여기서도 역시 immutable update를 위해서 새로운 객체를 생성해서 리턴하는 것을 볼 수 있습니다.

그런데 한 가지 문제는, 여기서 변경하려는 값이 state 객체의 깊숙한 곳에 들어있다는 것입니다.
그래서 중첩된 하위 객체들에 대해서 모두 Spread 문법을 사용해서 기존 값들을 복사해오고 있습니다.

그럼 여기서 우리는 한 가지 고민을 하게 됩니다.

"실제로 상용 프로젝트에 Redux 적용하려고 하면 복잡한 객체들을 Redux를 통해서 관리해야 할 경우가 많을텐데, 그럴때마다 매번 이렇게 코드를 작성해야 할까?"

라는 고민을 하게 되겠죠.

하지만 그 부분은 걱정하지 않아도 됩니다. 왜냐하면 우리가 지금 배우는 방식은 과거의 방식이라고 보면 되고, 지금은 Redux Toolkit을 사용하는 것이 사실상 Redux 개발의 표준으로 자리잡았기 때문입니다.

Redux Toolkit을 사용하는 방법에 대해서는 뒤에서 자세히 배울텐데, Redux Toolkit을 사용하면 복잡한 객체에 대해서도 손쉽게 immutable update를 처리할 수 있습니다.
그 원리는 앞에서도 나왔지만, 바로 Redux Toolkit이 Immutable Update를 위해 내부적으로 immer라는 라이브러리를 사용하기 때문입니다.

그래서 이 강의를 모두 다 완강한 이후에는 최종적으로는 Redux Toolkit을 사용하면 됩니다.
하지만 굳이 과거의 방식을 이렇게 배우는 이유는 곧바로 Redux Toolkit을 사용하면 내부에 담겨있는 Redux의 작동 원리를 알지 못한채 Redux를 사용하게 될 가능성이 높기 때문입니다.

어떤 기술이든 먼저 내부에 담겨 있는 작동 원리와 과거에 불편했던 부분들에 대해서 이해하고, 그 이후에 불편한 부분들을 개선하기 위해 등장한 새로운 기술에 대해서 배우는 것이 좋습니다.

여기에서는 Redux Toolkit이 바로 그 새로운 기술에 해당하는 것이죠.
이러한 부분을 잘 기억하면서 다음으로 넘어가면 좋을 것 같습니다.


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

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