🍲Boilerplate

Global Context

전역적으로 관리되는 컨텍스트 입니다.

state 와 handler, 그리고 사용되는 web-storage 등의 값이 포함 되어 있습니다.

web storage

web-storage 는 자주 사용되는 api 중에 하나입니다.

하지만, localStorage 는 리엑트에서 다양한 불편함을 가지고 있는데, 아래와 같습니다.

  • 데이터에 접근할 때 JSON.parse 와 stringfy 가 수반되어야 합니다.

  • 데이터의 수정사항을 react 의 생명주기에서 감지 할 수 없습니다.(데이터 수정으로 리랜더링이 촉발되지 않습니다.)

위와 같은 불편함을 해결하고자 만들어진 hook 이 useSyncWebStorage 입니다.

전역 컨텍스트에서는 위 훅을 사용해 아래 처럼 사용하고 있습니다.

사용 방법

// src/utils/web-storage/todo.ts
import { SyncedStorageFactory } from './helper/synced-storage-factory';

export type TodoType = {
  text: string;
};

export const {
  connector: todoConnector, //
  storage: todoStorage,
} = SyncedStorageFactory.createLocal<TodoType[]>('todo');

+ useSyncWebStorage 는 어떻게 구현 되었나요?

useSyncWebStorage 를 사용하기 위해선 3개의 모듈이 필요합니다.

  • useSyncExternalStore: react 18 에 나온 hook 이며 구독, 알림 패턴을 통해 외부 상태와 리액트 생명주기를 연결시켜주는 훅입니다. 여기서 해당 hook 은 구독을 하는 입장에 해당합니다.

  • Storage: 위의 훅에 연결될 Storage 주체입니다 useSyncExternalStore 에게 알림을 수행하는 입장에 해당합니다.

  • Connector: 알림을 관리하고, useSyncExternalStore 와 Storage 를 연결 시켜주는 역할입니다.

useSyncExternalStore

useSyncExternalStore 은 크게 2가지 함수를 사용자에게 전달받아 구독 기능을 수행합니다. 다음은 useSyncExternalStore 입장에서 구독기능을 수행하는 방법에 대해 설명합니다.

subscribe

인자로 받는 subscribe 함수를 통하여 listener 함수를 인자로 사용자에게 전달해 줍니다. listerner 함수는 구독하고 있는 모듈에게 알림을 주는 '알림 함수' 에 해당하며, 함수가 실행 될시, 리랜더링을 수행합니다.

subscribe 함수의 return 값으로 '정리 함수' 를 전달받습니다. listener 를 통해 리랜더링이 되고 나면, 정리 함수를 실행시키고, 사용자에게 새로운 알림 함수를 전달해 줍니다.

getSnapShot

listener 함수가 실행되서 리랜더링이 발생한 시점의 랜더링 할 데이터를 사용자에게 전달 받습니다.

+getServerSnapShot

server-side 시점의 데이터를 사용자에게 전달 받습니다

exmple

// example

type Listener = () => void
let myListenr: Listener | null = null

let count = 0
const increase = () => { 
    count += 1;
    if (myListener) myListener()
}

const subscribe = (listener: Listener) => {
    myListener = listener;    
    return () => { myListener = null } // clean-up 함수
}

const getSnapShot = () => count

const Component = () => {
    const count = useSyncExternalStore(subscribe, getSnapShot);
    return (
        <div>
            <button onClick={increase}>increase</button>
            <p>{count}</p> 
        </div>
    )
}

참고

우리 프로젝트에서는?

useSyncExternalStore 로부터 전달받은 알림함수를 관리하는 connector 모듈과 함께 사용을 합니다.

// src/hooks/useSyncWebStorage.ts
import { useSyncExternalStore } from 'react';

import { ReactSyncConnector } from '@/utils/react/react-sync-connector';

export const useSyncWebStorage = <T>(connector: ReactSyncConnector<T>) => {
  return useSyncExternalStore(
    connector.subscribe, 
    connector.getSnapshot,
    connector.getServerSnapShot,
  );
};

useGlobalHandler, useGlobalState

전역 context 에는 위와 같은 훅이 존재하는데요. 이는 비즈니스로직의 추상화를 권장하기 위해 만들어진 hook 입니다. 사용되는 범위로 훅을 분리하는것도 좋지만, 주제별 state 와 logic 을 가지고 있는 hooks 를 만드는것도 좋은 방법입니다.

example

범위로 구분하기

  • useGlobalContext

    • useGlobalState: 앱 전역에서 사용되는 state 와 state setter 로직

    • useGlobalHandler: 앱 전역에서 사용되는 handler 로직

주제로 구분하기

  • useGlobalContext

    • useLogin

    • useToast

Last updated