티스토리 뷰

리덕스란 자바스크립트로 만들어진 애플리케이션들의 state를 관리하기 위한 라이브러리입니다. 리액트와 같이 쓸 수 있는 것뿐이지 리액트에 종속된 라이브러리는 아닙니다.

 

리덕스는 단방향 데이터 흐름을 가지고 있습니다.

그래서? 그걸 왜 알아야 하는지 알아보면서 불변성과 리덕스의 동작원리에 대해 조금 더 알게 된 기록을 남기는 글입니다.

 

1. MVC, Flux

단방향이 있으면 양방향도 있겠지요. 둘의 차이를 알아보던 중 리액트를 개발한 페이스북의 사례가 나왔습니다.

처음에 페이스북에서 사용하던 MVC패턴입니다.

보시다시피 데이터가 양방향으로 이동할 수 있습니다. 컨트롤러에 의해 모델에 반영된 state를 view에 전달해서 랜더링 합니다. 혹은 사용자와 상호작용으로 view의 데이터에 변화가 생기면 model을 업데이트해야 합니다. 단, 하나의 변화가 여러 모델을 업데이트할 수도 있고, 하나의 모델이 여러 개의 view를 업데이트할 수도 있습니다. 애플리케이션이 점점 커지면 이 과정이 거미줄처럼 얽혀서 대체 지금 일어나고 있는 이 랜더링이 어디서부터 발생하는 건지 너무 헷갈릴 수 있습니다. 예측이 어렵습니다.

 

페이스북(현 메타)가 바꾼 flux패턴입니다. 데이터가 항상 단방향으로 흐릅니다.

상호작용에 의해 Action이 발생되고 Action은 Dispatcher를 거쳐 Store로 향하고 Store에서 view를 업데이트합니다.

이런 구조로 데이터 흐름을 훨씬 예측하기 쉬워졌습니다. 

 

페이스북에서 대체한 flux패턴이 리덕스가 아닙니다!!!

리덕스는 flux패턴에 reducer개념을 추가해서 만든 전역 상태 관리 라이브러리입니다. 

위에 나온 flux패턴 === 리덕스 가 아닙니다!!

 

리덕스

출처는 리덕스 공식문서 https://redux.js.org/tutorials/essentials/part-1-overview-concepts#redux-application-data-flow

위 영상을 보시면 단방향 데이터 흐름이 왜 예측하기 쉬운지, 액션은 무엇이고 리듀서는 무슨 일을 하는지 더 쉽게 알 수 있습니다.

 

Redux의 구성 요소

  • Store: 애플리케이션의 state(상태)를 저장하는 곳. Store 내에 Reducer 함수가 존재함.
  • Dispatch: Reducer 함수에게 Action을 송신하는 역할.
  • Action: 유저의 움직임으로부터 발생된 이벤트(버튼 클릭, input에 데이터 입력 등)와 API로부터의 데이터 수신 등 state(상태)를 갱신하기 위한 정보를 담은 object(객체). Dispatch 함수에 의해 Action을 Reducer에게 보낼 수 있다.
  • Reducer: 함수. Dispatch 통해 받은 Action을 토대로, state를 modify, 갱신, 변경할 수 있는 유.일.한 함수.
  • View: 애플리케이션의 유저 인터페이스(UI)

 

이렇게 예측하기 쉬운 단방향 데이터 흐름을 가진 리덕스를 사용하려면 세 가지 원칙이 있습니다. 

  • Single source of truth
    애플리케이션 내에 Store는 반드시 1개뿐.Store는 반드시 1개만 존재한다. 
    직렬화해서 서버와 클라이언트가 프로그램 전체 상태 값을 서로 주고받을 수 있다.
    어떤 특정한 상태에 있을 때 발생하는 버그를 확인하기 위해 그 상태 값을 저장한 후 반복해서 재현할 수 있다.
    실행 취소(undo)와 다시 실행(redo) 기능을 쉽게 구현할 수 있다.
  • State is read-only
    state를 직접 변경해서는 안.된.다.
    state를 변화시키는 유일한 방법은 Action을 Reducer에 dispatch(송신, 전달)하는 방법뿐이다.
    즉, state의 변경은 Reducer만 할 수 있다. Reducer 이외의 공간에서는 state는 읽기 전용인 것이다.
  • Changes are made with pure functions
    Reducer는 순수 함수여야만 한다.
    Reducer 함수는 parameter로 기.존.의 state와 Action을 받는데, Reducer 함수는 기존의 state를 직접 변경하지 않고, 새.로.운 state object(객체)를 작성해서 return 해야 한다.

