나만의 무기 만들기 'Wouldyouguess?' 프로젝트에서 우리는 React 상태를 Zustand라는 라이브러리를 통해 관리를 하기로 결정했다.
상태(State)는 무엇인가?
- React에서 상태(State)는 component 안에서 관리되는 것
- 자식 컴포넌트 간의 직접적인 데이터 전달이 불가능
- 자식 컴포넌트 간의 데이터를 주고 받을 때는 상태를 관리하는 부모 컴포넌트를 통해서 주고 받음
- 자식이 많아진다면 상태관리가 매우 복잡해짐
- 상태를 관리하는 상위(부모) 컴포넌트에서 계속 데이터를 내려받아야 함(Props drilling 이슈)
* Props drilling Issue
장점
1. 명시적인 값의 사용 : 각 컴포넌트에서 어떤 프로퍼티를 받아 사용하는지를 확인할 수 있어 코드의 의도를 명확하게 파악 가능
2. 값 추적 용이성 : 값이 어떤 컴포넌트를 거쳐 전달되는지 알 수 있으므로 버그를 디버깅하거나 코드를 변경할 때 편리함
3. 코드 변경 파악 용이성 : 데이터의 흐름을 명확하게 파악할 수 있으므로 변경 사항에 따른 영향을 사전에 예측하고 관리 가능
단점
1. 프로퍼티 데이터 형식 변경의 불편함 : 데이터 형식을 변경해야 하는 경우, 컴포넌트 계층 전체에서 업데이트가 필요함
2. 중간 컴포넌트에 불필요한 프로퍼티 전달 : 중간 컴포넌트를 통해 불필요한 프로퍼티가 전달될 수 있음(복잡성 증대)
3. 누락된 프로퍼티 인지의 어려움 : 타켓 컴포넌트에 전달되지 않은 상황을 인지하기 어려울 수 있어 잠재적인 문제 발생 우려
4. 프로퍼티 이름 변경 추적의 어려움 : 프로퍼티 이름이 계층에서 변경되면 해당 값을 추적하고 업데이트하는 것이 어려움
React 상태 관리 라이브러리
1. 리액트 리덕스(React-redux)
Redux 는 예측 가능한 상태 컨테이너로 자바스크립트 기반의 프레임 워크에서 모두 쓸 수 있지만, (Angular, Vue, React 등) 리액트에서는 React-Redux 라이브러리를 사용해야 state 와 props 가 연동됩니다.
React-Redux 는 React 용 동식 Redux UI 바인딩 라이브러리로 사용하려면 React-Redux 와 Redux 모두 설치해야 합니다.
1. 전역 상태 관리 : Store를 통해 전역으로 상태를 관리해 여러 컴포넌트를 거치지 않고 바로 데이터를 전달할 수 있음
2. UI Binding 라이브러리 : Provider 컴포넌트, useSelector, useDispatch 같은 리액트 전용 훅을 제공
- createStore : Store를 생성
- combineReducer : 여러 개의 slice를 결합하여 하나의 root reducer를 만듬
- Provider : React App 전체에 제공할 Store를 주입하는 컴포넌트
- useSelector : Store의 state에 접근하는 hook(useState의 state처럼 활용)
- useDispatch : action을 reducer로 보내는 역할(useState의 setState처럼 활용)
Ducks 패턴 : Redux 구성 요소인 Action Type, Action, Reducer 셋을 하나의 파일 안에서 관리하는 패턴
2. 리코일(Recoil)
리액트가 돌아가는 환경인 자바스크립트(JavaScript)는 싱글 쓰레드로 동작하기 때문에 렌더링이 끝날때까지 사용자와의 상호작용이 불가했다.(리액트 16버전에서 해결)
동시성(Concurrent) 측면에서 기존의 상태관리 라이브러리들은 하나의 상태가 변화할 때마다 관리되는 모든 상태를 가져와야 한다.
리코일은 상태와 컴포넌트가 직교하기 때문에 O(1)로 상태를 관리할 수 있다.
아톰(Atoms)은 상태 단위이며, 업데이트와 구독이 가능하다. 아톰이 업데이트됐을 때 해당 아톰을 구독하고 있는 컴포넌트들은 리렌더링이 일어나게 된다.
단점으로 전역 상태를 아톰으로 관리하고 컴포넌트에서도 바로 아톰을 구독해서 업데이트를 받다 보니, 해당 아톰을 여러 군데에서 사용하면 사이드 이펙트가 발생할 수 있다.
*사이드 이펙트
- 함수 내에서 동일한 입력에 대해 같은 결과를 보장할 수 없도록 하는 것
- 함수 실행 과정에서 외부의 값을 변경하는 것
- 멱등성을 가지지 않음
*멱등성 : 연산을 여러 번 적용하더라도 결과가 처음과 달라지지 않는 성질
- 서버에서 API 호출
- 함수 외부 값 변경
- 쿠키 및 브라우저 스토리지 이용
- 브라우저 직접 변경 (document, window)
- 시간 관련 함수 사용 (setTimeOut, setInterval)
// atom 생성
import {atom} from "recoil";
const anyState = atom(
{
key: "any"
default : 기본값
})
----------------------------------------------------------------------------------
// useRecoilValue : atom을 불러오는 함수
import { useRecoilValue } from "recoil";
const myState = useRecoilValue(anyState);
console.log(myState); // default에 설정한 값 출력
useRecoilState : useState와 동일하게 활용
import {useRecoilState} from "recoil";
const [myState, setMyState] = useRecoilState(anyState);
----------------------------------------------------------------------------------
/* Selector 개념
원본의 데이터 값은 유지하면서 변경된 값을 리턴할 수 있음 */
import { atom, selector } from "recoil";
const anyState = atom(
{
key: "any"
default : 0
})
const anySelector = selector({
key: "any2",
get: ({ get }) => {
const anyValue = get(anyState); // get으로 State에 저장된 값을 가져 올 수 있다.
return anyValue + 1;
},
set: ({ set }, newValue) => {
const anyValue2 = Number(newValue) + 10;
set(anyState, anyValue2); // anyState에 anyValue2를 Set해라
}
})
import { useRecoilState } from "recoil";
import { anySelector } from "./atoms";
const [anys, setAnys] = useRecoilState(anySelector);
// Selector를 대상으로 useRecoilState 함수 역시 사용할 수 있다.
const onChangeAnys = () => {
setAnys(5);
}
[출처]https://velog.io/@pjj186/Recoil-Atom-Selector
3. 주스탠드(Zustand)
주스탠드는 발행/구독 모델(publish/subscribe)을 기반으로 이루어져 있다.
스토어의 상태 변경이 일어날 때 리스너 함수를 모아 두었다가(subscribe), 상태가 변경되었을 때 등록된 리스너에게 상태가 변경되었다고 알려준다.(publish)
스토어를 생성하는 함수를 호출할 때 클로저를 활용한다.
그렇기 때문에 상태의 변경, 조회, 구독 등의 인터페이스를 통해서만 스토어를 다루고 실제 상태는 애플리케이션의 생명 주기 처음부터 끝까지 의도치 않게 변경되는 것을 막을 수 있다.
장점
- 한 개의 중앙에 집중된 형식의 Store 구조를 활용해, 상태를 정의하고 사용하는 방법이 단순
- Context API를 사용할 때와 달리 상태 변경 시 불필요한 리랜더링을 일으키지 않도록 제어하기 쉬움
- React에 직접적으로 의존하지 않기 때문에 자주 바뀌는 상태를 직접 제어할 수 있는 방법도 제공한다. (Transient Update)
Context API를 사용하지 않는 이유?
1. Context API를 사용하면 종종 Context.Provider와 Context.Consumer를 사용해야 하고, 별도의 로직을 작성해합니다.
-> 이런 보일러플레이트(Boilerplate) 코드를 줄이기 위해 context API를 사용하지 않습니다.
2. Context API는 상태 변경이 있을 때, 사용하는 모든 컴포넌트를 리렌더링합니다.
-> Zustand는 Context API 사용하지 않아서 상태 변경에 따른 렌더링을 더 효율적으로 관리할 수 있습니다.
3. 모든 상태(데이터)를 한 군데에서 관리하고, 특정 '액션'을 실행해서 상태를 변경하는 방식으로 돌아간다.
-> Context API보다 더 단순하게 상태를 공유하고 바꿀 수 있어서, 코딩이 간단하다.
[출처]Context API 없는 상태관리 Zustand
최종 결정
나만의 무기 만들기는 4주라는 짧은 기간이 주어졌기 때문에 러닝 커브가 낮고, 효율적으로 렌더링을 할 수 있는 Zustand 라이브러리를 선택하게 됐다.
'크래프톤 정글 - TIL' 카테고리의 다른 글
크래프톤 정글 키워드 복습하기(3) (2) | 2024.09.09 |
---|---|
크래프톤 정글 5기 TIL - 나만의 무기 만들기 8(Web Worker) (0) | 2024.08.08 |
크래프톤 정글 5기 TIL - 나만의 무기 만들기 6(Canvas - Laser pen) (0) | 2024.08.06 |
크래프톤 정글 5기 TIL - 나만의 무기 만들기 5(OpenVidu) (1) | 2024.07.12 |
크래프톤 정글 5기 TIL - 나만의 무기 만들기 4(WebRTC 구현 및 궁금증) (0) | 2024.07.07 |