Jotai

๋ฆฌ์•กํŠธ ์ƒํƒœ๊ด€๋ฆฌ๋ฅผ ์‰ฝ๊ฒŒ

Apr 25, 2023

๊ฐ•์˜๋ฏผ

#REACT

๋“ค์–ด๊ฐ€๋ฉฐ

React๋Š” ์‚ฌ์šฉ์ž ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ๊ตฌ์ถ•ํ•˜๊ธฐ ์œ„ํ•ด ๋งŽ์ด ์‚ฌ์šฉ๋˜๋Š” JavaScript ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์ž…๋‹ˆ๋‹ค. ๊ทธ๋Ÿฌ๋‚˜ React ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์„ ๊ฐœ๋ฐœํ•  ๋•Œ, ๋ณต์žกํ•œ ์ƒํƒœ ๊ด€๋ฆฌ ๋ฌธ์ œ๋ฅผ ๋‹ค๋ฃจ์–ด์•ผํ•˜๋Š” ๊ฒฝ์šฐ๊ฐ€ ์ข…์ข… ๋ฐœ์ƒํ•ฉ๋‹ˆ๋‹ค.

์˜ˆ๋ฅผ ๋“ค๋ฉด ์ตœ์ƒ์œ„ ๋ถ€๋ชจ ์ปดํฌ๋„ŒํŠธ์—์„œ ๋ณด๋‚ด์ฃผ๋Š” ๋ฐ์ดํ„ฐ๊ฐ€ ๋ช‡๋‹จ๊ณ„์˜ ์ž์‹ ์ปดํฌ๋„ŒํŠธ๋กœ ์ „๋‹ฌ๋˜์–ด์•ผ ํ•˜๋ฉฐ ์ „๋‹ฌ ๋ฐ›์€ ๋ฐ์ดํ„ฐ๋กœ ์ธํ•ด์„œ ์ƒ์œ„์˜ ์ƒํƒœ๊ฐ’์ด ์—…๋ฐ์ดํŠธ ๋˜์–ด์•ผ ํ• ๋•Œโ€ฆ ์‰ฝ์ง„ ์•Š๊ฒ ์ฃ ?

์ด๋Ÿฌํ•œ ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•˜๊ธฐ ์œ„ํ•ด ์ƒํƒœ ๊ด€๋ฆฌ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ด๋ฒˆ ๊ธ€์—์„œ๋Š” Jotai๋ผ๋Š” React ์ƒํƒœ ๊ด€๋ฆฌ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์— ๋Œ€ํ•ด ๊ฐ„๋‹จํžˆ ์‚ดํŽด๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค.

Jotai๋ž€?

React์˜ ์ƒํƒœ๊ด€๋ฆฌ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์ค‘ ํ•˜๋‚˜์ž…๋‹ˆ๋‹ค. Jotai๋ž€ ๋œป์€ ์ผ๋ณธ์–ด๋กœ โ€œ์ƒํƒœโ€๋ผ๋Š” ์˜๋ฏธ์ธ๋ฐ์š”
Jotai๋ฅผ ์‚ฌ์šฉํ–ˆ์„๋•Œ ๋ฆฌ์•กํŠธ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์—์„œ ๋น„๊ต์  ๊ฐ„๊ฒฐํ•˜๊ณ  ์‰ฝ๊ฒŒ ์ „์—ญ ์ƒํƒœ๊ด€๋ฆฌ๋ฅผ ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

Jotai๋Š” ๋ฆฌ์ฝ”์ผ์—์„œ ์˜๊ฐ์„ ๋ฐ›์•„ ์•„ํ† ๋ฏน ๋ชจ๋ธ๊ณผ ํ•จ๊ป˜ bottom-up ๋ฐฉ์‹์œผ๋กœ ์ ‘๊ทผํ•ฉ๋‹ˆ๋‹ค. ์•„ํ†ฐ๊ณผ ํ•จ๊ป˜ ์ƒํƒœ๋ฅผ ์ƒ์„ฑํ•˜๊ณ  ์•„ํ†ฐ ์˜์กด์„ฑ์— ๋”ฐ๋ผ ๋ Œ๋”๋ง ์ตœ์ ํ™”๋ฅผ ํ•˜๋Š”๋ฐ, ์ด ๋ฐฉ์‹์„ ํ†ตํ•ด ๋ฆฌ์•กํŠธ ์ปจํ…์ŠคํŠธ์˜ ๋ฆฌ๋ Œ๋”๋ง ์ด์Šˆ๋ฅผ ํ•ด๊ฒฐํ•˜๊ณ , ๋ฉ”๋ชจ์ด์ œ์ด์…˜์˜ ์˜์กด๋„๋ฅผ ์ค„์ผ์ˆ˜ ์žˆ๋‹ค๊ณ  Jotai๊ณตํ™ˆ์— ์„ค๋ช…๋˜์–ด ์žˆ์Šต๋‹ˆ๋‹ค.(์•„์ง recoil์„ ์‚ฌ์šฉํ•ด ๋ณด์ง€ ๋ชปํ•˜์—ฌ ์ถ”ํ›„์— ๊ธฐํšŒ๊ฐ€ ์žˆ์œผ๋ฉด recoil์— ๋Œ€ํ•ด์„œ ์•Œ์•„๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค)



