도움말 - 글감 수집하기 (인용)

도움말 - 부분 리뷰 작성하기

리덕스(Redux)의 리듀서(reducer)가 순수 함수여야만 하는 이유 (Why Redux need reducers to be "pure functions")

리덕스(Redux)가 함수형 프로그래밍(functional programming)의 "순수 함수(pure function)"에 의존한다고 알고 있을 것이다. 이것이 실제로 의미하는 바는 무엇일까?

아래 그림은 리덕스 예제의 간단한 할 일(Todo) 앱이다. 현재 4개의 할 일이 있다. 네 번째 할 일이 완료된(completed) 것으로 표시되고 완료된 할 일과 완료되지 않은 할 일 "모두(All)"를 표시하도록 설정되어 있다.

오른쪽은 리덕스에 저장된 현재 상태(state)를 보여준다. 한 곳에서 모든 세부 정보를 관리(capture)하는 간단한 자바스크립트 객체다.

이것이 리덕스가 대단한(beauty) 이유다.


여기서 네 번째 할 일을 완료하지 않은 것으로 토글했다고 생각 해보자. 새로운 리덕스 상태는 아래와 같을 것이다:


이제 리듀서에서 "TOGGLE_TODO"(완료된 것과 완료되지 않은 것 사이에서 할 일 항목의 상태(status)를 토글하는)를 보면, 아래와 같다 (여기에서 소스를 확인 해보자) :


할 일 항목의 상태를 토글 하면, 이러한 일이 이뤄진다: 먼저, 리듀서 함수는 "이전(old)"상태를 나타내는 객체를 취한 다음 이전 객체의 모든 세부 정보를 해당 새로운 객체에 복사하고(idtext와 같은), 기존 속성을 새로운 속성으로 대체한다 (completed prop).


순수 함수

기본적으로 입력 데이터를 변경하지 않고 외부 상태 (데이터베이스, DOM 또는 전역 변수와 같은)에 의존하지 않으며 동일한 입력 값에 대해 동일한 출력 값을 일관되게 제공하는 함수를 "순수(pure)" 함수라고 한다.

예를 들어 아래의 add 함수는 "a"또는 "b"를 변경하지 않고 외부 상태에 의존하지 않으며 항상 동일한 입력 값에 대해 동일한 출력 값을 리턴한다.


const add = (a, b) => a + b //pure function

이제 리듀서 함수를 보면 이것 역시 "순수한" 함수임을 알 수 있을 것이다.

그런데, 리듀서는 왜 "순수" 함수여야 할까?

우리가 리듀서를 "순수하지 않은(impure)"상태로 만들면 어떤 일이 일어나는지 보자. 새로운 객체를 만드는 부분을 주석 처리하고, 대신에 completed prop을 직접 변경 해보자.


case 'TOGGLE_TODO':
if (state.id !== action.id) {
return state;
}
// return {
// ...state,
// completed: !state.completed
// }
state.completed = !state.completed;//change original object
return state;
default: ...

위와 같이 변경 후, TODO를 토글하면 아무 일도 일어나지 않는다!

리덕스 소스에서 어떤 일이 일어나는지 확인하자.


리덕스는 주어진 상태(객체)를 가져 와서 loop의 각 리듀서로 전달한다. 그리고 변경 사항이있는 경우 리듀서의 새로운 객체를 리턴하고, 변경 사항이 없으면 이전 객체를 리턴한다.

리덕스는 두 객체의 메모리 위치를 비교하여 이전 객체가 새 객체와 동일한 지 여부를 단순 체크한다. 따라서, 리듀서 내부에서 이전 객체의 속성을 변경(mutate)하면 "새 상태"와 "이전 상태"가 모두 동일한 객체를 가리킨다. 그러므로 리덕스는 아무것도 변경되지 않았다고 생각한다! 그렇기 때문에 이것이 동작하지 않는것이다.

그러나 다음과 같은 중요한 질문에 대한 답이 남아있다:

  • 리덕스는 왜 이렇게 설계 되었을까?
  • 리덕스가 이전 상태 값을 다른 곳으로 복사 한 다음, 리듀서의 객체 props를 비교할 수 없는 이유는 무엇일까?
  • 리덕스가 개발자에게 이러한 부담을주는 이유는 무엇일까?

대답 : 두 개의 자바스크립트 객체가 동일한 속성을 갖고 있는지를 아는 단 한 가지 방법은 이 둘을 깊이 비교(deep-compare)하는 것 뿐이다.

그러나 이것은 일반적으로 큰 객체 이거나 비교해야하는 횟수가 많다면 실제 앱에서는 매우 무거운(expensive) 작업이 된다.

따라서 이것의 해결 방법으로는 변경 사항이 있을 때마다 개발자에게 새 객체를 만들어서 프레임 워크로 보내도록 하는 정책을 만드는 것이다. 그리고 변경 사항이 없다면 이전 객체를 그대로 되돌려 보내면 된다. 다시 말하면, 새로운 객체는 새로운 상태를 나타낸다.

slice 또는 유사한 메커니즘을 사용하여 이전 상태를 복제하여 이전 값을 새 객체로 복사해야하는 것에 주목하자.

이제 이러한 정책을 적용하면 객체의 각 속성 비교없이 ! ==를 사용하여 두 객체의 메모리 위치를 비교할 수 있다. 두 객체가 같지 않으면 객체의 상태가 변경된 것이라고 할 수 있다 (즉, 자바스크립트 객체의 속성이 변경 됨). 그것이 리덕스가 사용하는 전략이다.

이것이 리덕스의 "리듀서"가 순수 함수여야만 하는 이유이다!



이 글은 rajaraodv의 Why Redux need reducers to be “pure functions”를 번역한 글입니다. 전문 번역가가 아니라 오역이 있을 수 있습니다. 지적해주시면 수정하도록 하겠습니다. 원문은 아래에서 확인 하실 수 있습니다.









리뷰