[번역 jotai] Signal이 뭐야? jotai-signal으로부터 jotai-uncontrolled, 이거이거 굉장한데

eaasurmind·2023년 3월 5일
1

번역

목록 보기
1/1

해당 글은 번역글로 jotai와 zustand를 개발한 daishi의 아티클을 번역한 글입니다.
원문: https://zenn.dev/dai_shi/articles/01813b22907dcf

Reactive Programming에서는 Signal이라는 개념이 있다던데 (사실 더 오래전부터 있었던 개념) SolidJS가 도입하면서 React Hooks와의 차이가 확연해진 것 같습니다.

'Signal'은 Solid의 reactivity(반응형) 기초가 되는 것입니다.여기에는 시간이 지남에 따라 변화하는 값이 포함되어 있으며 Signal 값을 변경하면 그것을 사용하는 모든 것이 자동으로 업데이트됩니다.

Observable이나 Stream 같은 것도 비슷할까요?

SolidJS의 포인트는 Signal을 사용하여 fine-grained reactivity를 실현하고 있다는 점이라고 생각합니다.
React처럼 Virtual DOM(DOM이 아니기 때문에 더 이상 부르지 않는 것 같지만 편의를 위해 VDOM이라고 표기하겠습니다.)을 사용하지 않고 국소적으로 DOM을 직접 갱신하기 때문에 퍼포먼스가 좋다고 합니다. Svelte가 선구자일까요?

Preact Signals

또한 최근에 출시된 것이 Preact Signals입니다.

https://preactjs.com/blog/introducing-signals/

Preact Signals도 쓰는 방법에 따라 다르지만 VDOM을 건너뛰고 직접 업데이트할 수 있다고 합니다. 쓰는 방법도 간단합니다.

const count = signal(0);

const Component = () => (
  <p>Value: {count}</p>
);

퍼포먼스에 관심이 쏠리고 있는 것 같은데, 사용법에도 주목할 만하다고 생각합니다.

그런데 Preact Signals를 React에서도 사용할 수 있는 모양입니다.

https://github.com/preactjs/signals/blob/main/packages/react/README.md#react-integration

단, React판의 경우는 VDOM 스킵은 이루어지지 않습니다. 하지만 render phase의 스킵은 이루어집니다. 조금 이야기가 복잡해지긴 하지만 React에서 렌더링은 render phase와 commit phase로 나눠지는데 전자는 좁은 의미의 렌더링으로 VDOM을 만드는 데까지이고 후자가 VDOM을 DOM에 적용하는 것을 의미합니다. 일반적으로 React에서는 전자의 render phase의 처리가 더 무거워 최적화의 여지가 많다고 합니다. 따라서 React 버전의 Preact Signals도 충분히 가치가 있을 것 같습니다.

여기서 @preactjs/signals-react 코드를 들여다보니

__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED as internals,

라고 되어 있었습니다. 직원이 아니기 때문에 잘리지는 않지만 예전에 Internals를 사용하고나서 코드를 사용할 수 없게 되는 경험을 했기 때문에 이 접근법은 피하고 싶습니다.

이해를 돕기 위한 참고:
__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED 객체에는 React 내부 기능과 상태가 포함되어 있습니다. 이러한 내부는 React 공개 API에서 사용하기 위한 것이 아닙니다.

jotai-signal

문득 jsx-transform을 만지면 비슷한 일을 할 수 있지 않을까 해서 시도했더니 잘 되었습니다.jotai-signal의 탄생입니다.

https://twitter.com/dai_shi/status/1569542040010260482?ref_src=twsrc%5Etfw%7Ctwcamp%5Etweetembed%7Ctwterm%5E1569542040010260482%7Ctwgr%5E5c62d207e2afe7deb78ff993ab1b5bdb90ccd7d7%7Ctwcon%5Es1_c10&ref_url=https%3A%2F%2Fembed.zenn.studio%2Ftweetzenn-embedded__e33a701243a2b

