๐Ÿช 02. atom() ์™„์ „ ์ •๋ณต โ€” primitive atom ๊ณผ ํ•ต์‹ฌ ํ›…

๐Ÿ“‹ ๊ฐœ์š”

atom() ์ƒ์„ฑ๋ถ€ํ„ฐ useAtom, useAtomValue, useSetAtom์˜ ์ฐจ์ด์™€ ์„ ํƒ ๊ธฐ์ค€, debugLabel, onMount๊นŒ์ง€ atom์˜ ๋ชจ๋“  ๊ฒƒ์„ ํŒŒํ—ค์นฉ๋‹ˆ๋‹ค.

๐ŸŽฏ ์ด ์„น์…˜์„ ์ฝ๊ณ  ๋‚˜๋ฉด:

  • useAtom, useAtomValue, useSetAtom ์˜ ์ฐจ์ด๋ฅผ ์•Œ๊ณ , ์ƒํ™ฉ์— ๋งž๊ฒŒ ๊ณจ๋ผ ์“ธ ์ˆ˜ ์žˆ๋‹ค.
  • ๋ Œ๋” ํ•จ์ˆ˜ ์•ˆ์—์„œ atom ์„ ์ƒ์„ฑํ•  ๋•Œ ์™œ useMemo ๊ฐ€ ํ•„์ˆ˜์ธ์ง€ ์„ค๋ช…ํ•  ์ˆ˜ ์žˆ๋‹ค.
  • debugLabel ๊ณผ onMount ๋ฅผ ์‹ค๋ฌด์—์„œ ์–ด๋–ป๊ฒŒ ํ™œ์šฉํ•˜๋Š”์ง€ ์•Œ ์ˆ˜ ์žˆ๋‹ค.

๐Ÿ“‹ ๋ชฉ์ฐจ


๐Ÿ“Œ ์ด ๋ฌธ์„œ๋ฅผ ์ฝ๊ธฐ ์ „์—

โฑ๏ธ ์˜ˆ์ƒ ์ฝ๊ธฐ ์‹œ๊ฐ„: 18๋ถ„ (์ „์ฒด) / ํ•ต์‹ฌ ํŒŒํŠธ๋งŒ: 10๋ถ„

๐Ÿ—บ๏ธ ์ด ๋ฌธ์„œ์˜ ํ๋ฆ„

[atom() ๊ธฐ๋ณธ ์‚ฌ์šฉ] โ†’ [์„ธ ๊ฐ€์ง€ ํ›… ๋น„๊ต] โ†’ [๋ Œ๋” ๋‚ด atom ์ƒ์„ฑ ์ฃผ์˜] โ†’ [debugLabel & onMount ์‹ค๋ฌด ํ™œ์šฉ]

๐Ÿ—บ๏ธ ์ด ๋ฌธ์„œ์˜ ๋ฐฐ๊ฒฝ ์„ธ๊ณ„๊ด€: '์˜์ˆ˜๋„ค ์ปค๋ฎค๋‹ˆํ‹ฐ'

์˜ค๋Š˜ ์˜์ฒ ์ด๊ฐ€ ์ง์ ‘ atom ์„ ์จ๋ณด๋Š” ๋‚ ์ด์•ผ.

๐Ÿฃ ์˜์ฒ : "์˜ํ˜ธ ๋‹˜, useAtom ์ด๋ž‘ useAtomValue ๋ž‘ useSetAtom ์ด ์„ธ ๊ฐœ๊ฐ€ ๋‹ค ์žˆ๋˜๋ฐ... ๋ญ˜ ์จ์•ผ ํ•ด์š”? ๊ทธ๋ƒฅ useAtom ์“ฐ๋ฉด ์•ˆ ๋ผ์š”?"

๐Ÿฆ ์˜ํ˜ธ ๋‹˜: "์“ธ ์ˆ˜๋Š” ์žˆ์ฃ . ๊ทผ๋ฐ useAtom ์€ ๊ฐ’์„ ๊ตฌ๋… ํ•ด์š”. ๋ฒ„ํŠผ์ฒ˜๋Ÿผ ์“ฐ๊ธฐ๋งŒ ํ•˜๋Š” ์ปดํฌ๋„ŒํŠธ์—์„œ useAtom ์„ ์“ฐ๋ฉด, atom ์ด ๋ฐ”๋€” ๋•Œ๋งˆ๋‹ค ๊ทธ ๋ฒ„ํŠผ๋„ ๊ฐ™์ด ๋ฆฌ๋ Œ๋”๋ผ์š”. useSetAtom ์€ ๊ฐ’์„ ๊ตฌ๋…ํ•˜์ง€ ์•Š์œผ๋‹ˆ๊นŒ atom ์ด ๋ฐ”๋€Œ์–ด๋„ ๊ทธ ์ปดํฌ๋„ŒํŠธ๋Š” ๋ฆฌ๋ Œ๋” ์—†์ด ์กฐ์šฉํžˆ ์žˆ์„ ์ˆ˜ ์žˆ์–ด์š”."

๐Ÿฃ ์˜์ฒ : (๋ฉ”๋ชจํ•˜๋ฉฐ) "์•„... ๊ฐ’์„ ํ™”๋ฉด์— ์•ˆ ์จ๋„ ๋ฆฌ๋ Œ๋”๊ฐ€ ๋œ๋‹ค๋Š” ๊ฑฐ์ฃ ? ๊ทธ๋Ÿฌ๋ฉด ์ข‹์•„์š” ๋ฒ„ํŠผ ๊ฐ™์€ ๊ฑด useSetAtom ์จ์•ผ๊ฒ ๋„ค์š”."


๐Ÿงฉ atom() โ€” ๊ฐ€์žฅ ๋‹จ์ˆœํ•œ ์ƒํƒœ ๋‹จ์œ„ ๐ŸŸข