이런 원칙을 왜 지켜야 할까요?

순수함 수란 게 뭔지 알아야 합니다.

 

순수함수

  • side effect(전역 변수의 값을 수정하거나 API 요청을 보내는 등 함수 외부의 상태를 변경)를 발생시키지 않아야 한다.
  • 같은 인수에 대해 항상 같은 값을 반환해야 한다.
    • 랜덤 함수나 시간 함수를 이용하면 순수 함수가 아니다.
    • 같은 인수를 입력해도 호출하는 시점에 따라 다른 값을 반환하기 때문이다.

무슨 소리인지 잘 모르겠습니다.

예를 들어서 알아보겠습니다.

 

1.

function calc(a, b) {
  return a * b;
}

calc(2, 3);
calc(2, 3);

위 calc 함수는 순수함수입니다. 

함수 외부의 상태를 변경하지 않고, 실행 시점과 관계없이 항상 2,3을 입력하면 6이라는 결과가 나오기 때문입니다.

 

2.

let c = 1;
function calc(a, b) {
  return a * b * c;
}

calc(2, 3);

위 함수는 순수함수가 아닙니다.

두 번째 조건(같은 인수에 대해 항상 같은 값을 반환해야 한다.)에서 만족하지 않습니다.

c변수가 2로 재할당 된다면 결과는 12가 될 수도 있습니다. 즉, 같은 인수에 대해 항상 같은 값을 반환하지 않습니다.

 

3.

let c = 1;
function calc(a, b) {
  c = a + b;
  return a * b;
}

console.log(calc(2, 3));
console.log(c);

위 함수는 순수함수가 아닙니다.

첫 번째 조건(side effect를 발생시키지 않아야 한다.)을 만족하지 않습니다.

전역변수 c의 값을 변경하기 때문입니다. c는 1에서 5로 변경되었습니다.

 

그럼 순수함수가 뭔지 알았는데... 그래서 reducer는 왜 순수함수여야 한다는 걸까요?

 

Redux의 변경 감지 정책

리덕스를 써보시면 불변성에 대한 궁금증이 생깁니다. 단어도 어렵고, 왜 지켜줘야 하는지도 이해가 안 됩니다.

결론부터 말씀드리자면 리덕스가 state가 변화했다는 것을 알아차리고 리랜더링 시켜주려면 불변성을 지켜야 합니다. 

function counter(state = initialState, action) {
  switch(action.type) {
    case types.INCREMENT:
      return { ...state, number: state.number + 1 };
    case types.DECREMENT:
      return { ...state, number: state.number - 1 };
    default:
      return state;
  }
}

* Dino_Ground님의 예제를 그대로 사용했습니다

 

리듀서는 항상 state와 action을 인자로 받습니다. 그리고 state를 직접 변경하는 게 아니라 spread연산자를 이용해서 직전의 state를 복사한 후 새로운 객체를 만들어 리턴합니다. 

이것이 불변성을 지켜주는 것입니다. state를 직접 변경시키면 불변성이 깨지고 리랜더링이 안 일어납니다.

제가 직접 테스트해본 결과를 보시거나 직접 해보셔도 됩니다.

https://pungwa.tistory.com/79

즉, 리덕스 세 가지 원칙 중 2번째 원칙과 연결됩니다.

아직 불변성에 대해 와닿지 않습니다. 그래서 얕은 복사 깊은 복사에 대해 알아야 합니다.

 

얕은 복사(Shallow Copy)와 깊은 복사(Deep Copy)

얕은복사

const obj = { value: 10 }
const newObj = obj

newObj.value = 5;

console.log(obj.value); // 5
console.log(newObj.value); // 5

변수가 메모리에 할당된 메모리 주소를 가리키고 있다는 건 아시지요?