사용법의 일부입니다. jotai에서는 atom이 이미 상태의 정의로 존재하기 때문에 signal()로 감사주어 Signal화합니다.(참고로, 일반 signal은 signal 자체에 값이 포함될 수 있지만 jotai-signal의 경우 atom 자체에는 값이 포함되지 않습니다.)

/** @jsxImportSource jotai-signal */

import { atom, useAtom, useSetAtom } from 'jotai';
import { signal } from 'jotai-signal';

const countAtom = atom(0);

const CounterWithSignal = () => {
  return (
    <div>
      <h1>With signal(atom)</h1>
      Count: {signal(countAtom)} ({Math.random()})
    </div>
  );
};

첫줄 @jsxImportSource가 포인트입니다. 소스 코드 내부에서 쓰는 방법 대신에 tsconfig.json에 쓸 수도 있습니다.이 접근법도 일종의 우회법이긴 하지만 internals보다는 낫지 않을까 생각합니다.

props에도 사용할 수 있습니다.

https://twitter.com/dai_shi/status/1569648820551823360?ref_src=twsrc%5Etfw%7Ctwcamp%5Etweetembed%7Ctwterm%5E1569648820551823360%7Ctwgr%5E1da162c5f46abc9388ccb3382421974adda9c654%7Ctwcon%5Es1_c10&ref_url=https%3A%2F%2Fembed.zenn.studio%2Ftweetzenn-embedded__cffb23f2f8f9d

jotai-uncontrolled

jotai-signal와 @preactjs/signals-react가 같다고 생각할수도 있다고 생각합니다만, @preactjs/signals처럼 VDOM의 스킵은 할 수 없습니다.

어떻게든 안될까 생각하다가 react-hook-form의 register()가 생각났습니다. 즉, uncontrolled component로 만들면 VDOM 스킵이 가능할 것 같습니다. jotai-uncontrolled의 탄생입니다.

https://twitter.com/dai_shi/status/1570361010757959682?ref_src=twsrc%5Etfw%7Ctwcamp%5Etweetembed%7Ctwterm%5E1570361010757959682%7Ctwgr%5Ee06cd5eacfc9cdefd8b7f97d82a15d175eb0e675%7Ctwcon%5Es1_c10&ref_url=https%3A%2F%2Fembed.zenn.studio%2Ftweetzenn-embedded__500a689498e41

v0.1.0 로의 사용법은, ref={} 를 지정하는 것입니다.

import { atom, useAtom, useSetAtom } from 'jotai';
import { register } from 'jotai-uncontrolled';

const countAtom = atom(0);

const UncontrolledCounter = () => {
  return (
    <div>
      <h1>Uncontrolled</h1>
      Count: <span ref={register({ text: countAtom })} /> ({Math.random()})
    </div>
  );
};

특수한 사용법이긴 하지만 jotai-signal의 경우@jsxImportSource는 요구하지 않습니다.

그러자 어떤 사람이 새로운 API를 제안해 주었습니다.

https://twitter.com/grumd_osu/status/1570511600200843264?ref_src=twsrc%5Etfw%7Ctwcamp%5Etweetembed%7Ctwterm%5E1570511600200843264%7Ctwgr%5E15cc09df895f822f856b3d90d709b895571235c6%7Ctwcon%5Es1_c10&ref_url=https%3A%2F%2Fembed.zenn.studio%2Ftweetzenn-embedded__09c1ba4cdec1a

이 방법이라면 ref={}을 명시할 필요 없이 자연스러운 느낌입니다. 채용.

https://twitter.com/dai_shi/status/1570703789270646784?ref_src=twsrc%5Etfw%7Ctwcamp%5Etweetembed%7Ctwterm%5E1570703789270646784%7Ctwgr%5Ed2e9e3c2772d50a128a8f6ea2f9dcb2690b5b675%7Ctwcon%5Es1_c10&ref_url=https%3A%2F%2Fembed.zenn.studio%2Ftweetzenn-embedded__d3043d4ac233e