ํŠน์ง•

  • ์ตœ์†Œํ•œ์˜ API
  • TypeScript ๊ธฐ๋ณธ ๋‚ด์žฅ
  • ์ž‘์€ ๋ฒˆ๋“ค ํฌ๊ธฐ
  • ๋งŽ์€ ์ถ”๊ฐ€ ์œ ํ‹ธ๋ฆฌํ‹ฐ ๋ฐ ๊ณต์‹ ํ†ตํ•ฉ
  • ๋ฆฌ์•กํŠธ์—์„œ๋งŒ ์‚ฌ์šฉ ๊ฐ€๋Šฅ
  • Next.js ๋ฐ React Native ์ง€์›
  • atomicํ•œ ์ƒํƒœ ๊ด€๋ฆฌ ๋ฐฉ์‹์œผ๋กœ ๊ตฌ์„ฑ

๊ธฐ๋ณธ ์‚ฌ์šฉ๋ฒ•

Jotai๋Š” ์‚ฌ์šฉ๋ฒ•์ด ์ƒ๋Œ€์ ์œผ๋กœ ์‰ฝ๊ณ  ๊ฐ„๊ฒฐํ•œ ํŽธ์ธ๋ฐ์š” ์•„๋ž˜์— ์˜ˆ์‹œ ์ฝ”๋“œ๋ฅผ ๋ณด๋ฉฐ ์„ค๋ช… ๋“œ๋ฆฌ๊ฒ ์Šต๋‹ˆ๋‹ค.

์šฐ์„  npm์„ ์ด์šฉํ•ด Jotai๋ฅผ ์„ค์น˜ํ•ด ์ค๋‹ˆ๋‹ค.

npm install jotai

๋‹ค์Œ์€ Jotai์˜ atom์„ ์‚ฌ์šฉํ•˜์—ฌ ์ƒํƒœ๋ฅผ ์ƒ์„ฑํ•˜๋Š” ์ฝ”๋“œ์ž…๋‹ˆ๋‹ค.

import { atom } from 'jotai';

const countAtom = atom(0);

์œ„ ์ฝ”๋“œ๋Š” countAtom ์ด๋ผ๋Š” ์ด๋ฆ„์˜ ์ƒํƒœ๋ฅผ ์ƒ์„ฑํ•˜๋ฉฐ, ์ดˆ๊นƒ๊ฐ’์œผ๋กœ 0์„ ์„ค์ •ํ•ฉ๋‹ˆ๋‹ค.

์ด์ œ ์ด ์ƒํƒœ๋ฅผ ์ปดํฌ๋„ŒํŠธ์—์„œ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

import { useAtom } from 'jotai';

function Counter() {
  const [count, setCount] = useAtom(countAtom);

  function increment() {
    setCount((count) => count + 1);
  }

  return (
    <div>
      <p>Count: {count}</p>
      <button onClick={increment}>Increment</button>
    </div>
  );
}

์œ„์˜ ์˜ˆ์‹œ ์ฝ”๋“œ์—์„œ๋Š”, countAtom์ด๋ผ๋Š” ์ƒํƒœ(atom)๋ฅผ ์ •์˜ํ•˜๊ณ , useAtom ํ›…์„ ์‚ฌ์šฉํ•˜์—ฌ ํ•ด๋‹น ์ƒํƒœ๋ฅผ ์ฝ๊ณ  ๋ณ€๊ฒฝํ•ฉ๋‹ˆ๋‹ค. Counter ์ปดํฌ๋„ŒํŠธ์—์„œ๋Š” count ์ƒํƒœ๋ฅผ ๋ Œ๋”๋งํ•˜๊ณ , ๋ฒ„ํŠผ ํด๋ฆญ ์ด๋ฒคํŠธ๋ฅผ ํ†ตํ•ด ํ•ด๋‹น ์ƒํƒœ๋ฅผ ์—…๋ฐ์ดํŠธํ•ฉ๋‹ˆ๋‹ค.