๐ŸŽฏ ์ด ์„น์…˜์„ ์ฝ๊ณ  ๋‚˜๋ฉด:

  • primitive atom ์„ TypeScript ์™€ ํ•จ๊ป˜ ์˜ฌ๋ฐ”๋ฅด๊ฒŒ ์„ ์–ธํ•˜๋Š” ๋ฒ•์„ ์•Œ ์ˆ˜ ์žˆ๋‹ค
  • atom ์€ ์–ด๋””์— ์„ ์–ธํ•ด๋„ ๋˜์ง€๋งŒ, ๋ Œ๋” ํ•จ์ˆ˜ ์•ˆ์€ ์œ„ํ—˜ํ•˜๋‹ค๋Š” ๊ฒƒ์„ ์ดํ•ดํ•œ๋‹ค

๊ธฐ๋ณธ ์„ ์–ธ

import { atom } from 'jotai'
 
// ์ˆซ์ž
const likeCountAtom = atom(0)
 
// ๋ฌธ์ž์—ด
const searchKeywordAtom = atom('')
 
// ๊ฐ์ฒด (TypeScript ์ œ๋„ค๋ฆญ ๊ถŒ์žฅ)
const userAtom = atom<User | null>(null)
 
// ๋ฐฐ์—ด
const studyListAtom = atom<Study[]>([])
 
// boolean
const isModalOpenAtom = atom(false)

TypeScript ์™€ ํ•จ๊ป˜ ์“ธ ๋•Œ

interface Study {
  id: string
  title: string
  tags: string[]
  likeCount: number
}
 
// ๐Ÿฆ ์˜ํ˜ธ: "์ œ๋„ค๋ฆญ์œผ๋กœ ํƒ€์ž…์„ ๋ช…์‹œํ•ด๋‘๋ฉด IDE ์ž๋™์™„์„ฑ์ด ์™„๋ฒฝํ•˜๊ฒŒ ๋™์ž‘ํ•ด์š”"
const selectedStudyAtom = atom<Study | null>(null)
const studyFiltersAtom = atom<{
  category: string
  tags: string[]
  sortBy: 'latest' | 'popular'
}>({
  category: 'all',
  tags: [],
  sortBy: 'latest',
})

atom ์„ ์–ธ ์œ„์น˜

// โœ… ๋ชจ๋“ˆ ์ตœ์ƒ๋‹จ (๊ถŒ์žฅ) โ€” ์•ˆ์ •์ ์ธ ์ฐธ์กฐ
const searchKeywordAtom = atom('')
 
// โœ… ๋ณ„๋„ ํŒŒ์ผ์—์„œ export โ€” ๋Œ€๊ทœ๋ชจ ์•ฑ์—์„œ ๊ถŒ์žฅ
// atoms/search.ts
export const searchKeywordAtom = atom('')
export const searchResultsAtom = atom<Study[]>([])
 
// โš ๏ธ ๋ Œ๋” ํ•จ์ˆ˜ ๋‚ด๋ถ€ โ€” useMemo ์—†์ด ์“ฐ๋ฉด ๋ฌดํ•œ ๋ฃจํ”„ (๋‹ค์Œ ์„น์…˜์—์„œ ์ƒ์„ธํžˆ)
const Component = () => {
  const myAtom = atom('') // โŒ ๋ Œ๋”๋งˆ๋‹ค ์ƒˆ atom config ์ƒ์„ฑ
}

๐Ÿ’ก ํ•œ ์ค„๋กœ ๊ธฐ์–ตํ•˜๊ธฐ
atom ์€ "์ „์—ญ ๋ณ€์ˆ˜" ์ฒ˜๋Ÿผ ๋ชจ๋“ˆ ์ตœ์ƒ๋‹จ์— ์„ ์–ธํ•˜๋Š” ๊ฒŒ ๊ธฐ๋ณธ์ด์•ผ.
๋‹จ, ๋ Œ๋” ํ•จ์ˆ˜ ์•ˆ์—์„œ๋„ ์“ธ ์ˆ˜ ์žˆ์–ด โ€” useMemo ๋กœ ๊ฐ์‹ธ๋ฉด.


๐ŸŽฃ ์„ธ ๊ฐ€์ง€ ํ›… โ€” useAtom / useAtomValue / useSetAtom ๐ŸŸข

๐ŸŽฏ ์ด ์„น์…˜์„ ์ฝ๊ณ  ๋‚˜๋ฉด:

  • ์„ธ ํ›…์˜ ์ฐจ์ด๋ฅผ ์„ค๋ช…ํ•˜๊ณ , ์ƒํ™ฉ์— ๋งž๊ฒŒ ๊ณ ๋ฅผ ์ˆ˜ ์žˆ๋‹ค
  • useAtom ์„ ํ•ญ์ƒ ์“ฐ๋Š” ๊ฒŒ ์™œ ๋น„ํšจ์œจ์ ์ธ์ง€ ์ดํ•ดํ•œ๋‹ค

๐Ÿค” ์ž ๊น, ๋จผ์ € ์ƒ๊ฐํ•ด๋ด
๋ฒ„ํŠผ ์ปดํฌ๋„ŒํŠธ๊ฐ€ ์žˆ์–ด. ์ด ๋ฒ„ํŠผ์€ ์นด์šดํŠธ๋ฅผ ์ฆ๊ฐ€์‹œํ‚ค๊ธฐ๋งŒ ํ•˜๊ณ , ํ˜„์žฌ ์นด์šดํŠธ๋ฅผ ํ™”๋ฉด์— ํ‘œ์‹œํ•˜์ง„ ์•Š์•„.
์ด ์ปดํฌ๋„ŒํŠธ์—์„œ ์นด์šดํŠธ atom ์„ ์–ด๋–ค ํ›…์œผ๋กœ ๊ตฌ๋…ํ•ด์•ผ ํ• ๊นŒ?

ํ›… ๋น„๊ตํ‘œ

ํ›…๋ฐ˜ํ™˜๊ฐ’๋ฆฌ๋ Œ๋” ์กฐ๊ฑด์‚ฌ์šฉ ์ผ€์ด์Šค
useAtom[value, setter]๊ฐ’์ด ๋ฐ”๋€” ๋•Œ์ฝ๊ธฐ + ์“ฐ๊ธฐ ๋ชจ๋‘ ํ•„์š”ํ•  ๋•Œ
useAtomValuevalue๊ฐ’์ด ๋ฐ”๋€” ๋•Œ์ฝ๊ธฐ๋งŒ ํ•„์š”ํ•  ๋•Œ (๋ทฐ ์ปดํฌ๋„ŒํŠธ)
useSetAtomsetter๋ฆฌ๋ Œ๋” ์—†์Œ์“ฐ๊ธฐ๋งŒ ํ•„์š”ํ•  ๋•Œ (๋ฒ„ํŠผ, ์ด๋ฒคํŠธ ํ•ธ๋“ค๋Ÿฌ)