사용법은 span 대신 uncontrolled.span으로 하면 됩니다.

import { atom, useAtom, useSetAtom } from 'jotai';
import { uncontrolled } from 'jotai-uncontrolled';

const countAtom = atom(0);

const UncontrolledCounter = () => {
  return (
    <div>
      <h1>Uncontrolled</h1>
      Count: <uncontrolled.span>{countAtom}</uncontrolled.span> ({Math.random()}
      )
    </div>
  );
};

jotai-signal와 달리 signal()로 감싸줄 필요도 없습니다. (이것도 열심히 하면 atom 자체를 signal처럼 사용할 수 있을지도 모릅니다.)

참고로 TypeScript와 딱 맞아서 실수 없이 사용할 수 있을 것입니다. 게다가 이 구조는 사실 react-dom 이외의 render로 발전시킬 수도 있을 것 같습니다.가볍게 시도한 느낌으로는 react-three-fiber에서도 동작시켜보았습니다. react-native로도 사용할 수도 있을 것 같습니다.

비교

코드를 비교하면 이렇게 됩니다.

https://twitter.com/dai_shi/status/1570713352137064448?ref_src=twsrc%5Etfw%7Ctwcamp%5Etweetembed%7Ctwterm%5E1570713352137064448%7Ctwgr%5E6f7711dccc2fa4259fce4e24440e596d8d5e4ec1%7Ctwcon%5Es1_c10&ref_url=https%3A%2F%2Fembed.zenn.studio%2Ftweetzenn-embedded__6ed0d72f182ff

jotai-signal에 비해 jotai-uncontrolled는 "사용법이 단순해지고 버그는 줄어들고 퍼포먼스도 개선된다" 라는 꿈같은 라이브러리가 되었습니다.

단, jotai-uncontrolled는 VDOM을 건너뛰기 때문에 VDOM이기 때문에 할 수 있는 것은 일절 할 수 없습니다. 예를 들면 Suspense등이 있습니다. 대략적인 비교표입니다.

https://twitter.com/dai_shi/status/1570719014489292802?ref_src=twsrc%5Etfw%7Ctwcamp%5Etweetembed%7Ctwterm%5E1570719014489292802%7Ctwgr%5Ef885a8859245c8cfd0b41abd3d73e89e137700da%7Ctwcon%5Es1_c10&ref_url=https%3A%2F%2Fembed.zenn.studio%2Ftweetzenn-embedded__a279ff41b3f4b

'DOMonly'는 위와 같이 어쩌면 개선될지도 모릅니다.Immutability/Concurrency는 정확하지 않을 수 있습니다.

jotai-uncontrolled는 기능적으로는 뒤쳐저 보이지만 그래도 게임과 같은 앱이거나 Suspense의 필요성이 없고 성능 위주의 상황이라면 도움이 될 것 같습니다. 무엇보다 일반 jotai 라이브러리와 같이 사용하는 것이 가능합니다 (jotai-uncontrolled 뿐만 아니라 jotai-signal도 마찬가지입니다). 필요한 컴포넌트에만 uncontrolled를 사용하시면 됩니다. 사용 경험이 좋다면 퍼포먼스 중시가 아니더라도 사용해도 좋을지도 모릅니다.

jotai-signal은 jotai와 jotai-uncontrolled의 중간 느낌이 되어버렸기 때문에 오히려 사용할 곳이 잘 떠오르지 않습니다. jsx-transform의 가능성을 확인한다는 점에서는 가치가 있고 useMemo가 필요 없기 때문에 일반 jotai 라이브러리보다는 쓰기에 더 좋을 것 같습니다.

의견 감상평 있으시면 꼭.

링크

https://github.com/jotaijs/jotai-signal

https://github.com/jotaijs/jotai-uncontrolled

profile
You only have to right once

0개의 댓글