ํ”„๋กœ์ ํŠธ์— ์ „์—ญ์œผ๋กœ ๊ด€๋ฆฌ๋  ์ƒํƒœ๊ฐ’๋“ค์„ ์„ฑ๊ฒฉ์— ๋งž๋„๋ก ํ•œ๊ณณ์— ์ •์˜ํ•ด๋‘์–ด ํ•„์š”์‹œ ํ•ด๋‹น ์ปดํฌ๋„ŒํŠธ์—์„œ

import { useAtomValue } from 'jotai';
const count = useAtomValue(countAtom);

useAtomValue(read)์ฝ๊ธฐ

import { useSetValue } from 'jotai';
const count = useSetAtom(countAtom);

useSetAtom(write)์“ฐ๊ธฐ

์œ„์™€๊ฐ™์€ ํ˜•ํƒœ๋กœ๋„ ์‚ฌ์šฉ์ด ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค.

์ด๋ ‡๊ฒŒ Jotai ์—๋Š” ์„ธ๊ฐ€์ง€ ํŒจํ„ด์ด ์žˆ์Šต๋‹ˆ๋‹ค.

  • ์ฝ๊ธฐ(read),์“ฐ๊ธฐ(write)
  • ์ฝ๊ธฐ(read)
  • ์“ฐ๊ธฐ(write)

์ด๋ ‡๊ฒŒ ์ž‘์„ฑ์‹œ ๊ฐ๊ฐ ์ฝ๊ธฐ, ์“ฐ๊ธฐ๋งŒ ๊ฐ€์ ธ์™€์„œ ์‚ฌ์šฉํ•˜๊ธฐ ๋•Œ๋ฌธ์— useAtom ๊ณผ ๋‹ค๋ฅด๊ฒŒ ์žฌ๋žœ๋”๋ง ํ•˜์ง€ ์•Š๋Š” ์žฅ์ ์ด ์žˆ์Šต๋‹ˆ๋‹ค.

๋งค์šฐ ๊ฐ„๋‹จํ•ฉ๋‹ˆ๋‹ค ์‚ฌ์šฉ ๋ฐฉ์‹์ด ๋ฆฌ์•กํŠธ์˜ useState hook๊ณผ ๊ฑฐ์˜ ํก์‚ฌํ•˜์ฃ  ํ•˜์ง€๋งŒ useState์™€ ๋‹ค๋ฅด๊ฒŒ ํŠน์ • ์ปดํฌ๋„ŒํŠธ์— ๊ตฌ์†๋˜์–ด์žˆ์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

export const count = atom(0);

์œ„์™€ ๊ฐ™์ด export ์„ ์–ธ์„ ํ†ตํ•ด ์–ด๋””์„œ๋“  ๋‹ค๋ฅธ ์ปดํฌ๋„ŒํŠธ์—์„œ ํ•ด๋‹น atom๊ฐ’์— ์ ‘๊ทผ์ด ๊ฐ€๋Šฅํ•˜์ฃ 