์ฝ”๋“œ๋กœ ๋น„๊ต

const likeCountAtom = atom(0)
 
// โŒ ๐Ÿฃ ์˜์ฒ ์˜ ์ฝ”๋“œ โ€” ๋ชจ๋“  ๊ณณ์— useAtom ์‚ฌ์šฉ
// LikeButton ์€ ์นด์šดํŠธ๋ฅผ ํ‘œ์‹œํ•˜์ง€ ์•Š๋Š”๋ฐ๋„ ์นด์šดํŠธ ๋ณ€๊ฒฝ ์‹œ ๋ฆฌ๋ Œ๋”๋จ
const LikeButton = () => {
  const [, setLikeCount] = useAtom(likeCountAtom) // value ๋ฅผ ์•ˆ ์“ฐ๋Š”๋ฐ ๊ตฌ๋… ์ค‘
  return <button onClick={() => setLikeCount((c) => c + 1)}>โค๏ธ</button>
}
 
// โœ… ๐Ÿฆ ์˜ํ˜ธ์˜ ์ฝ”๋“œ โ€” ๋ชฉ์ ์— ๋งž๋Š” ํ›… ์„ ํƒ
// ์นด์šดํŠธ ํ‘œ์‹œ ์ปดํฌ๋„ŒํŠธ โ€” ์ฝ๊ธฐ๋งŒ ํ•„์š”
const LikeCounter = () => {
  const likeCount = useAtomValue(likeCountAtom) // ๊ฐ’๋งŒ ๊ตฌ๋…
  return <span>{likeCount}</span>
}
 
// ๋ฒ„ํŠผ ์ปดํฌ๋„ŒํŠธ โ€” ์“ฐ๊ธฐ๋งŒ ํ•„์š”, ๋ฆฌ๋ Œ๋”๋ง ๋ฐœ์ƒ ์•ˆ ํ•จ!
const LikeButton = () => {
  const setLikeCount = useSetAtom(likeCountAtom) // setter ๋งŒ ๊ฐ€์ ธ์˜ด
  return <button onClick={() => setLikeCount((c) => c + 1)}>โค๏ธ</button>
}

useSetAtom ์ด ๋ฆฌ๋ Œ๋”๋ฅผ ์ผ์œผํ‚ค์ง€ ์•Š๋Š” ์ด์œ 

// useSetAtom ์€ ๊ฐ’(value)์„ ๊ตฌ๋…ํ•˜์ง€ ์•Š์•„
// โ†’ likeCountAtom ๊ฐ’์ด ๋ฐ”๋€Œ์–ด๋„ ์ด ์ปดํฌ๋„ŒํŠธ๋Š” ๋ฆฌ๋ Œ๋” ์•ˆ ๋จ
const LikeButton = () => {
  const setLikeCount = useSetAtom(likeCountAtom)
  // ์—ฌ๊ธฐ์—” likeCount ๊ฐ’์ด ์—†์Œ โ†’ ๊ตฌ๋… ์—†์Œ โ†’ ๋ฆฌ๋ Œ๋” ์—†์Œ
  return (
    <button onClick={() => setLikeCount((prev) => prev + 1)}>
      ์ข‹์•„์š”
    </button>
  )
}

๐Ÿ”— ์—ฐ๊ฒฐ ๊ณ ๋ฆฌ
useSetAtom ์œผ๋กœ ์“ฐ๊ธฐ ์ „์šฉ ์•ก์…˜์„ ๋งŒ๋“œ๋Š” ํŒจํ„ด์€ 14. ๋Œ€๊ทœ๋ชจ ์•ฑ Jotai ์•„ํ‚คํ…์ฒ˜ ์—์„œ "write-only action atom" ์œผ๋กœ ๋” ๋ฐœ์ „์‹œ์ผœ.

์‹ค๋ฌด ํŒจํ„ด โ€” ์ปดํฌ๋„ŒํŠธ ๋ถ„๋ฆฌ

// โœ… ์˜์ˆ˜๋„ค ์Šคํ„ฐ๋”” ์นด๋“œ ์ปดํฌ๋„ŒํŠธ
// ์—ญํ• ๋ณ„๋กœ ๋ถ„๋ฆฌํ•ด์„œ ๊ฐ์ž ํ•„์š”ํ•œ ํ›…๋งŒ ์‚ฌ์šฉ
 
// ์นด๋“œ ๋ณธ๋ฌธ โ€” ์Šคํ„ฐ๋”” ์ •๋ณด ํ‘œ์‹œ (์ฝ๊ธฐ ์ „์šฉ)
const StudyCardBody = ({ studyId }: { studyId: string }) => {
  const study = useAtomValue(selectedStudyAtom) // ์ฝ๊ธฐ๋งŒ
  return <div>{study?.title}</div>
}
 
// ์ข‹์•„์š” ์นด์šดํ„ฐ โ€” ์ˆซ์ž ํ‘œ์‹œ (์ฝ๊ธฐ ์ „์šฉ)
const LikeCount = () => {
  const count = useAtomValue(likeCountAtom) // ์ฝ๊ธฐ๋งŒ
  return <span>{count}</span>
}
 
// ์ข‹์•„์š” ๋ฒ„ํŠผ โ€” ํด๋ฆญ ์ด๋ฒคํŠธ ์ฒ˜๋ฆฌ (์“ฐ๊ธฐ ์ „์šฉ)
const LikeButton = () => {
  const setCount = useSetAtom(likeCountAtom) // ์“ฐ๊ธฐ๋งŒ, ๋ฆฌ๋ Œ๋” ์—†์Œ
  return <button onClick={() => setCount((c) => c + 1)}>โค๏ธ</button>
}
 