위에서 new.value를 5로 바꿨을 뿐인데 obj의 value로 5로 바뀌었습니다. 즉, 두 번째 줄의 const newObj = obj에서 

newObj는 완전히 새로 메모리를 할당받은 변수가 아니라 obj의 메모리 주소를 참조하는 변수입니다. 결국 하나의 메모리를 공유하고 있는 것이죠. 이런 것을 얕은 복사라고 합니다.

 

깊은복사

const obj = { value: 10 }
const newObj = { ...obj }

newObj.value = 5

console.log(obj.value); // 10
console.log(newObj.value); // 5

위 예제와 다른 점이 눈에 보이시죠? spread연산자로 새로운 객체를 하나 만들었고 이번에는 같은 메모리를 공유하지 않습니다.

즉 newObj도 어엿한 하나의 메모리 주소를 가지고 있는 변수입니다.

이런 것을 깊은 복사라고 합니다.

 

다시 리덕스 이야기로 돌아가면

불변성과 얕은 복사, 깊은 복사는 무슨 관계냐..

바로 리덕스의 변경 알고리즘과 관련 있습니다.

reducer는 state가 변경되었는지 검사하기 위해 state 객체의 메모리 주소를 비교합니다. 

만약 위처럼 얕은 복사를 하거나 state를 직접 변경하게 되면 메모리 주소는 여전히 같겠죠?

같은 메모리 주소에 value값만 변경되었으니 reducer는 state가 바뀌었다는 것을 감지할 수 없습니다.

그래서 리랜더링이 안 일어납니다.

반면 두 번째 예제처럼 새로운 객체를 만들어 반환하게 되면 state의 주소 값이 변했으니 reducer가 변경되었다는 것을 알 수 있습니다.

리랜더링이 잘 일어나고 view에 반영됩니다.

이것은 얕은 동등성 비교라고 하는 것 같습니다.

 

그렇다면 왜 데이터 그 자체가 아니라 메모리 주소 값을 비교하느냐? 왜 개발자가 불변성에 신경 쓰게 만드냐..

바로 성능과 복잡성 때문이라고 합니다. 성능에 안 좋아서!

 

거꾸로 올라가며 정리를 하자면

리덕스의 리듀서가 변화를 감지하려면 얕은 동등성 비교를 해야 하고

그러려면 리듀서가 순수함수여야 하고 state를 직접 변경해서는 안된다. 

리덕스는 flux패턴에 reducer가 추가된 개념으로 만들어졌고 단방향 데이터 흐름을 가져서 예측하기 쉽다.

 

 

 

 

 

출처

https://basemenks.tistory.com/284

 

Redux란 무엇인가 (VS Flux, Redux만 있는 장점)

이 글도 읽어보세요! 2022.03.13 - [📲 For Front/📲Front Knowledge] - Redux의 구성요소와 3원칙 1. Redux란 무엇인가요? Redux는 JavaScript의 라이브러리 중 하나이며, React와는 독립적인 존재입니다. Redux..

basemenks.tistory.com

https://velog.io/@make_w/Redux-Reducer%EB%8A%94-%EC%99%9C-%EC%88%9C%EC%88%98%ED%95%A8%EC%88%98%EC%9D%B8%EA%B0%80

 

Redux Reducer는 왜 순수함수인가?

순수함수 순수함수란 동일한 인자가 주어졌을 때 항상 동일한 결과를 반환해야 하며 외부의 상태를 변경하지 않는 함수이다. 즉 함수 내 변수 외에 외부의 값을 참조, 의존하거나 변경하지 않아

velog.io

https://velog.io/@iamhayoung/Redux-JavaScript-Redux-%EC%9E%85%EB%AC%B8-Store-Reducer-Action-Dispatch-Subscription

 

Redux + JavaScript :: Redux 입문 (Store, Reducer, Action, Dispatch, Subscription)

Redux의 탄생 배경을 알아봄으로써 Redux에 좀 더 가까이 다가가보며, Redux의 기초 구문에 대해 알아봅시다🤓

velog.io

 

공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
TAG more
«   2025/04   »
1 2 3 4 5
6 7 8 9 10 11 12
13 14 15 16 17 18 19
20 21 22 23 24 25 26
27 28 29 30
글 보관함