์–ธ์ œ ์‚ฌ์šฉํ•˜๋ฉด ์ข‹์„๊นŒ?

  • jotai๋Š” Redux๋‚˜ MobX์™€ ๊ฐ™์€ ๋‹ค๋ฅธ ์ƒํƒœ ๊ด€๋ฆฌ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ณด๋‹ค ์ƒ๋Œ€์ ์œผ๋กœ ์ž‘์€ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์ด๊ธฐ ๋•Œ๋ฌธ์—, ์ž‘์€ ๊ทœ๋ชจ์˜ ํ”„๋กœ์ ํŠธ์—์„œ ์‚ฌ์šฉํ•˜๊ธฐ ์ ํ•ฉํ•ฉ๋‹ˆ๋‹ค.

  • jotai๋Š” Redux๋‚˜ MobX์™€ ๊ฐ™์€ ๋‹ค๋ฅธ ์ƒํƒœ ๊ด€๋ฆฌ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์— ๋น„ํ•ด ๋ณด๋‹ค ๋‹จ์ˆœํ•˜๊ณ  ์ง๊ด€์ ์ธ API๋ฅผ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค. ๋”ฐ๋ผ์„œ ๊ฐ„๋‹จํ•œ ์ƒํƒœ ๊ด€๋ฆฌ์— ์ ํ•ฉํ•ฉ๋‹ˆ๋‹ค.

  • jotai๋Š” React์˜ ์ƒˆ๋กœ์šด Context API์™€ ํ•จ๊ป˜ ์ž‘๋™ํ•˜๋ฉฐ, Context API์˜ ๊ฐ„ํŽธํ•œ ์‚ฌ์šฉ๋ฒ•์„ ์ด์šฉํ•˜์—ฌ ์ƒํƒœ ๊ด€๋ฆฌ๋ฅผ ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๋”ฐ๋ผ์„œ Redux๋‚˜ MobX์™€ ๊ฐ™์€ ๋ณ„๋„์˜ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ์‚ฌ์šฉํ•˜์ง€ ์•Š๊ณ ๋„ ์ƒํƒœ๋ฅผ ๊ด€๋ฆฌํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

  • jotai๋Š” ๋ถˆ๋ณ€์„ฑ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์ธ Immer์™€ ํ•จ๊ป˜ ์‚ฌ์šฉํ•˜๋ฉด์„œ๋„ ์ƒํƒœ์˜ ๋ถˆ๋ณ€์„ฑ์„ ๋ณด์žฅํ•˜๋ฉด์„œ ์ฝ”๋“œ์˜ ๊ฐ€๋…์„ฑ์„ ๋†’์ผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

  • jotai๋Š” ์ƒํƒœ ๋ณ€ํ™”๋ฅผ ์ถ”์ ํ•˜๊ธฐ ์‰ฝ๊ฒŒ ๋งŒ๋“ค์–ด์ค๋‹ˆ๋‹ค. ์ด๋Š” ๋””๋ฒ„๊น…์ด๋‚˜ ๊ฐœ๋ฐœ ๊ณผ์ •์—์„œ ๋„์›€์ด ๋ฉ๋‹ˆ๋‹ค.

๋งˆ์น˜๋ฉฐ

๊ฐ„๋‹จํ•˜๊ฒŒ ๋ฆฌ์•กํŠธ์˜ ์ƒํƒœ๊ด€๋ฆฌ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์ธ Jotai์— ๋Œ€ํ•ด์„œ ์•Œ์•„๋ดค๋Š”๋ฐ์š”

๋ฆฌ์•กํŠธ์—์„œ useState์™€ context api ๋งŒ์œผ๋กœ ์ƒํƒœ๊ด€๋ฆฌ๋ฅผ ์ง„ํ–‰ํ•˜๊ฑฐ๋‚˜ ๊ณ ๋ ค์ค‘์ด๋ผ๋ฉด Jotai๋ฅผ ๋„์ž…ํ•ด ๋ณด๋Š” ๊ฒƒ๋„ ์ข‹์„๊ฒƒ ๊ฐ™์Šต๋‹ˆ๋‹ค. ๋ฌผ๋ก  ๊นŠ์ด ํŒŒ๊ณ ๋“ค์–ด ๋‚ด๋ถ€๊ตฌ์กฐ ๋ฐ ์ตœ์ ํ™” ๋ถ€๋ถ„๊นŒ์ง€ ์ดํ•ดํ•˜๊ณ  ์‚ฌ์šฉํ•˜๊ธฐ์—๋Š” ์‰ฝ์ง„ ์•Š๊ฒ ์ง€๋งŒ ์ƒ๋Œ€์ ์œผ๋กœ ๋‚ฎ์€ ๋Ÿฌ๋‹์ปค๋ธŒ์™€ ์ œ๊ณต๋˜๋Š” ์œ ํ‹ธ๋“ค์ด ๋‹ค์–‘ํ•œ๋ฐ๋„ ๋ถˆ๊ตฌํ•˜๊ณ  ์ด๋ฒˆ๋“ค ์‚ฌ์ด์ฆˆ๊ฐ€ ๋‹ค๋ฅธ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์— ๋น„ํ•ด ๋‚ฎ์€ํŽธ์ธ๊ฑด ์ถฉ๋ถ„ํžˆ ๋งค๋ ฅ์ ์ธ ๋„๊ตฌ๋ผ ์ƒ๊ฐ๋ฉ๋‹ˆ๋‹ค.


์ฐธ๊ณ ๋ฌธ์„œ

Grow & Glow ยฉ 2025

Banner images by undraw.co