// ๋ถ€๋ชจ ์ปดํฌ๋„ŒํŠธ โ€” ๊ทธ๋ƒฅ ์กฐํ•ฉ
const StudyCard = ({ studyId }: { studyId: string }) => (
  <div>
    <StudyCardBody studyId={studyId} />
    <LikeCount />
    <LikeButton />
  </div>
)

๐Ÿ’ก ํ•œ ์ค„๋กœ ๊ธฐ์–ตํ•˜๊ธฐ
"์ฝ๊ธฐ๋งŒ ํ•˜๋ฉด useAtomValue, ์“ฐ๊ธฐ๋งŒ ํ•˜๋ฉด useSetAtom, ๋‘˜ ๋‹ค๋ฉด useAtom."
ํŠนํžˆ ๋ฒ„ํŠผ์ฒ˜๋Ÿผ ๊ฐ’์„ ํ‘œ์‹œํ•˜์ง€ ์•Š๋Š” ์ปดํฌ๋„ŒํŠธ์—” useSetAtom ์ด ์ •์„์ด์•ผ.


โšก ๋ Œ๋” ํ•จ์ˆ˜ ๋‚ด atom ์ƒ์„ฑ โ€” useMemo ๊ฐ€ ํ•„์ˆ˜์ธ ์ด์œ  ๐ŸŸก

๐ŸŽฏ ์ด ์„น์…˜์„ ์ฝ๊ณ  ๋‚˜๋ฉด:

  • ๋ Œ๋” ํ•จ์ˆ˜ ๋‚ด atom ์ƒ์„ฑ์ด ์™œ ์œ„ํ—˜ํ•œ์ง€ ์›๋ฆฌ๋ฅผ ์„ค๋ช…ํ•  ์ˆ˜ ์žˆ๋‹ค
  • useMemo ์™€ useRef ์ค‘ ์–ด๋–ค ๊ฑธ ์จ์•ผ ํ•˜๋Š”์ง€ ํŒ๋‹จํ•  ์ˆ˜ ์žˆ๋‹ค

๐Ÿค” ์ž ๊น, ๋จผ์ € ์ƒ๊ฐํ•ด๋ด
๋ Œ๋” ํ•จ์ˆ˜ ์•ˆ์—์„œ const myAtom = atom(0) ์„ ์“ฐ๋ฉด ๋ฌด์Šจ ์ผ์ด ์ƒ๊ธธ๊นŒ?
"๋ Œ๋”๋งˆ๋‹ค ์ƒˆ ๊ฐ์ฒด๊ฐ€ ๋งŒ๋“ค์–ด์งˆ ๊ฒƒ ๊ฐ™๋‹ค" ๊นŒ์ง€ ๋– ์˜ฌ๋ ธ์œผ๋ฉด ์ถฉ๋ถ„ํ•ด.

๋ฌดํ•œ ๋ฃจํ”„๊ฐ€ ๋ฐœ์ƒํ•˜๋Š” ์›๋ฆฌ

// โŒ ๋ฌดํ•œ ๋ฃจํ”„ ๋ฐœ์ƒ ๋ฉ”์ปค๋‹ˆ์ฆ˜
const StudyItem = ({ id }: { id: string }) => {
  // 1. ๋ Œ๋” โ†’ atom config ๊ฐ์ฒด A ์ƒ์„ฑ
  const itemAtom = atom({ id })
 
  // 2. useAtom ์ด "์ƒˆ atom!" ์œผ๋กœ ์ธ์‹ โ†’ ์ดˆ๊ธฐํ™” โ†’ ๋ฆฌ๋ Œ๋” ํŠธ๋ฆฌ๊ฑฐ
  const [item] = useAtom(itemAtom)
 
  // 3. ๋ฆฌ๋ Œ๋” โ†’ atom config ๊ฐ์ฒด B ์ƒ์„ฑ (A ์™€ ๋‹ค๋ฅธ ์ฐธ์กฐ)
  // 4. useAtom ์ด ๋˜ "์ƒˆ atom!" โ†’ ์ดˆ๊ธฐํ™” โ†’ ๋ฆฌ๋ Œ๋” ํŠธ๋ฆฌ๊ฑฐ
  // 5. ๋ฌดํ•œ ๋ฐ˜๋ณต...
  return <div>{item.id}</div>
}

ํ•ด๊ฒฐ์ฑ…: useMemo

// โœ… useMemo ๋กœ atom config ๋ฅผ ์•ˆ์ •์ ์ธ ์ฐธ์กฐ๋กœ ์œ ์ง€
const StudyItem = ({ id }: { id: string }) => {
  // id ๊ฐ€ ๋ฐ”๋€” ๋•Œ๋งŒ ์ƒˆ atom config ์ƒ์„ฑ
  const itemAtom = useMemo(() => atom({ id }), [id])
  const [item, setItem] = useAtom(itemAtom)
  return <div>{item.id}</div>
}

useMemo vs useRef โ€” ์–ธ์ œ ์–ด๋–ค ๊ฑธ?

// useMemo: ์˜์กด์„ฑ(deps)์ด ์žˆ์„ ๋•Œ โ€” id ๊ฐ€ ๋ฐ”๋€Œ๋ฉด ์ƒˆ atom
const itemAtom = useMemo(() => atom({ id }), [id])
 
// useRef: ์˜์กด์„ฑ์ด ์—†์„ ๋•Œ (ํ•œ ๋ฒˆ๋งŒ ์ƒ์„ฑ, ์ปดํฌ๋„ŒํŠธ ์ƒ๋ช…์ฃผ๊ธฐ ๋™์•ˆ ์œ ์ง€)
const itemAtomRef = useRef(atom({ id }))
const itemAtom = itemAtomRef.current
 
// ๐Ÿฆ ์˜ํ˜ธ: "์˜์กด์„ฑ์ด ์žˆ์œผ๋ฉด useMemo, ์—†์œผ๋ฉด useRef.
//          ์˜์‹ฌ์Šค๋Ÿฌ์šฐ๋ฉด useMemo ์“ฐ๋Š” ๊ฒŒ ์•ˆ์ „ํ•ด์š”."

