Redux

zzwwoonn·2022년 6월 8일
0

React

목록 보기
18/23

리덕스(Redux)

리덕스는 리액트에서 특히 가장 많이 사용되는 상태관리 라이브러리이다. 리덕스를 사용하면 프로젝트 내에 있는 컴포넌트들의 상태 관련 로직들을 다른 파일들로 분리시켜서 더욱 효율적으로 관리 할 수 있다. 또한 글로벌 상태 관리도 손쉽게 할 수 있다.

사람들이 잘못 알고 있는 사실이 있다 (나도 그랬고)
Redux 공식 홈페이지에도 나와있는 내용인데, Redux는 JavaScript로 만들어진 애플리케이션들의 state(상태)를 관리하기 위한 라이브러리이다.
=> Redux는 React 에서 쓰는, 만드는, 종속된 라이브러리가 아니다.

흔히들 React에서 사용하는 Redux는 React Redux라는 기술이다. React Redux는 Redux 개발팀에서 공식적(Official)으로 개발한 React 전용 Redux이다.

React Redux 와 Vanilla JavaScript에서의 Redux 사용 실습은 다음 포스팅에서 해보고 여기서는 리덕스의 개념과 간단한 동작원리에 대해 알아보자

리덕스를 사용하는 이유

리액트에서 프로젝트(웹 애플리케이션)를 만들 때, 기본적으로 App.js => 루트 컴포넌트에서 상태를 관리한다.

대부분의 프로젝트에서 대부분의 작업을 할 때 부모 컴포넌트가 중간자 역할을 한다. (App.js 에서 라우팅해주고, 프롭스 전달, 모든 자식 노드의 출발점 역할)

컴포넌트끼리 직접 데이터를 주고 받는(상태를 관리하는, 소통하는) 방법이 있긴 하지만, 코드가 굉장히 꼬인다.
( Refri Bank => 리팩토링 해보려고 코드를 깠는데 도저히 엄두가 안나더라..)

=> 당연히 가능은 하다. (ref 사용)

Ref는 render 메서드에서 생성된 DOM 노드나 React 엘리먼트에 접근하는 방법을 제공한다.

일반적으로는 부모 컴포넌트에서 모든걸 관리하고 아래로 내려주는 방식이기 때문에 이는 매우 직관적이고(한 눈에 모든 걸 볼 수 있다) 무엇보다도 관리하기 편하다.

그런데 문제는 앱(웹 애플리케이션)의 규모가 커졌을 때 이다.

앱의 크기가 커지면 보여지는 컴포넌트의 개수가 늘어나고, 다루는 데이터(props, state)도 늘어나고, 그 데이터를 업데이트 하는 함수들도 늘어난다. 결론적으로 App.js 의 코드가 길어지게 된다.

< 위에서 언급한 Refri Bank의 App.js 코드 일부 >

홀리 쒯

App.js의 코드가 말도 안되게 길어지고 이에 따라 유지보수가 힘들어진다 (리팩토링 해보려고 코드를 까봤으나 한숨부터 나오는)

리덕스 구성요소

  • Store: 애플리케이션의 state(상태값)를 저장하는 곳. Store 내에 Reducer 함수가 존재함.

  • Dispatch: Reducer 함수에게 Action을 송신하는 역할.

  • Action: 유저의 움직임로부터 발생된 이벤트(버튼 클릭, input에 데이터 입력 등)와 API로부터의 데이터 수신 등 state(상태)를 갱신하기 위한 정보를 담은 object(객체). Dispatch 함수에 의해 Action을 Reducer에게 보낼 수 있다.

  • Reducer: Dispatch 통해 받은 Action을 토대로, state를 modify, 갱신, 변경할 수 있는 유일한 함수.

  • View: 애플리케이션의 유저 인터페이스(UI)

리덕스를 사용하게 되면

리덕스에 대해서 많은 설명이 나와있지만 가장 와닿는 설명은

리덕스를 사용하면 상태값을 컴포넌트에 종속시키지 않고, 상태 관리를 컴포넌트의 바깥에서 관리할 수 있게 된다.

간단한 로직은 다음과 같다.

  1. 스토어 생성(createStore)
const countStore = createStore(reducer)
  1. 컴포넌트의 스토어 구독(subscribe)
    컴포넌트는 스토어에 구독을 한다. 구독을 하는 과정에서 특정 함수가 스토어에 전달이 된다. 만약 스토어의 상태값에 변동이 생긴다면 전달 받았던 함수(listener)를 호출해준다.
