리덕스를 사용하는 이유(1)

최경열·2021년 9월 15일
0
post-thumbnail

리덕스란?

리덕스는 JavaScript 어플리케이션에서 data-state, UI-state를 관리해주는 도구이다.
이는 데이터관리가 쌓일수록 불편해지는 SPA에서 매우 유용하게 주로 사용된다. 대표적으로
React에서 사용되나, 그외에도 jQuery, Angular 에서도 사용 될수 있다.

리덕스의 사용이유

주로 사용하는 React에서는 어플리케이션을 만들때, 보통 하나의 루트 컴포넌트(App.js)에서 상태를 관리한다. 예를 들어서 ToDoList 프로젝트에서는 다음과 같은 구조로 상태가 관리되고 있다.


React 프로젝트에서는 대부분 작업시 부모 컴포넌트가 중간자의 역할을 한다.
물론 컴포넌트 끼리 직접적으로 소통할수 있는 방법은 있지만, 그렇게 하게 될 경우 코드가 굉장히 많이 꼬이므로 절대 권장되지 않는 방식이다.(ref 등을 사용해 이런 작업을 할수 있다.)

App에서는 Input과 이를 변경하는 onChange, 새로 생성하는 onCreate함수를 props로 Form에 전달한다. Form은 위 함수들과 값을 받아서 화면에 보여주고, 변경 이벤트가 발생시 부모에게서 받은 onChange함수를 호출하여 App 지닌Input값을 업데이트 한다. 이후 생성 버튼을 누르면 onCreate를 호출하여 toDo배열을 업데이트 한다. toDo배열이 업데이트 되면 해당 배열은 toDoItemList 컴포넌트에 전달이 되어 최종적으로 화면에 렌더링이 된다.
이런식으로 App컴포넌트를 거쳐서 건너건더 필요한 값을 업데이트후, 다시 랜더링 하는 방식으로 프로젝트가 개발이 된다.
이러한 구조는 부모 컴포넌트에서 모든것을 관리하고 아래로 내려주기 때문에 직관적이며 관리하는 것도 편하다.(왜냐하면 모든것은 부모컴포넌트에 전달되므로) 그러나 문제는 앱의 규모가 커졌을 경우 컴포넌트의 갯수는 늘어나고 다른 데이터도 늘어나면서 업데이트 해야하는 데이터들도 늘어나면 하나의 부모컴포넌트가 관리하는 코드가 매우길어지고, 매번 하위컴포넌트는 매번 부모 컴포넌트에게 요청을 해야하기 때문에 성능저하와 유지보수가 힘들어 진다.
예를들어 다음과 같은 프로젝트 구조를 살펴보면

Root 컴포넌트에서 G컴포넌트에게 값을 전달하기 위해서는 어떻게 해야할까?

위 그림처럼 Root의 하위 컴포넌트 A를 지나 E를 지나 G에게 도달한다.
즉 하나의 컴포넌의 변화를 위해서 많은 경로를 지나야 하는 불편함이 발생하는 것이다.
코드를 통해 예를 살펴보면 이 같은 코드가 존재할시

// App.js 에서 A 렌더링
<A value={5}>

// A.js 에서 E 렌더링
<E value={this.props.value} />

// B.js 에서 G 렌더링
<G value={this.props.value} />

value라는 이름을 다른 이름으로 수정시 파일3개를 모두 수정해야 하는 유지보수적 번거로움이 발생한다.

리덕스는 상태관리를 컴포넌트 바깥에서 한다!

리덕스를 사용하면 상태값을 부모의 컴포넌트, 하나의 컴포넌트에 종속시키지 않고, 상태관리를 컴포넌트 바깥에서 관리할수 있게 된다. 그림으로 설명하면 다음과 같은 구조이다.
예를들어 B에 변화가 G에 반영된다고 가정을 해보자

스토어 설정


리덕스를 프로젝트에 적용할 경우 스토어라는 것이 생기는데 이 스토어에는 이름 그대로 프로젝트의 상태에 관한 데이터가 저장되어있다.

컴포넌트의 스토어 구독


G컴포넌트는 스토어에 구독을 한다. 구독하는 과정에서 특정함수가 스토어에 전달이 되며, 나중에 스토어의 상태값이 변동이 생길시 전달받은 함수를 호출해 준다.

스토어에게 상태 변경하라고 알려주기


이제 B컴포넌트에서 이벤트가 발생하여 상태를 변화할 일이 생길시, dispatch라는 함수를 통해서 action을 스토어에게 전달한다. 예를들어 { type: 'INCREMENT' }란 객체를 전달받으면, 스토어는 해당 객체의 action을 참조하게 된다.
추가적으로 상태값에 2를 + 해야 한다면 이런 action객체를 만들게 된다.
{ type: 'INCREMENT', diff: 2 } 그러면, 나중에 diff 값을 참고해서 기존값에 2를 + 하게 된다.

리듀서를 통해 상태 변화 시키기


action객체를 받으면 전달받은 action의 타입에 따라 어떻게 상태를 업데이트 해줘야 할지 정의를 해줘야 하는데, 이러한 업데이트 로직을 정의하는 함수를 리듀서라고 한다.
예를들어 { type: 'INCREMENT' }라는 action의 객체를 받으면 숫자를 + 해주고,
{ type: 'DECREMENT' }라는 aciton의 객체를 받으면 숫자를 - 작업을 리듀서에서 하게된다.
리듀서는 두가지 파라미터를 받는다.

  • state: 현재상태
  • action: 액션객체

이 두가지 파라미터를 참조하여, 새로운 상태객체를 만들어 이를 반환한다.

상태변화시 구독하고 있는 컴포넌트에게 알리기


상태 변화가 생기면 이전에 컴포넌트가 스토어에게 구독할 당시 줬던 listener가 호출되며, 이를 통해 컴포넌트는 새로운 상태를 받게되여 리렌더링 하게 된다.

정리

기존에는 부모에서 자식의 자식까지 상태가 흘러서 도달했는데, 리덕스를 사용하면 스토어를 사용하여 상태를 컴포넌트 바깥에 두고, 상태를 업데이트 할수 있게 된다. 여러 컴포넌트들은 스토어를 구독하고 추후 스토어의 상태값이 변경이 되면 전달받은 함수를 호출하여 리렌더링이 된다.
스토어는 어떤 컴포넌트에서 변화가 생길시 dispatch를 통해서 action객체를 전달받게 되는데,
이 전달받은 action객체가 속해 있는 리듀서를 살펴보게 된다. 그 리듀서는 action객체 파라미터를 받아 새로운 상태객체를 만들어서 반환한다.

참고: https://velopert.com/3528

profile
숲을보는 개발자

1개의 댓글

comment-user-thumbnail
2021년 9월 15일

그러게요

답글 달기