๐Ÿ’ก ํ•œ ์ค„๋กœ ๊ธฐ์–ตํ•˜๊ธฐ
๋ Œ๋” ํ•จ์ˆ˜ ์•ˆ์—์„œ atom() ์„ ์“ฐ๋ฉด ๋ฐ˜๋“œ์‹œ useMemo(() => atom(), [deps]) ๋กœ ๊ฐ์‹ธ์•ผ ํ•ด.
๋ชจ๋“ˆ ์ตœ์ƒ๋‹จ์— ์„ ์–ธํ•  ์ˆ˜ ์žˆ์œผ๋ฉด ๊ทธ๊ฒŒ ์ œ์ผ ์•ˆ์ „ํ•ด.


๐Ÿท๏ธ debugLabel โ€” DevTools ์—์„œ atom ์„ ์ฐพ๋Š” ๋ฒ• ๐ŸŸก

๐ŸŽฏ ์ด ์„น์…˜์„ ์ฝ๊ณ  ๋‚˜๋ฉด:

  • debugLabel ์„ ์„ค์ •ํ•ด์„œ ๊ฐœ๋ฐœ ํ™˜๊ฒฝ์—์„œ atom ์„ ์‰ฝ๊ฒŒ ์‹๋ณ„ํ•  ์ˆ˜ ์žˆ๋‹ค

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

const searchKeywordAtom = atom('')
// DevTools ์—์„œ ์ด๋ฆ„์ด ์•ˆ ๋ณด์—ฌ ๋””๋ฒ„๊น…์ด ์–ด๋ ค์›€
 
// โœ… debugLabel ์„ค์ •
searchKeywordAtom.debugLabel = 'searchKeyword'
 
// ๋˜๋Š” ์„ ์–ธ๊ณผ ๋™์‹œ์—
const userAtom = Object.assign(atom<User | null>(null), {
  debugLabel: 'currentUser',
})

์‹ค๋ฌด ํŒจํ„ด โ€” ์ผ๊ด€๋œ ๋„ค์ด๋ฐ ์ปจ๋ฒค์…˜

// atoms/study.ts
export const studyListAtom = atom<Study[]>([])
studyListAtom.debugLabel = 'study/list'
 
export const selectedStudyAtom = atom<Study | null>(null)
selectedStudyAtom.debugLabel = 'study/selected'
 
export const studyFiltersAtom = atom({ category: 'all', tags: [] as string[] })
studyFiltersAtom.debugLabel = 'study/filters'
 
// ๐Ÿฆ ์˜ํ˜ธ: "๋„๋ฉ”์ธ/์—ญํ•  ํ˜•ํƒœ๋กœ ์ด๋ฆ„ ์ง“๋Š” ๊ฒŒ DevTools ์—์„œ ์ฐพ๊ธฐ ํŽธํ•ด์š”.
//          ๋‚˜์ค‘์— atom ์ด ์ˆ˜์‹ญ ๊ฐœ ๋  ๋•Œ ์ด ์Šต๊ด€์ด ๋น›๋‚ฉ๋‹ˆ๋‹ค."

๐Ÿ’ก ํ•œ ์ค„๋กœ ๊ธฐ์–ตํ•˜๊ธฐ
debugLabel ์€ DevTools ์šฉ ์ด๋ฆ„ํ‘œ์•ผ. ์„ ์–ธํ•˜๋Š” ์Šต๊ด€์„ ๋“ค์ด๋ฉด ๋””๋ฒ„๊น… ์‹œ๊ฐ„์ด ํ™• ์ค„์–ด.


๐Ÿ”Œ onMount โ€” atom ์ด ๊ตฌ๋…๋  ๋•Œ ์‚ฌ์ด๋“œ ์ดํŽ™ํŠธ ์ฒ˜๋ฆฌ ๐Ÿ”ด

๐ŸŽฏ ์ด ์„น์…˜์„ ์ฝ๊ณ  ๋‚˜๋ฉด:

  • onMount ๊ฐ€ ์–ธ์ œ ํ˜ธ์ถœ๋˜๋Š”์ง€, ์–ด๋–ค ์šฉ๋„๋กœ ์“ฐ๋Š”์ง€ ์ดํ•ดํ•œ๋‹ค
  • onMount ์˜ ์ •๋ฆฌ(cleanup) ํ•จ์ˆ˜๋ฅผ ์˜ฌ๋ฐ”๋ฅด๊ฒŒ ๋ฐ˜ํ™˜ํ•˜๋Š” ๋ฒ•์„ ์•ˆ๋‹ค

onMount ๋ž€?

// atom ์ด ์ฒ˜์Œ ๊ตฌ๋…๋  ๋•Œ ์‹คํ–‰, unmount ์‹œ ์ •๋ฆฌ ํ•จ์ˆ˜ ๋ฐ˜ํ™˜
const countAtom = atom(1)
 
countAtom.onMount = (setAtom) => {
  console.log('countAtom ์ด ์ฒ˜์Œ ๊ตฌ๋…๋จ')
  setAtom((c) => c + 1) // ๊ตฌ๋… ์ฆ‰์‹œ ์ดˆ๊ธฐ๊ฐ’ ์กฐ์ž‘ ๊ฐ€๋Šฅ
 
  return () => {
    console.log('countAtom ๊ตฌ๋…์ž๊ฐ€ ์—†์–ด์ง') // ์ •๋ฆฌ ํ•จ์ˆ˜
  }
}

์‹ค๋ฌด ์˜ˆ์‹œ โ€” ์‹ค์‹œ๊ฐ„ ์•Œ๋ฆผ ๊ตฌ๋…

// ์•Œ๋ฆผ atom โ€” ๊ตฌ๋… ์‹œ WebSocket ์—ฐ๊ฒฐ, ํ•ด์ œ ์‹œ ๋Š๊ธฐ
const notificationAtom = atom<Notification[]>([])
 
