Skip to content

Latest commit

 

History

History
211 lines (159 loc) · 6.99 KB

3주차_류지예_개인.md

File metadata and controls

211 lines (159 loc) · 6.99 KB

Vanilla Store와 React Store의 차이점과 내부 구현

0. 들어가며

일반적으로 상태 관리 라이브러리는 React에 종속되어 있습니다.

하지만 Zustand는 다음과 같이

  • React에 독립적인 Vanilla Store 방식
  • React에 최적화된 React Store 방식

상태 관리를 위해 두 가지 다른 접근 방식을 제공합니다.

// 1. Vanilla Store: 순수 JavaScript 기반 
import { createStore } from 'zustand/vanilla'
const vanillaStore = createStore((set) => ({/*...*/}))

// 2. React Store: React Hook 기반
import { create } from 'zustand'
const useStore = create((set) => ({/*...*/}))

이 두 방식은 어떤 차이가 있으며, 내부적으로는 어떻게 구현되어 있을까요? 코드를 자세히 살펴보며 이해해보겠습니다.

1. Vanilla Store: 코어 구현

Vanilla Store는 Zustand의 핵심 구현체입니다. vanilla.ts를 보면, 프레임워크에 독립적인 상태 관리 메커니즘을 제공합니다.

const createStoreImpl: CreateStoreImpl = (createState) => {
  let state: TState
  // Set 자료구조를 사용한 구독자 관리
  const listeners = new Set<Listener>()

  const setState: StoreApi<TState>['setState'] = (partial, replace) => {
    // 함수형 업데이트 지원
    const nextState = typeof partial === 'function' 
      ? partial(state)
      : partial
    
    // 실제 변경이 있을 때만 업데이트 수행
    if (!Object.is(nextState, state)) {
      const previousState = state
      // 불변성 보장을 위한 상태 복사
      state = replace 
        ? (nextState as TState)
        : Object.assign({}, state, nextState)
      
      // 모든 구독자에게 변경 알림
      listeners.forEach((listener) => listener(state, previousState))
    }
  }

  // 현재 상태 반환
  const getState: StoreApi<TState>['getState'] = () => state

  // 구독 시스템
  const subscribe: StoreApi<TState>['subscribe'] = (listener) => {
    listeners.add(listener)
    // 구독 해제 함수 반환
    return () => listeners.delete(listener)
  }

  const api = { setState, getState, subscribe }
  state = createState(setState, getState, api)

  return api
}

이 구현의 핵심은 상태 변경과 구독 시스템의 조화에 있습니다.

  • setState : 상태 변경을 처리하면서 불변성을 보장
  • 구독 시스템 : Set 자료구조를 통해 효율적인 리스너 관리를 수행합니다.

2. React Store: Vanilla Store의 확장

React Store는 앞서 살펴본 Vanilla Store의 코어 기능을 기반으로 React에 최적화된 형태로 확장할 목적으로 만들어졌습니다.

주요 핵심 사항은

  • React의 Hook 시스템과의 통합
  • 동시성 모드 대응

2-1. useStore Hook을 통한 React 통합

react.ts의 핵심 부분을 살펴보면 다음과 같습니다.

function useStore<TState, StateSlice>(
  api: ReadonlyStoreApi<TState>,
  selector: (state: TState) => StateSlice = identity as any,
) {
  // useSyncExternalStore: React 18의 동시성 모드를 위한 핵심 Hook
  const slice = React.useSyncExternalStore(
    api.subscribe,                     // Vanilla Store의 구독 시스템 활용
    () => selector(api.getState()),    // 선택자 패턴으로 필요한 상태만 구독
    () => selector(api.getInitialState()) // SSR을 위한 서버 상태 지원
  )
  // React DevTools에서의 디버깅 지원
  React.useDebugValue(slice)
  return slice
}

2-2. Store 생성과 확장

createImpl 함수를 통해 Vanilla Store를 React Store로 확장합니다.

const createImpl = <T>(createState: StateCreator<T, [], []>) => {
  // 1. Vanilla Store 인스턴스 생성
  const api = createStore(createState)
  
  // 2. useStore Hook과 결합
  const useBoundStore: any = (selector?: any) => useStore(api, selector)
  
  // 3. Vanilla Store의 모든 기능을 Hook에 결합
  // 이를 통해 하나의 인터페이스로 모든 기능 사용 가능
  Object.assign(useBoundStore, api)
  
  return useBoundStore
}
  1. 동시성 모드 안전성

    • useSyncExternalStore를 통한 외부 상태의 안전한 구독
    • React의 렌더링 사이클과 동기화된 상태 업데이트
  2. 선택적 구독 시스템

    • selector를 통해 컴포넌트가 필요로 하는 상태만 구독
    • 불필요한 리렌더링 방지
  3. 통합된 개발 경험

    • React DevTools 지원으로 상태 디버깅 용이
    • SSR 환경 고려한 설계

3. 두 스토어의 핵심 차이점

3-1. API 설계 철학

두 스토어는 같은 기능을 제공하면서도, 각자의 환경에 맞는 다른 접근 방식을 취합니다.

// Vanilla Store - 명령형 API
const store = createStore((set) => ({
  count: 0,
  increment: () => set(state => ({ count: state.count + 1 }))
}))

// 명시적인 상태 접근과 변경
const count = store.getState().count
store.setState({ count: count + 1 })
// React Store - 선언적 API
const useStore = create((set) => ({
  count: 0,
  increment: () => set(state => ({ count: state.count + 1 }))
}))

// 컴포넌트 내부에서의 자연스러운 사용
function Counter() {
  const { count, increment } = useStore()
  return <button onClick={increment}>{count}</button>
}
  • Vanilla: 명령형으로 상태와 메서드에 직접 접근
  • React: Hook을 통한 선언적 상태 관리와 자동 구독

3-2. 상태 구독 시스템

상태 변화를 감지하고 반영하는 방식에서도 큰 차이를 보입니다:

// Vanilla Store의 구독 시스템
function VanillaExample() {
  // 명시적인 구독 설정
  const unsubscribe = store.subscribe((state) => {
    console.log('New state:', state)
  })

  // 구독 해제를 직접 관리해야 함
  return () => unsubscribe()
}
// React Store의 자동화된 구독 관리
function ReactExample() {
  // Hook이 자동으로 구독 설정 및 해제
  const state = useStore()
  
  // 컴포넌트의 생명주기와 함께 관리됨
  return <div>{state.value}</div>
}

이러한 차이는 각각의 장점을 가집니다:

  • Vanilla Store: 세밀한 제어와 커스터마이징 가능
  • React Store: React의 선언적 패러다임과 완벽한 통합

4. 결론

fiber-traversal

참고 자료