TIR: 리액트 딥다이브 (5) 상태관리

Lumpen·2024년 12월 22일
1

React

목록 보기
21/26

상태

상태는 어떤 의미를 지닌 값으로
애플리케이션의 시나리오에 따라 지속적으로 변경될 수 있는 값

리액트의 상태 관리

리액트는 단순히 UI 를 만들기 위한 라이브러리로 그 이상의 기능을 제공하진 않는다

flux 패턴

순수 리액트로 전역 상태 관리를 한다면 Context API 가 있다
상태 관리보다는 상태 주입이라고 볼 수 있다
flux 패턴 등장 이전의 mvc 패턴은 모델과 뷰가 많아질수록 복잡도가 증가한다
페이스북 팀은 이러한 문제의 원인을 양방향 데이터 바인딩에 있다고 생각했다
뷰와 모델이 서로 변경할 수 있는 양방향 바인딩은 작성하긴 쉽지만 이해하기 어려운 코드가 된다

페이스북 팀이 단방향 데이터 흐름을 제안한 것이 flux 패턴의 시작이다
action -> dispatcher -> model -> view

  • 액션: 작업을 처리할 액션과 액션 발생 시 함께 포함시킬 데이터를 의미한다
    액션 타입과 데이터를 정의해 디스패처로 보낸다
  • 디스패처: 액션을 스토어에 보내는 역할을 한다 콜백 함수 형태로 앞서 액션이 정의한 타입과 데이터를 모두 스토어로 보낸다
  • 스토어: 실제 상태에 따른 값과 상태를 변경할 수 있는 메서드를 가지고 있다
    액션의 타입에 따라 어떻게 변경할지 정의되어 있다
  • 뷰: 리액트의 컴포넌트에 해당하는 부분으로 스토어에서 만들어진 데이터를 가져와 화면을 렌더링한다 뷰에서 업데이트 되는 상태는 뷰에서 액션을 호출하는 구조로 구성된다

단방향 데이터 흐름에는 불편함도 존재한다
사용자 입력에 따라 데이터를 갱신하고 화면을 어떻게 업데이트 하는지 코드로 작성해야 하므로 코드의 양이 많아진다
그러나 데이터 흐름은 액션이라는 한 방향으로 줄어들기 때문에 데이터 흐름을 추적하기 쉽고 코드를 이해하기 수월해진다

리액트 또한 방향 데이터 바인딩을 기반한 라이브러리이기 때문에 flux 패턴과 궁합이 잘 맞는다

리덕스

리덕스 또한 최초에는 flux 구조를 구현하기 위해 만들어진 라이브러리 중 하나였다
특별한 것은 여기에 Elm 아키텍처를 도입했다는 것이다
Elm 은 웹페이지를 선언적으로 작성하기 위한 언어다
model, update, view 세가지가 Elm 아키텍처의 핵심이다

  • model: 애플리케이션의 상태
  • view: 모델을 표현하는 HTML
  • update: 모델을 수정하는 방식

flux 와 마찬가지로 데이터 흐름을 세 가지로 분류하고
이를 단방향으로 강제해 웹 애플리케이션의 상태를 안정적으로 관리하고자 했다

리덕스는 하나의 상태 객체를 스토어에 저장해 두고 이 객체를 업데이트하는 작업을 디스패치해 업데이트를 수행한다
이러한 작업은 reducer 함수로 발생시킬 수 있는데 이 함수의 실행은 웹 애플리케이션 상태에 대한 복사본을 반환한 다음 애플리케이션에 새롭게 만들어진 상태를 전파하게 된다

하나의 글로벌 상태 객체를 통해 상태를 하위 컴포넌트에 전파할 수 있기 때문에
props drilling 문제를 해결할 수 있었고
스토어가 필요한 컴포넌트라면 connect 로 스토어에 바로 접근할 수 있다