notificationAtom.onMount = (setNotifications) => {
  // ์ฒซ ์ปดํฌ๋„ŒํŠธ๊ฐ€ ๊ตฌ๋…ํ•  ๋•Œ WebSocket ์—ฐ๊ฒฐ
  const ws = new WebSocket('wss://api.yeongsune.dev/notifications')
 
  ws.onmessage = (event) => {
    const notification = JSON.parse(event.data) as Notification
    setNotifications((prev) => [notification, ...prev])
  }
 
  // ๋งˆ์ง€๋ง‰ ๊ตฌ๋…์ž๊ฐ€ ์‚ฌ๋ผ์งˆ ๋•Œ ์—ฐ๊ฒฐ ํ•ด์ œ
  return () => ws.close()
}

โš ๏ธ ์ฃผ์˜์‚ฌํ•ญ:

  • onMount ๋Š” ์ฒซ ๋ฒˆ์งธ ๊ตฌ๋…์ž ๊ฐ€ ์ƒ๊ธธ ๋•Œ ํ•œ ๋ฒˆ๋งŒ ํ˜ธ์ถœ๋ผ
  • useSetAtom ๋งŒ ์‚ฌ์šฉํ•˜๋Š” ์ปดํฌ๋„ŒํŠธ๋Š” ๊ตฌ๋…์ž๋กœ ์ทจ๊ธ‰ ์•ˆ ๋ผ โ†’ onMount ํ˜ธ์ถœ ์•ˆ ๋จ
  • React Strict Mode ์—์„œ๋Š” mount โ†’ unmount โ†’ mount ์ˆœ์„œ๋กœ ๋‘ ๋ฒˆ ํ˜ธ์ถœ๋  ์ˆ˜ ์žˆ์–ด (์ •๋ฆฌ ํ•จ์ˆ˜ ๊ตฌํ˜„ ์ค‘์š”)

๐Ÿ’ก ํ•œ ์ค„๋กœ ๊ธฐ์–ตํ•˜๊ธฐ
onMount ๋Š” "์ด atom ์˜ ์ฒซ ์†๋‹˜์ด ์™”์„ ๋•Œ ์‹คํ–‰ํ•  ํ™˜์˜ ์ฝ”๋“œ" ์•ผ.
WebSocket, ํƒ€์ด๋จธ, ์ด๋ฒคํŠธ ๋ฆฌ์Šค๋„ˆ ๊ฐ™์€ ๊ตฌ๋… ์‹œ์ž‘ ๋กœ์ง์— ์–ด์šธ๋ ค.


๐Ÿ’ฅ ์—๋Ÿฌ ํ•ด๊ฒฐ ์นดํƒˆ๋กœ๊ทธ


โŒ useAtom called outside of Provider ์—๋Ÿฌ

์›์ธ: React ์ปจํ…์ŠคํŠธ ๋ฐ–์—์„œ useAtom ํ˜ธ์ถœ

ํ•ด๊ฒฐ์ฑ…:

// ์ปดํฌ๋„ŒํŠธ ๋‚ด๋ถ€์—์„œ๋งŒ useAtom ํ˜ธ์ถœํ•ด์•ผ ํ•จ
// ์ด๋ฒคํŠธ ํ•ธ๋“ค๋Ÿฌ๋‚˜ ๋น„๋™๊ธฐ ํ•จ์ˆ˜ ๋‚ด๋ถ€์—์„  useStore() ์‚ฌ์šฉ

โŒ ๋ Œ๋”๋งˆ๋‹ค setter ํ•จ์ˆ˜ ์ฐธ์กฐ๊ฐ€ ๋ฐ”๋€Œ์–ด ์ž์‹ ์ปดํฌ๋„ŒํŠธ๊ฐ€ ๋ฆฌ๋ Œ๋”๋จ

์›์ธ: useAtom ์ด ๋ฐ˜ํ™˜ํ•˜๋Š” setter ๋Š” ํ•ญ์ƒ ์•ˆ์ •์ ์ธ ์ฐธ์กฐ๋ฅผ ๊ฐ€์ ธ. ๋ฆฌ๋ Œ๋” ์›์ธ์ด ์•„๋‹ ๊ฐ€๋Šฅ์„ฑ์ด ๋†’์œผ๋‹ˆ ๋‹ค๋ฅธ props ๋ฅผ ํ™•์ธํ•ด๋ด.

// useAtom, useSetAtom ์˜ setter ๋Š” ์•ˆ์ •์  ์ฐธ์กฐ (๋งค ๋ Œ๋”๋งˆ๋‹ค ๊ฐ™์€ ํ•จ์ˆ˜)
const [, setCount] = useAtom(countAtom)
// setCount ๋Š” ํ•ญ์ƒ ๋™์ผํ•œ ์ฐธ์กฐ โ†’ memo ๋œ ์ž์‹์— props ๋กœ ๋„˜๊ฒจ๋„ ๋ฆฌ๋ Œ๋” ์•ˆ ์ผ์–ด๋‚จ

๐Ÿ ์ด๋ฒˆ์— ๋ฐฐ์šด ๋‚ด์šฉ ์ด์ •๋ฆฌ

๐Ÿ“‹ ์„ธ ํ›… ์„ ํƒ ๊ธฐ์ค€ ์š”์•ฝ

์ƒํ™ฉํ›…์ด์œ 
๊ฐ’ ํ‘œ์‹œ + ์ƒํƒœ ๋ณ€๊ฒฝ ๋ชจ๋‘useAtom[value, setter] ๋ฐ˜ํ™˜
๊ฐ’๋งŒ ํ‘œ์‹œ (๋ทฐ ์ปดํฌ๋„ŒํŠธ)useAtomValue์ฝ๊ธฐ ์ „์šฉ ๋ช…์‹œ, ์ฝ”๋“œ ์˜๋„ ๋ช…ํ™•
ํด๋ฆญ/์ด๋ฒคํŠธ ์ฒ˜๋ฆฌ๋งŒ (๋ฒ„ํŠผ)useSetAtom๊ตฌ๋… ์—†์Œ โ†’ ๋ฆฌ๋ Œ๋” ์—†์Œ

โš ๏ธ ์ ˆ๋Œ€ ํ•˜์ง€ ๋ง ๊ฒƒ