countStore.subscribe(stateChanged)
  1. 스토어에게 상태 변경 알림
    컴포넌트에서 어떤 이벤트가 발생해서 상태를 변화 할 일이 생겼다. 이 때 dispatch라는 함수를 통해 액션을 스토어에게 전달한다. 액션은 상태에 변화를일으킬 때 참조 할 수 있는 객체이다. 액션 객체는 type 이라는 값을 가지고 있어야 한다.
countStore.dispatch({type:"MINUS"})

액션 객체를 받으면 전달받은 액션의 타입(어떤 동작을 하면 되는지?)에 따라 어떻게 상태를 업데이트해주면 되는지 정의를 해줘야 하는데, 이 때의 업데이트 로직을 정의하는 함수를 리듀서(Reducer)라고 한다.

리듀서 함수는 두 가지의 파라미터를 받는다.

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

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

리덕스 스토어의 상태값의 변화가 생겼다면 구독하고 있던 컴포넌트에게 알려준다. 이 때 listener가 호출된다. listener는 이전에 컴포넌트가 스토어를 구독할 때(subscribe) 전달해줬었던 함수이다.

이를 통해 컴포넌트는 새로운 상태를 받게 되고, 이에 따라 리렌더링의 과정을 거친다.

subscribe(상태 변화 감지요청, 구독) -> action(이벤트 발생으로 인한 상태 변화) -> dispatch(상태 업데이트, store에 action 전달) -> Store(state, 상태값 갱신) -> listener(상태 변화 알림) -> 컴포넌트 리렌더링

Redux 3 원칙

  1. Single source of truth
    애플리케이션 내에 Store는 반드시 1개 뿐이여야 한다.

  2. State is read-only
    state를 직접 변경해서는 안된다. state를 변화시키는 유일한 방법은 Action을 Reducer에 dispatch(송신, 전달)하는 방법 뿐이다. 즉, state의 변경은 Reducer만 할 수 있다. Reducer 이외의 공간에서는 state는 읽기 전용인 것이다.

  3. Changes are made with pure functions
    Reducer는 순수 함수여야만 한다. Reducer 함수는 parameter로 기존의 state와 Action을 받는데, Reducer 함수는 기존의 state를 직접 변경하지 않고, 새로운 state object(객체)를 작성해서 return해야한다.

핵심

기존의 방법으로는 App.js - 부모 컴포넌트에서 자식 컴포넌트까지 계속 타고타고 들어가면서 데이터(상태값 등)가 전달 되었었는데, 리덕스를 사용하면 리덕스 스토어를 컴포넌트 외부에 두고 App.js가 하던 중간자 역할을 스토어가 대신하게 된다.

스토어는 상태를 업데이트 하거나, 새로운 상태를 전달 받는다. 따라서 여러 컴포넌트를 건너 건너 데이터를 받아오는게 아니라 아무리 depth가 깊은 자식의 자식의 자식의~~~ 컴포넌트더라도 부모 컴포넌트에게 직접 받는 것과 같이 상태값을 골라서 props를 받아올 수 있게 된다.

간단하게 정리해보면

  1. redux의 핵심 구조는 store, reducer가 있고 이 둘을 이용하면 reducer 내부의 데이터 흐름을 일방향화하여 관리를 유용하게 할 수 있다.
  2. redux 내부의 데이터는 store에 종속되고, 반드시 redux logic에 의해서만 변화한다.
  3. redux 내부에서의 데이터 변화 보다는, redux와 action을 연결하여 외부적으로 연결하는 방식을 사용한다.
  4. action은 dispatch를 통해 연결할 수 있고, subscribe를 통해 reducer가 호출되는 시점에서 상태값을 얻거나(getState) 화면에 구현하는 메서드 등을 구성할 수 있다.
  5. dispatch, subscribe, getState 등은 모두 Store가 제공하는 메서드이다.

바로 다음 포스팅에서는 redux 를 간단하게 사용(실습)해보고 이전에 만들어두었던 리액트 실습 페이지(movieWeb , axios 등등) 를 redux를 사용하여 리팩토링 해보자.

useReducer Hook

Redux의 기능 대부분을 useReducer 와 context API로 구현 가능하다. 하지만 프로젝트의 규모가 크다면 useReducer와 context api 조합의 한계 때문에 비동기적인 작업이 불편하다고 한다.

참고 자료
Three Principles (Redux 공식 문서), GitBook, VELOPERT.LOG, gyrbs22.log

0개의 댓글