리덕스는 하나의 상태를 바꾸기 위해 발생하는 보일러 플레이트가 너무 크기 때문에
불편한 부분도 있지만 현재는 많이 간소화 되었다

Context API 와 useContext

리액트에서 전역 상태를 하위 컴포넌트에 주입할 수 있는 새로운 Context API 를 출시했다 props 로 상태를 넘겨주지 않더라도 Context API 를 사용하면 원하는 곳에서 주입된 상태를 사용할 수 있게 되었다

기존의 context 인 getChildContext() 에서는 상위 컴포넌트 렌더링 시 하위 컴포넌트에서도 불필요하게 렌더링이 일어난다는 점과 사용하는 모든 곳에서 context 를 인수로 받아야 하기 때문에 결합도가 높아지는 등의 단점이 있었다

그럼에도 Context API 는 상태 관리보다는 주입을 도와주는 기능이며
렌더링을 막아주는 기능 또한 존재하지 않으므로 사용 시 유의해야 한다

훅의 탄생, React Query 와 SWR

리액트의 훅, 특히 useState() 의 등장으로 이전에 볼 수 없던 방식의 상태 관리가 등장했다
바로 Rect Qeury 와 SWR 이다

두 라이브러리는 모두 외부에서 데이터를 불러오는 fetch 를 관리하는 데 특화된 라이브러리지만 API 호출에 대한 상태를 관리하고 있기 때문에 HTTp 요청에 특화된 상태 관리 라이브러리라고 할 수 있다

useState 와 useReducer

useState 의 등장으로 여러 컴포넌트에서 동일한 인터페이스의 상태를 생성하고
관리할 수 있게 되었다
사용자 정의 훅을 만들어 사용하면 같은 상태를 여러 곳에서 함께 사용할 수 있다

useState 와 useReducer 를 기반으로 하는 사용자 정의 훅의 한계는
훅을 사용할 때마다 컴포넌트별로 초기화되므로 서로 다른 상태를 가질 수밖에 없다는 것
이러한 상태를 지역상태라고 하며 지역 상태는 해당 컴포넌트 내에서만 유효하다

이러한 지역 상태를 전역 상태로 만들어 동일한 값을 갖게 하기 위해서는
상태를 공유하는 가장 상위의 컴포넌트 부모에서 상태를 관리하고
props 로 상태를 공유하는 방법이 있다

상태를 바깥으로 분리

useState 가 리액트 클로저가 아닌 다른 자바스크립트 실행 문맥에서 관리된다면
전역으로 관리할 수 있을 것처럼 보인다
하지만 이러한 방식으로는 리액트의 렌더링을 일으킬 수 없다

지역상태를 벗어나는 새로운 상태 만들기
상태를 업데이트하는 것뿐만 아니라 업데이트된 상태를 반영시키기 위한 렌더링이 필요하다
상태 업데이트를 위해서는 useState 나 useReducer 의 두 번째 인수가 실행되어야 한다

함수 외부에서 상태를 참조하고 렌더링까지 자연스럽게 일어나기 위해서는 다음과 같은 조건을 만족해야 한다
1. 컴포넌트 외부 어딘가에 상태를 두고 여러 컴포넌트에서 같이 쓸 수 있어야 한다
2. 외부 상태를 사용하는 컴포넌트는 모두 상태 변화를 알아챌 수 있어야 하고 상태 변화 시마다 사용하는 모든 컴포넌트에서 리렌더링이 일어나야 한다
3. 상태가 원시값이 아닌 개체인 경우 그 객체에 감지하지 않는 값이 변한다고 해도 리렌더링이 발생해서는 안된다

2번의 조건을 만족하기 위해서는 sotre 의 값이 변할 때마다 이를 알리는 callback 을 실행해야 하고 callback 을 등록하기 위한 subscribe 함수가 필요하다

profile
떠돌이 생활을 하는. 실업자, 부랑 생활을 하는

0개의 댓글