์ƒํ™ฉโŒ ๋‚˜์œ ์˜ˆโœ… ์ข‹์€ ์˜ˆ
๋ Œ๋” ๋‚ด atom ์ƒ์„ฑconst a = atom(0) inside renderuseMemo(() => atom(0), [])
๋ชจ๋“  ๊ณณ์— useAtom๊ฐ’ ์•ˆ ์จ๋„ useAtom์“ฐ๊ธฐ๋งŒ ํ•˜๋ฉด useSetAtom
debugLabel ์ƒ๋žต์ด๋ฆ„ ์—†๋Š” atom ๋‹ค์ˆ˜atom.debugLabel = 'study/list'

๐Ÿ“ ๋งˆ๋ฌด๋ฆฌ ํ€ด์ฆˆ

Q1. ๐Ÿ› ๏ธ ์‹ค๋ฌด ๋”œ๋ ˆ๋งˆ (์˜์ฒ ์˜ ์„ ํƒ)

์˜์ฒ ์ด๊ฐ€ ์Šคํ„ฐ๋”” ์นด๋“œ ์ปดํฌ๋„ŒํŠธ๋ฅผ ๋งŒ๋“ค๊ณ  ์žˆ์–ด. ์ด ์ปดํฌ๋„ŒํŠธ๋Š” ์ข‹์•„์š” ์ˆซ์ž๋ฅผ ํ‘œ์‹œํ•˜์ง€ ์•Š๊ณ , ์ข‹์•„์š” ๋ฒ„ํŠผ ํด๋ฆญ ์‹œ ์นด์šดํŠธ๋งŒ ์ฆ๊ฐ€์‹œ์ผœ. ์–ด๋–ค ํ›…์„ ์จ์•ผ ํ• ๊นŒ?

  • A) const [likeCount, setLikeCount] = useAtom(likeCountAtom)
  • B) const likeCount = useAtomValue(likeCountAtom)
  • C) const setLikeCount = useSetAtom(likeCountAtom)
  • D) const [, setLikeCount] = useAtom(likeCountAtom)

โœ… ์ •๋‹ต: C

๐Ÿ’ก ์ƒ์„ธ ํ•ด์„ค:

  • ์›๋ฆฌ ์„ค๋ช…: ์ปดํฌ๋„ŒํŠธ๊ฐ€ ๊ฐ’์„ ํ‘œ์‹œํ•˜์ง€ ์•Š์œผ๋ฏ€๋กœ likeCountAtom ์„ ๊ตฌ๋…ํ•  ํ•„์š”๊ฐ€ ์—†์–ด. useSetAtom ์€ setter ๋งŒ ๋ฐ˜ํ™˜ํ•˜๊ณ  atom ๊ฐ’์„ ๊ตฌ๋…ํ•˜์ง€ ์•Š์•„์„œ, likeCountAtom ์ด ๋ณ€ํ•ด๋„ ์ด ์ปดํฌ๋„ŒํŠธ๋Š” ๋ฆฌ๋ Œ๋”๋˜์ง€ ์•Š์•„. ๋ถˆํ•„์š”ํ•œ ๋ฆฌ๋ Œ๋” ์ œ๋กœ!
  • ์˜ค๋‹ต ํ”ผ๋“œ๋ฐฑ: A, D ๋Š” value ๋ฅผ ๊ตฌ๋…ํ•˜๊ธฐ ๋•Œ๋ฌธ์— likeCount ๊ฐ€ ๋ฐ”๋€” ๋•Œ๋งˆ๋‹ค ์ด ์ปดํฌ๋„ŒํŠธ๊ฐ€ ๋ฆฌ๋ Œ๋”๋ผ. ๊ฐ’์„ ์•ˆ ์“ฐ๋Š”๋ฐ ๋ฆฌ๋ Œ๋”๋˜๋ฉด ๋‚ญ๋น„์•ผ. B ๋Š” setter ๋ฅผ ์ œ๊ณตํ•˜์ง€ ์•Š์•„.
  • ๐Ÿ“Œ ํ•ต์‹ฌ ๊ธฐ์–ต๋ฒ•: "๋ฒ„ํŠผ ์ปดํฌ๋„ŒํŠธ์—” useSetAtom." ํ™”๋ฉด์— ์ˆซ์ž๋ฅผ ์•ˆ ๋ณด์—ฌ์ค€๋‹ค๋ฉด ๊ตฌ๋…๋„ ํ•˜์ง€ ๋งˆ.

Q2. ๋นˆ์นธ ์ฑ„์šฐ๊ธฐ

์•„๋ž˜ ์ฝ”๋“œ์—์„œ ๋ฌดํ•œ ๋ฃจํ”„๋ฅผ ๋ง‰๊ธฐ ์œ„ํ•ด ๋นˆ์นธ์„ ์ฑ„์›Œ๋ด:

const StudyFilterChip = ({ tag }: { tag: string }) => {
  const tagAtom = ______(() => atom(tag), [tag]) // โ† ๋นˆ์นธ
  const [isSelected, setIsSelected] = useAtom(tagAtom)
  return (
    <button onClick={() => setIsSelected((v) => !v)}>
      {tag}
    </button>
  )
}

โœ… ์ •๋‹ต: useMemo

๐Ÿ’ก ์ƒ์„ธ ํ•ด์„ค:
useMemo ๋กœ ๊ฐ์‹ธ๋ฉด tag ๊ฐ€ ๋ฐ”๋€” ๋•Œ๋งŒ ์ƒˆ atom config ๊ฐ€ ์ƒ์„ฑ๋ผ. tag ๊ฐ€ ๋™์ผํ•˜๋ฉด ์ด์ „๊ณผ ๊ฐ™์€ atom config ๊ฐ์ฒด ์ฐธ์กฐ๋ฅผ ๋ฐ˜ํ™˜ํ•˜๊ธฐ ๋•Œ๋ฌธ์— useAtom ์ด "์ƒˆ atom" ์œผ๋กœ ์ฐฉ๊ฐํ•˜์ง€ ์•Š์•„. ๋ฌดํ•œ ๋ฃจํ”„ ๋ฐฉ์ง€!

๐Ÿ“Œ ํ•ต์‹ฌ ๊ธฐ์–ต๋ฒ•: "๋ Œ๋” ํ•จ์ˆ˜ ์•ˆ atom() = useMemo ๋กœ ๊ฐ์‹ธ๊ธฐ."


Q3. ์นœ๊ตฌ์—๊ฒŒ ์„ค๋ช…ํ•œ๋‹ค๋ฉด?

useAtomValue ์™€ useSetAtom ์„ ๋”ฐ๋กœ ๋‚˜๋ˆ  ์“ฐ๋Š” ์ด์œ ๋ฅผ ์นœ๊ตฌ์—๊ฒŒ ์„ค๋ช…ํ•ด๋ด.

์˜ˆ์‹œ ๋‹ต๋ณ€:

"๊ฐ’์„ ํ‘œ์‹œํ•˜๋Š” ์ปดํฌ๋„ŒํŠธ์™€ ๊ฐ’์„ ๋ฐ”๊พธ๋Š” ์ปดํฌ๋„ŒํŠธ๋ฅผ ๋‚˜๋ˆ„๋ฉด, ๋ฐ”๊พธ๋Š” ์ชฝ์€ ๊ฐ’์ด ๋ณ€ํ•ด๋„ ๋ฆฌ๋ Œ๋”๊ฐ€ ์•ˆ ์ผ์–ด๋‚˜๊ฑฐ๋“ . useSetAtom ์€ setter ๋งŒ ๊ฐ€์ ธ์™€์„œ '๊ตฌ๋…'์„ ์•ˆ ํ•˜๊ธฐ ๋•Œ๋ฌธ์ด์•ผ. ์ข‹์•„์š” ๋ฒ„ํŠผ์ฒ˜๋Ÿผ ์ˆซ์ž๋ฅผ ์•ˆ ๋ณด์—ฌ์ฃผ๋Š” ์ปดํฌ๋„ŒํŠธ๊ฐ€ ์นด์šดํŠธ ๋ฐ”๋€” ๋•Œ๋งˆ๋‹ค ๋ฆฌ๋ Œ๋”๋  ํ•„์š” ์—†์ž–์•„."

๐Ÿ’ก ์ง์ ‘ ์„ค๋ช…ํ•ด๋ดค๋‹ค๋ฉด: ์ด๋ฏธ ์ถฉ๋ถ„ํžˆ ์ดํ•ดํ•œ ๊ฑฐ์•ผ! ๐ŸŽ‰


๐Ÿฃ ์˜์ฒ ์ด์˜ ํ‡ด๊ทผ ์ผ๊ธฐ

์˜ค๋Š˜์€ atom ํ›… ์„ธ ์ข…๋ฅ˜๋ฅผ ์ œ๋Œ€๋กœ ์จ๋ดค๋‹ค. ์‚ฌ์‹ค ์ฒ˜์Œ์—” ๊ทธ๋ƒฅ useAtom ํ•˜๋‚˜๋กœ ๋‹ค ํ•ด๊ฒฐํ•˜๋ ค ํ–ˆ๋Š”๋ฐ...

์˜ํ˜ธ ๋‹˜์ด ๋‚ด PR ์— ๋‚จ๊ธด ์ฝ”๋ฉ˜ํŠธ: "์˜์ฒ  ๋‹˜, ๋ฒ„ํŠผ ์ปดํฌ๋„ŒํŠธ์— useAtom ์“ฐ์…จ๋Š”๋ฐ, ์ด ์ปดํฌ๋„ŒํŠธ๋Š” ์ข‹์•„์š” ์ˆซ์ž๋ฅผ ๋ณด์—ฌ์ฃผ์ง€ ์•Š๋Š”๋ฐ ์™œ ๊ตฌ๋…์„ ํ•˜๊ณ  ์žˆ์–ด์š”?"

๐Ÿ’ก ์˜ค๋Š˜์˜ ๊ตํ›ˆ: "์ฝ๊ธฐ๋งŒ ํ•˜๋ฉด useAtomValue, ์“ฐ๊ธฐ๋งŒ ํ•˜๋ฉด useSetAtom. ๋‘˜ ๋‹ค ํ•„์š”ํ•  ๋•Œ๋งŒ useAtom."

๊ทธ๋ฆฌ๊ณ  ๋ Œ๋” ํ•จ์ˆ˜ ์•ˆ์—์„œ atom() ์“ฐ๋‹ค๊ฐ€ ์ง„์งœ๋กœ ๋ฌดํ•œ ๋ฃจํ”„๋ฅผ ๊ฒช์—ˆ๋‹ค. ํƒญ์ด ๋ฉˆ์ถ”๊ณ  ํŒฌ ์†Œ๋ฆฌ๊ฐ€ ๋‚ฌ๋‹ค... useMemo ๋กœ ๊ฐ์‹ธ๋‹ˆ๊นŒ ๋ฐ”๋กœ ํ•ด๊ฒฐ๋๋Š”๋ฐ, ์›๋ฆฌ๋ฅผ ์ดํ•ดํ•˜๊ณ  ๋‚˜๋‹ˆ ์™œ ๊ทธ๋žฌ๋Š”์ง€ ๋‚ฉ๋“์ด ๋๋‹ค.

์˜ค๋Š˜ ์ข€ ๋งŽ์ด ํ˜ผ๋‚ฌ์ง€๋งŒ ๊ทธ๋ž˜๋„ ์˜ํ˜ธ ๋‹˜ ๋•์— ์‚ด์•˜๋‹ค. ๋‚ด์ผ์€ ๋‚ด๊ฐ€ ๋จผ์ € useSetAtom ์“ฐ๋Š” ๋ชจ์Šต ๋ณด์—ฌ๋“œ๋ ค์•ผ์ง€. ํ‡ด๊ทผํ•˜๊ณ  ํŽธ์˜์  ๋“ค๋Ÿฌ์„œ ์‚ผ๊ฐ๊น€๋ฐฅ์ด๋‚˜ ์‚ฌ๋จน๊ณ  ๊ฐ€์•ผ๊ฒ ๋‹ค. ๐Ÿ™


๐Ÿ”— ๋” ์•Œ์•„๋ณด๊ธฐ