๐Ÿ’ก 07. atom ์„ค๊ณ„ ์ „๋žต โ€” ํŒŒ์ผ ๊ตฌ์กฐ์™€ ์ฑ…์ž„ ๋ถ„๋ฆฌ

๐Ÿ“‹ ๊ฐœ์š”

๋„๋ฉ”์ธ๋ณ„ atom ํŒŒ์ผ ๋ถ„๋ฆฌ, ๋„ค์ด๋ฐ ์ปจ๋ฒค์…˜, ์ˆœํ™˜ ์˜์กด์„ฑ ๋ฐฉ์ง€, atoms/hooks ๋ ˆ์ด์–ด ์„ค๊ณ„ ์ „๋žต์„ ๋ฐฐ์›๋‹ˆ๋‹ค.

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

  • ๋„๋ฉ”์ธ๋ณ„ atom ํŒŒ์ผ ๋ถ„๋ฆฌ ์ „๋žต์„ ์‹ค๋ฌด ํ”„๋กœ์ ํŠธ์— ์ ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค.
  • ์ˆœํ™˜ ์˜์กด์„ฑ์ด ์ƒ๊ธฐ๋Š” ์›์ธ๊ณผ ํ•ด๊ฒฐ์ฑ…์„ ์„ค๋ช…ํ•  ์ˆ˜ ์žˆ๋‹ค.
  • atoms/ ๋ ˆ์ด์–ด์™€ hooks/ ๋ ˆ์ด์–ด์˜ ์—ญํ• ์„ ๋ช…ํ™•ํžˆ ๊ตฌ๋ถ„ํ•  ์ˆ˜ ์žˆ๋‹ค.

๐Ÿ“‹ ๋ชฉ์ฐจ


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

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

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

[atoms.ts ํ•˜๋‚˜์— 100๊ฐœ ์ง€์˜ฅ] โ†’ [๋„ค์ด๋ฐ ์ปจ๋ฒค์…˜] โ†’ [๋„๋ฉ”์ธ๋ณ„ ๋ถ„๋ฆฌ] โ†’ [์ˆœํ™˜ ์˜์กด์„ฑ ๋ฐฉ์ง€] โ†’ [atoms/hooks ๋ ˆ์ด์–ด] โ†’ [Feature ์Šฌ๋ผ์ด์‹ฑ]

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

์˜์ˆ˜๋„ค ์•ฑ์ด ์Šฌ์Šฌ ์ปค์ง€๋ฉด์„œ atom ๊ด€๋ฆฌ๊ฐ€ ๋ฌธ์ œ๊ฐ€ ๋œ ๋‚ ์ด์•ผ.

๐Ÿฆ ์˜ํ˜ธ ๋‹˜: (์Šคํ”„๋ฆฐํŠธ ํšŒ์˜์—์„œ) "์ด๋ฒˆ ์Šคํ”„๋ฆฐํŠธ์— ๊ธฐ์ˆ  ๋ถ€์ฑ„ ํ•˜๋‚˜ ์ •๋ฆฌํ•ด์•ผ ํ•  ๊ฒƒ ๊ฐ™์•„์š”. atoms.ts ํŒŒ์ผ ํ•œ ๋ฒˆ ์—ด์–ด๋ณผ๊ฒŒ์š”."

(ํŒŒ์ผ์„ ์—ด์ž ์Šคํฌ๋กค์ด ๋์—†์ด ๋‚ด๋ ค๊ฐ„๋‹ค)

๐Ÿฃ ์˜์ฒ : (์กฐ์šฉํžˆ) "...์ €๋„ ์ง€๊ธˆ ๋ญ๊ฐ€ ์–ด๋”” ์žˆ๋Š”์ง€ ๋ชจ๋ฅด๊ฒ ์–ด์š”."

๐Ÿ‘” ์˜์ˆ˜ ๋‹˜: "atom ์ด 100๊ฐœ๊ฐ€ ๋„˜์–ด๊ฐ€๋Š”๋ฐ ํŒŒ์ผ์ด ํ•˜๋‚˜์•ผ? ์ฐพ์„ ์ˆ˜๊ฐ€ ์žˆ์–ด?"

๐Ÿฆ ์˜ํ˜ธ ๋‹˜: "๋งž์•„์š”. ์ง€๊ธˆ searchKeywordAtom ์ด auth ๊ด€๋ จ ์ฝ”๋“œ ์‚ฌ์ด์— ๋ผ์–ด์žˆ๊ณ , isModalOpenAtom ์ด ์Šคํ„ฐ๋”” fetch ๋กœ์ง ๋ฐ‘์— ์žˆ์–ด์š”. ๋ฆฌํŒฉํ† ๋ง ํ•„์š”ํ•ด์š”."

๐Ÿฃ ์˜์ฒ : (์†”์งํ•˜๊ฒŒ) "์ œ๊ฐ€ ์ฒ˜์Œ์— atom ์ถ”๊ฐ€ํ•  ๋•Œ๋งˆ๋‹ค ๊ทธ๋ƒฅ ํŒŒ์ผ ๋งจ ๋ฐ‘์— ๋ถ™์˜€์–ด์š”... ์ฃ„์†กํ•ฉ๋‹ˆ๋‹ค."

๐Ÿฆ ์˜ํ˜ธ ๋‹˜: "์ง€๊ธˆ ์•Œ์•˜์œผ๋ฉด ๋œ ๊ฑฐ์˜ˆ์š”. ์˜ค๋Š˜ ๊ฐ™์ด ์ •๋ฆฌํ•ด๋ด…์‹œ๋‹ค. ๋„๋ฉ”์ธ๋ณ„๋กœ ๋‚˜๋ˆ„๋Š” ์ „๋žต๋ถ€ํ„ฐ ์‹œ์ž‘ํ• ๊ฒŒ์š”."


๐Ÿค” ์™œ atom ๊ตฌ์กฐ ์„ค๊ณ„๊ฐ€ ์ค‘์š”ํ•œ๊ฐ€? ๐ŸŸข

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

  • atoms.ts ํ•˜๋‚˜์— ๋ชจ๋“  atom ์„ ์Œ“๋Š” ๋ฐฉ์‹์˜ ๋ฌธ์ œ์ ์„ ๊ตฌ์ฒด์ ์œผ๋กœ ์„ค๋ช…ํ•  ์ˆ˜ ์žˆ๋‹ค
  • ๊ตฌ์กฐ ์„ค๊ณ„๊ฐ€ ์ฝ”๋“œ ์œ ์ง€๋ณด์ˆ˜์„ฑ์— ๋ฏธ์น˜๋Š” ์˜ํ–ฅ์„ ์ดํ•ดํ•œ๋‹ค

๋ฌด์งˆ์„œํ•œ ๋‹จ์ผ ํŒŒ์ผ์˜ ๋ฌธ์ œ๋“ค

// โŒ ๐Ÿฃ ์˜์ฒ ์˜ atoms.ts โ€” ์‹ค์ œ๋กœ ์ด๋žฌ์Œ (100๊ฐœ ์ด์ƒ)
// src/atoms.ts
 
// ์ธ์ฆ ๊ด€๋ จ
const userAtom = atom<User | null>(null)
const isLoggedInAtom = atom((get) => get(userAtom) !== null)
 
// ์Šคํ„ฐ๋”” ๋ชฉ๋ก
const studyListAtom = atom<Study[]>([])
 
// ๊ฒ€์ƒ‰ (์ธ์ฆ ๊ด€๋ จ ์ฝ”๋“œ๋“ค ์‚ฌ์ด์— ๋ผ์–ด์žˆ์Œ)
const searchKeywordAtom = atom('')
 
// ๋˜ ์ธ์ฆ ๊ด€๋ จ (๊ฒ€์ƒ‰ ์ฝ”๋“œ ๋’ค์— ๋’ค๋Šฆ๊ฒŒ ์ถ”๊ฐ€๋จ)
const authTokenAtom = atom<string | null>(null)
 
// ๋ชจ๋‹ฌ (์Šคํ„ฐ๋”” ์ฝ”๋“œ ์ค‘๊ฐ„์—)
const isCreateStudyModalOpenAtom = atom(false)
 
// ์Šคํ„ฐ๋”” ํ•„ํ„ฐ (๋ชจ๋‹ฌ ์ฝ”๋“œ ๋‹ค์Œ์—)
const selectedTagsAtom = atom<string[]>([])
const sortByAtom = atom<'latest' | 'popular'>('latest')
 
// ์ฑ„ํŒ… (ํ•„ํ„ฐ ์ฝ”๋“œ ์‚ฌ์ด์— ๋ผ์–ด์žˆ์Œ)
const chatRoomIdAtom = atom<string | null>(null)
 
// ... ์ด๋Ÿฐ ์‹์œผ๋กœ 80๊ฐœ ๋” ...

์˜ํ˜ธ ๋‹˜์ด ์ฝ”๋“œ ๋ฆฌ๋ทฐ์—์„œ ์ง€์ ํ•œ ๋ฌธ์ œ๋“ค:

๐Ÿฆ ์˜ํ˜ธ: "์ง€๊ธˆ ์ด ํŒŒ์ผ์—๋Š” ์„ธ ๊ฐ€์ง€ ์‹ฌ๊ฐํ•œ ๋ฌธ์ œ๊ฐ€ ์žˆ์–ด์š”:

1. ๊ด€์‹ฌ์‚ฌ ๋ถ„๋ฆฌ ์‹คํŒจ: auth, study, search, chat ์ด ํ•œ ํŒŒ์ผ์— ๋’ค์„ž์—ฌ ์žˆ์–ด์š”.
   'searchKeywordAtom ์–ด๋”” ์žˆ์–ด?' ๋ผ๊ณ  ๋ฌผ์œผ๋ฉด ์ „์ฒด ๊ฒ€์ƒ‰์„ ํ•ด์•ผ ํ•ด์š”.

2. ํŒ€ ํ˜‘์—… ์ถฉ๋Œ: ์˜์ฒ  ๋‹˜์ด ์Šคํ„ฐ๋”” atom ์ถ”๊ฐ€ํ•˜๊ณ , ๋‚ด๊ฐ€ ์ธ์ฆ atom ์ˆ˜์ •ํ•˜๋ฉด
   ๋งค๋ฒˆ ๊ฐ™์€ ํŒŒ์ผ์— git conflict ๊ฐ€ ๋‚˜์š”.

3. ๋ฒˆ๋“ค ๊ฒฝ๊ณ„ ๋ถˆ๋ช…ํ™•: ๊ฒ€์ƒ‰ ๊ธฐ๋Šฅ๋งŒ ์“ฐ๋Š” ํŽ˜์ด์ง€๋„ chat, auth ๊ด€๋ จ atom ์„
   ์ „๋ถ€ import ํ•˜๊ฒŒ ๋ผ์š”. ํŠธ๋ฆฌ ์‰์ดํ‚น์ด ํž˜๋“ค์–ด์ ธ์š”."

๐Ÿ’ก ํ•œ ์ค„๋กœ ๊ธฐ์–ตํ•˜๊ธฐ
atom ํŒŒ์ผ์ด ํ•˜๋‚˜๋ผ๋Š” ๊ฑด "ํŒ€ ์ „์ฒด๊ฐ€ ํ•˜๋‚˜์˜ ์„œ๋ž์„ ์“ฐ๋Š” ๊ฒƒ" ์ด์•ผ. ๋ˆ„๊ฐ€ ๋ญ˜ ๋„ฃ์—ˆ๋Š”์ง€, ์–ด๋”” ์žˆ๋Š”์ง€ ์ฐพ๊ธฐ ์–ด๋ ต๊ณ  ์ถฉ๋Œ๋„ ์žฆ์•„์ ธ.


๐Ÿท๏ธ ๋„ค์ด๋ฐ ์ปจ๋ฒค์…˜ ๐ŸŸข

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

  • *Atom ์ ‘๋ฏธ์‚ฌ ์ปจ๋ฒค์…˜์ด ์™œ ํŒ€ ํ˜‘์—…์— ๋„์›€์ด ๋˜๋Š”์ง€ ์ดํ•ดํ•œ๋‹ค
  • ์ข‹์€ atom ์ด๋ฆ„๊ณผ ๋‚˜์œ atom ์ด๋ฆ„์„ ๊ตฌ๋ถ„ํ•  ์ˆ˜ ์žˆ๋‹ค

*Atom ์ ‘๋ฏธ์‚ฌ โ€” ์‹๋ณ„์˜ ๊ธฐ์ค€

// โœ… ๊ถŒ์žฅ โ€” ์ ‘๋ฏธ์‚ฌ Atom ์œผ๋กœ ๋๋‚˜๋„๋ก
const searchKeywordAtom = atom('')
const userAtom = atom<User | null>(null)
const studyListAtom = atom<Study[]>([])
const isModalOpenAtom = atom(false)
const filteredStudyListAtom = atom((get) => ...)
const toggleStudyLikeAtom = atom(null, (_, set, id: string) => ...)
 
// โŒ ๋น„๊ถŒ์žฅ โ€” atom ์ธ์ง€ ์‹๋ณ„ ์–ด๋ ค์›€
const searchKeyword = atom('')    // ์ผ๋ฐ˜ ๋ณ€์ˆ˜์ธ์ง€ atom ์ธ์ง€ ๋ถˆ๋ช…ํ™•
const user = atom<User | null>(null)  // ๊ฐ™์€ ์ด๋ฆ„์˜ User ๊ฐ์ฒด์™€ ํ˜ผ๋™
const STUDY_LIST = atom<Study[]>([])  // ์ƒ์ˆ˜์ฒ˜๋Ÿผ ๋ณด์ž„

์ด๋ฆ„ ์ž์ฒด๋กœ ์—ญํ• ์ด ๋“œ๋Ÿฌ๋‚˜๋Š” ํŒจํ„ด

// ์ƒํƒœ ์„ฑ๊ฒฉ์ด ์ด๋ฆ„์—์„œ ๋ช…ํ™•ํžˆ ๋ณด์—ฌ์•ผ ํ•ด
const isLoadingAtom = atom(false)           // boolean ์ƒํƒœ โ€” is/has ์ ‘๋‘์‚ฌ
const selectedStudyIdAtom = atom<string | null>(null)  // ์„ ํƒ ์ƒํƒœ โ€” selected ์ ‘๋‘์‚ฌ
const filteredStudyListAtom = atom(...)     // ํŒŒ์ƒ ์ƒํƒœ โ€” filtered/sorted ์ ‘๋‘์‚ฌ
const toggleLikeAtom = atom(null, ...)      // action atom โ€” ๋™์‚ฌ ์ ‘๋‘์‚ฌ
 
// ๐Ÿฆ ์˜ํ˜ธ: "์ด๋ฆ„๋งŒ ๋ด๋„ '์ด๊ฑด boolean', '์ด๊ฑด action', '์ด๊ฑด derived' ๊ฐ€ ๋ณด์—ฌ์•ผ ํ•ด์š”"

๐Ÿ—‚๏ธ ๋„๋ฉ”์ธ๋ณ„ ํŒŒ์ผ ๋ถ„๋ฆฌ ์ „๋žต ๐ŸŸก

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

  • ์‹ค๋ฌด ํ”„๋กœ์ ํŠธ์— ๋ฐ”๋กœ ์ ์šฉ ๊ฐ€๋Šฅํ•œ atom ํŒŒ์ผ ๋ถ„๋ฆฌ ๊ตฌ์กฐ๋ฅผ ์•Œ ์ˆ˜ ์žˆ๋‹ค
  • ๊ฐ ํŒŒ์ผ์— ์–ด๋–ค atom ์ด ๋“ค์–ด๊ฐ€์•ผ ํ•˜๋Š”์ง€ ํŒ๋‹จํ•  ์ˆ˜ ์žˆ๋‹ค

์˜ํ˜ธ ๋‹˜์ด ์ œ์•ˆํ•œ ์˜์ˆ˜๋„ค ํŒ€์˜ ํŒŒ์ผ ๊ตฌ์กฐ

src/atoms/
โ”œโ”€โ”€ auth.ts        โ† ์ธ์ฆ ๊ด€๋ จ atom
โ”œโ”€โ”€ study.ts       โ† ์Šคํ„ฐ๋”” ๋„๋ฉ”์ธ atom
โ”œโ”€โ”€ search.ts      โ† ๊ฒ€์ƒ‰ ๊ด€๋ จ atom
โ”œโ”€โ”€ ui.ts          โ† UI ์ƒํƒœ atom (๋ชจ๋‹ฌ, ํƒญ ๋“ฑ)
โ”œโ”€โ”€ chat.ts        โ† ์ฑ„ํŒ… ๊ด€๋ จ atom
โ””โ”€โ”€ index.ts       โ† ๊ณต๊ฐœ API (re-export)
// src/atoms/auth.ts
import { atom } from 'jotai'
import type { User } from '@/types'
 
// ๐Ÿฆ ์˜ํ˜ธ: "auth ๋„๋ฉ”์ธ์— ์†ํ•˜๋Š” atom ๋งŒ ์—ฌ๊ธฐ์—"
export const userAtom = atom<User | null>(null)
export const authTokenAtom = atom<string | null>(null)
export const isLoggedInAtom = atom((get) => get(userAtom) !== null)
export const userRoleAtom = atom((get) => get(userAtom)?.role ?? 'guest')
// src/atoms/study.ts
import { atom } from 'jotai'
import { searchKeywordAtom, selectedTagsAtom } from './search'
// โ† search atom ์— ์˜์กด (auth ์—๋Š” ์˜์กด ์•ˆ ํ•จ)
import type { Study } from '@/types'
 
// ๐Ÿฆ ์˜ํ˜ธ: "์Šคํ„ฐ๋”” ๋„๋ฉ”์ธ atom. ํŒŒ์ƒ atom ์€ search atom ์— ์˜์กดํ•ด๋„ OK"
export const studyListAtom = atom<Study[]>([])
export const selectedStudyIdAtom = atom<string | null>(null)
 
// search atom ์œผ๋กœ๋ถ€ํ„ฐ ํŒŒ์ƒ
export const filteredStudyListAtom = atom((get) => {
  const studies = get(studyListAtom)
  const keyword = get(searchKeywordAtom)
  const tags = get(selectedTagsAtom)
 
  return studies
    .filter((s) => s.title.toLowerCase().includes(keyword.toLowerCase()))
    .filter((s) => tags.length === 0 || tags.some((t) => s.tags.includes(t)))
})
// src/atoms/search.ts
import { atom } from 'jotai'
 
// ๐Ÿฆ ์˜ํ˜ธ: "๊ฒ€์ƒ‰ ๊ด€๋ จ ์ˆœ์ˆ˜ ์ƒํƒœ. ๋‹ค๋ฅธ ๋„๋ฉ”์ธ atom ์— ์˜์กดํ•˜์ง€ ์•Š์•„์š”"
export const searchKeywordAtom = atom('')
export const selectedTagsAtom = atom<string[]>([])
export const sortByAtom = atom<'latest' | 'popular'>('latest')
export const isSearchActiveAtom = atom((get) => get(searchKeywordAtom).length > 0)
// src/atoms/ui.ts
import { atom } from 'jotai'
 
// ๐Ÿฆ ์˜ํ˜ธ: "UI ์ƒํƒœ๋Š” ๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง๊ณผ ๋ฌด๊ด€ํ•˜๊ฒŒ ๋…๋ฆฝ์ ์œผ๋กœ ์œ ์ง€ํ•ด์š”"
export const isCreateStudyModalOpenAtom = atom(false)
export const isProfileMenuOpenAtom = atom(false)
export const activeTabAtom = atom<'explore' | 'my-studies' | 'chat'>('explore')
export const sidebarWidthAtom = atom(280)
// src/atoms/index.ts โ€” ๊ณต๊ฐœ API
// ๐Ÿฆ ์˜ํ˜ธ: "์™ธ๋ถ€์—์„œ๋Š” ์ด index.ts ์—์„œ๋งŒ import ํ•˜๋„๋ก ๊ฐ•์ œํ•˜๋ฉด
//          ๋‚ด๋ถ€ ๊ตฌ์กฐ๊ฐ€ ๋ฐ”๋€Œ์–ด๋„ import ๊ฒฝ๋กœ๋ฅผ ์ˆ˜์ •ํ•  ํ•„์š” ์—†์–ด์š”"
export * from './auth'
export * from './study'
export * from './search'
export * from './ui'
export * from './chat'

Before / After ๋น„๊ต

// โŒ Before โ€” ๋ชจ๋“  ํŒŒ์ผ์ด atoms.ts ๋ฅผ import
import { userAtom, searchKeywordAtom, isModalOpenAtom } from '@/atoms'
// atoms.ts ์— 100๊ฐœ atom ์ด ์žˆ๊ณ , ์ด ํŒŒ์ผ์—์„œ 3๊ฐœ๋งŒ ์”€
// โ†’ ๋ฒˆ๋“ค๋Ÿฌ๊ฐ€ 100๊ฐœ๋ฅผ ์ „๋ถ€ ํฌํ•จํ•  ๊ฐ€๋Šฅ์„ฑ
 
// โœ… After โ€” ํ•„์š”ํ•œ ๋„๋ฉ”์ธ ํŒŒ์ผ๋งŒ import (tree-shaking ์นœํ™”์ )
import { userAtom } from '@/atoms/auth'
import { searchKeywordAtom } from '@/atoms/search'
import { isCreateStudyModalOpenAtom } from '@/atoms/ui'
// ๋˜๋Š” index.ts ์—์„œ โ€” ๊ฒฝ๋กœ ์ผ๊ด€์„ฑ ์œ ์ง€
import { userAtom, searchKeywordAtom, isCreateStudyModalOpenAtom } from '@/atoms'

๐Ÿ”„ ์ˆœํ™˜ ์˜์กด์„ฑ ๋ฐฉ์ง€ ๐Ÿ”ด

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

  • ์ˆœํ™˜ ์˜์กด์„ฑ์ด ์–ด๋–ป๊ฒŒ ๋ฐœ์ƒํ•˜๋Š”์ง€ ์ดํ•ดํ•œ๋‹ค
  • ๊ณตํ†ต atoms ๋ถ„๋ฆฌ๋กœ ์ˆœํ™˜ ์˜์กด์„ฑ์„ ํ•ด๊ฒฐํ•˜๋Š” ๋ฒ•์„ ์•Œ ์ˆ˜ ์žˆ๋‹ค

์ˆœํ™˜ ์˜์กด์„ฑ ๋ฐœ์ƒ ์‹œ๋‚˜๋ฆฌ์˜ค

auth.ts       โ†’ study.ts ๋ฅผ import (์Šคํ„ฐ๋”” atom ์— user ๊ด€๋ จ ํ•„ํ„ฐ ์ ์šฉ)
study.ts      โ†’ auth.ts ๋ฅผ import (๋ฉค๋ฒ„ ์ •๋ณด ๊ฐ€์ ธ์˜ค๊ธฐ)

โ†’ ์ˆœํ™˜ ์˜์กด์„ฑ ๋ฐœ์ƒ! ๋ชจ๋“ˆ ๋กœ๋“œ ์ˆœ์„œ ๋ถˆํ™•์‹ค โ†’ ๋Ÿฐํƒ€์ž„ ์—๋Ÿฌ
// โŒ ์ˆœํ™˜ ์˜์กด์„ฑ์ด ์ƒ๊ธฐ๋Š” ํŒจํ„ด
// src/atoms/auth.ts
import { selectedStudyIdAtom } from './study'  // study.ts ๋ฅผ import
 
export const userStudyRoleAtom = atom((get) => {
  const studyId = get(selectedStudyIdAtom) // study atom ์— ์˜์กด
  const user = get(userAtom)
  // ...
})
 
// src/atoms/study.ts
import { userAtom } from './auth'  // auth.ts ๋ฅผ import โ† ์ˆœํ™˜!
 
export const studyMembersAtom = atom((get) => {
  const user = get(userAtom)  // auth atom ์— ์˜์กด
  // ...
})

ํ•ด๊ฒฐ์ฑ…: ๊ณตํ†ต atoms ๋ฅผ ๋ณ„๋„ ํŒŒ์ผ๋กœ ๋ถ„๋ฆฌ

src/atoms/
โ”œโ”€โ”€ core.ts    โ† ๋‹ค๋ฅธ atom ์— ์˜์กดํ•˜์ง€ ์•Š๋Š” ๊ธฐ๋ฐ˜ atom (์ˆœํ™˜ ์˜์กด ์—†์Œ)
โ”œโ”€โ”€ auth.ts    โ† core.ts ์—๋งŒ ์˜์กด
โ”œโ”€โ”€ study.ts   โ† core.ts ์—๋งŒ ์˜์กด
โ”œโ”€โ”€ derived.ts โ† auth.ts, study.ts ์— ์˜์กดํ•˜๋Š” ํŒŒ์ƒ atom
โ””โ”€โ”€ index.ts
// src/atoms/core.ts โ€” ๋ชจ๋“  ํŒŒ์ผ์˜ ๊ธฐ๋ฐ˜
// ๐Ÿฆ ์˜ํ˜ธ: "๋‹ค๋ฅธ ํŒŒ์ผ์— ์˜์กดํ•˜์ง€ ์•Š๋Š” ์ˆœ์ˆ˜ ๊ธฐ๋ฐ˜ atom ๋งŒ ์—ฌ๊ธฐ์—"
export const userAtom = atom<User | null>(null)
export const studyListAtom = atom<Study[]>([])
export const searchKeywordAtom = atom('')
// src/atoms/auth.ts โ€” core.ts ์—๋งŒ ์˜์กด
import { userAtom } from './core'
 
export const isLoggedInAtom = atom((get) => get(userAtom) !== null)
export const userRoleAtom = atom((get) => get(userAtom)?.role ?? 'guest')
// src/atoms/study.ts โ€” core.ts ์—๋งŒ ์˜์กด
import { studyListAtom, searchKeywordAtom } from './core'
 
export const filteredStudyListAtom = atom((get) => {
  const studies = get(studyListAtom)
  const keyword = get(searchKeywordAtom)
  return studies.filter((s) => s.title.includes(keyword))
})
// src/atoms/derived.ts โ€” auth.ts, study.ts ์–‘์ชฝ์— ์˜์กด
// ๐Ÿฆ ์˜ํ˜ธ: "๋‘ ๋„๋ฉ”์ธ์„ ๊ฒฐํ•ฉํ•œ ํŒŒ์ƒ ์ƒํƒœ๋Š” ๋ณ„๋„ ํŒŒ์ผ์— ๊ฒฉ๋ฆฌํ•ด์š”"
import { userAtom } from './auth'
import { studyListAtom } from './study'
 
export const myStudiesAtom = atom((get) => {
  const user = get(userAtom)
  const studies = get(studyListAtom)
  if (!user) return []
  return studies.filter((s) => s.members.some((m) => m.id === user.id))
})

๐Ÿ’ก ํ•œ ์ค„๋กœ ๊ธฐ์–ตํ•˜๊ธฐ
์ˆœํ™˜ ์˜์กด์„ฑ์˜ ํ•ด๋ฒ•์€ ํ•ญ์ƒ ๊ฐ™์•„. "๋‘ ๋ชจ๋“ˆ์ด ์„œ๋กœ๋ฅผ ๋ฐ”๋ผ๋ณด๋ฉด, ๋‘ ๋ชจ๋“ˆ์ด ๊ณตํ†ต์œผ๋กœ ๋ฐ”๋ผ๋ณผ ์„ธ ๋ฒˆ์งธ ๋ชจ๋“ˆ์„ ๋งŒ๋“ค์–ด."


๐ŸŽฏ atoms vs hooks ๋ ˆ์ด์–ด ์„ค๊ณ„ ๐ŸŸก

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

  • atoms/ ๋ ˆ์ด์–ด์™€ hooks/ ๋ ˆ์ด์–ด์˜ ์ฑ…์ž„ ์ฐจ์ด๋ฅผ ๋ช…ํ™•ํžˆ ์„ค๋ช…ํ•  ์ˆ˜ ์žˆ๋‹ค
  • ์ปค์Šคํ…€ ํ›…์œผ๋กœ atom ์„ ์บก์Аํ™”ํ•˜๋Š” ์‹ค๋ฌด ํŒจํ„ด์„ ์ ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค

๋‘ ๋ ˆ์ด์–ด์˜ ์—ญํ•  ๋ถ„๋ฆฌ

src/
โ”œโ”€โ”€ atoms/         โ† "๋ฌด์—‡์„ ์ €์žฅํ•  ๊ฒƒ์ธ๊ฐ€" โ€” ์ˆœ์ˆ˜ํ•œ ์ƒํƒœ ์„ ์–ธ
โ”‚   โ”œโ”€โ”€ auth.ts
โ”‚   โ”œโ”€โ”€ study.ts
โ”‚   โ””โ”€โ”€ search.ts
โ”‚
โ””โ”€โ”€ hooks/         โ† "์ปดํฌ๋„ŒํŠธ์—๊ฒŒ ์–ด๋–ค ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ์ค„ ๊ฒƒ์ธ๊ฐ€" โ€” ๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง ์กฐํ•ฉ
    โ”œโ”€โ”€ useStudySearch.ts
    โ”œโ”€โ”€ useAuth.ts
    โ””โ”€โ”€ useStudyActions.ts
// src/hooks/useStudySearch.ts
// ๐Ÿฆ ์˜ํ˜ธ: "์ปดํฌ๋„ŒํŠธ๋Š” atom ์„ ์ง์ ‘ ์“ฐ๋Š” ๊ฒƒ๋ณด๋‹ค ์ด ํ›…์„ ์“ฐ๋Š” ๊ฒŒ ์ข‹์•„์š”.
//          atom ์ด ๋ฐ”๋€Œ์–ด๋„ ์ปดํฌ๋„ŒํŠธ ์ฝ”๋“œ๋ฅผ ์ˆ˜์ •ํ•  ํ•„์š”๊ฐ€ ์—†๊ฑฐ๋“ ์š”."
import { useAtom, useAtomValue, useSetAtom } from 'jotai'
import { searchKeywordAtom, selectedTagsAtom, sortByAtom } from '@/atoms/search'
import { filteredStudyListAtom } from '@/atoms/study'
import { resetSearchAtom } from '@/atoms/search'
 
export function useStudySearch() {
  const [keyword, setKeyword] = useAtom(searchKeywordAtom)
  const [selectedTags, setSelectedTags] = useAtom(selectedTagsAtom)
  const [sortBy, setSortBy] = useAtom(sortByAtom)
  const results = useAtomValue(filteredStudyListAtom)
  const resetSearch = useSetAtom(resetSearchAtom)
 
  const addTag = (tag: string) => {
    setSelectedTags((prev) => [...new Set([...prev, tag])])
  }
 
  const removeTag = (tag: string) => {
    setSelectedTags((prev) => prev.filter((t) => t !== tag))
  }
 
  return {
    keyword,
    setKeyword,
    selectedTags,
    addTag,
    removeTag,
    sortBy,
    setSortBy,
    results,
    resetSearch,
    hasActiveFilter: keyword.length > 0 || selectedTags.length > 0,
  }
}
// ์ปดํฌ๋„ŒํŠธ โ€” atom ์„ ์ง์ ‘ ์•Œ ํ•„์š” ์—†์Œ
const StudySearchPanel = () => {
  // ๐Ÿฃ ์˜์ฒ : "์ปดํฌ๋„ŒํŠธ์—์„œ atom import ๋ฅผ ํ•˜๋‚˜๋„ ์•ˆ ํ•ด๋„ ๋˜๋Š” ๊ฑฐ์˜ˆ์š”?"
  // ๐Ÿฆ ์˜ํ˜ธ: "๋งž์•„์š”. ํ›…์ด atom ์˜ ๋ณต์žก์„ฑ์„ ๊ฐ์ถฐ์ค˜์š”.
  //          ๋‚˜์ค‘์— atom ์ด๋ฆ„์ด ๋ฐ”๋€Œ์–ด๋„ ์ด ์ปดํฌ๋„ŒํŠธ๋Š” ์ˆ˜์ • ์•ˆ ํ•ด๋„ ๋ผ์š”."
  const { keyword, setKeyword, results, selectedTags, addTag, removeTag, hasActiveFilter } =
    useStudySearch()
 
  return (
    <div>
      <input value={keyword} onChange={(e) => setKeyword(e.target.value)} placeholder="๊ฒ€์ƒ‰..." />
      <TagFilter tags={selectedTags} onAdd={addTag} onRemove={removeTag} />
      {hasActiveFilter && <span>{results.length}๊ฐœ ๊ฒฐ๊ณผ</span>}
      <StudyList studies={results} />
    </div>
  )
}
// src/hooks/useAuth.ts
import { useAtom, useAtomValue, useSetAtom } from 'jotai'
import { userAtom, isLoggedInAtom, authTokenAtom } from '@/atoms/auth'
 
export function useAuth() {
  const [user, setUser] = useAtom(userAtom)
  const isLoggedIn = useAtomValue(isLoggedInAtom)
  const setToken = useSetAtom(authTokenAtom)
 
  const login = async (credentials: LoginCredentials) => {
    const { user, token } = await authApi.login(credentials)
    setUser(user)
    setToken(token)
  }
 
  const logout = () => {
    setUser(null)
    setToken(null)
  }
 
  return { user, isLoggedIn, login, logout }
}

atoms/ ๋ ˆ์ด์–ด์˜ ์ˆœ์ˆ˜์„ฑ ์œ ์ง€

// โœ… atoms/ ์—๋Š” ์ด๊ฒƒ๋งŒ โ€” ์ƒํƒœ ์„ ์–ธ๊ณผ ํŒŒ์ƒ ๊ณ„์‚ฐ
// src/atoms/search.ts
export const searchKeywordAtom = atom('')
export const selectedTagsAtom = atom<string[]>([])
 
// write-only reset atom (action ์— ํ•ด๋‹นํ•˜์ง€๋งŒ ์ˆœ์ˆ˜ํ•œ ์ƒํƒœ ์กฐ์ž‘)
export const resetSearchAtom = atom(null, (_get, set) => {
  set(searchKeywordAtom, '')
  set(selectedTagsAtom, [])
})
 
// โŒ atoms/ ์—๋Š” ์ด๋Ÿฐ ๊ฒƒ์ด ๋“ค์–ด๊ฐ€๋ฉด ์•ˆ ๋จ
// fetch, console.log, router.push ๊ฐ™์€ ์‚ฌ์ด๋“œ ์ดํŽ™ํŠธ
// ์ปดํฌ๋„ŒํŠธ ๋ผ์ดํ”„์‚ฌ์ดํด ์—ฐ๋™ (useEffect ๋“ฑ)
// ์ด๋Ÿฐ ๊ฑด hooks/ ๋ ˆ์ด์–ด์—์„œ ์ฒ˜๋ฆฌ

๐Ÿข Feature ๋‹จ์œ„ atom ์Šฌ๋ผ์ด์‹ฑ (๋Œ€๊ทœ๋ชจ ์•ฑ) ๐Ÿ”ด

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

  • Feature ๊ธฐ๋ฐ˜ atom ๊ตฌ์กฐ์™€ ๋„๋ฉ”์ธ ๊ธฐ๋ฐ˜ ๊ตฌ์กฐ์˜ ์ฐจ์ด๋ฅผ ์ดํ•ดํ•œ๋‹ค
  • ๋Œ€๊ทœ๋ชจ ์•ฑ์—์„œ Feature ์Šฌ๋ผ์ด์‹ฑ์ด ์™œ ์œ ๋ฆฌํ•œ์ง€ ์„ค๋ช…ํ•  ์ˆ˜ ์žˆ๋‹ค

Feature ์Šฌ๋ผ์ด์‹ฑ ๊ตฌ์กฐ

src/features/
โ”œโ”€โ”€ auth/
โ”‚   โ”œโ”€โ”€ atoms/
โ”‚   โ”‚   โ”œโ”€โ”€ user.ts
โ”‚   โ”‚   โ””โ”€โ”€ session.ts
โ”‚   โ”œโ”€โ”€ hooks/
โ”‚   โ”‚   โ””โ”€โ”€ useAuth.ts
โ”‚   โ””โ”€โ”€ components/
โ”‚       โ”œโ”€โ”€ LoginForm.tsx
โ”‚       โ””โ”€โ”€ UserAvatar.tsx
โ”‚
โ”œโ”€โ”€ study/
โ”‚   โ”œโ”€โ”€ atoms/
โ”‚   โ”‚   โ”œโ”€โ”€ list.ts
โ”‚   โ”‚   โ”œโ”€โ”€ detail.ts
โ”‚   โ”‚   โ””โ”€โ”€ filter.ts
โ”‚   โ”œโ”€โ”€ hooks/
โ”‚   โ”‚   โ”œโ”€โ”€ useStudyList.ts
โ”‚   โ”‚   โ””โ”€โ”€ useStudyActions.ts
โ”‚   โ””โ”€โ”€ components/
โ”‚       โ”œโ”€โ”€ StudyCard.tsx
โ”‚       โ””โ”€โ”€ StudyList.tsx
โ”‚
โ””โ”€โ”€ chat/
    โ”œโ”€โ”€ atoms/
    โ”‚   โ””โ”€โ”€ chatRoom.ts
    โ”œโ”€โ”€ hooks/
    โ”‚   โ””โ”€โ”€ useChatRoom.ts
    โ””โ”€โ”€ components/
        โ””โ”€โ”€ ChatWindow.tsx
// src/features/study/atoms/filter.ts
// ๐Ÿฆ ์˜ํ˜ธ: "feature ๋‚ด๋ถ€์—์„œ๋งŒ ์“ฐ๋Š” atom ์€ feature ์•ˆ์— ๊ฒฉ๋ฆฌํ•ด์š”"
import { atom } from 'jotai'
 
export const studyFilterAtom = atom({
  keyword: '',
  tags: [] as string[],
  sortBy: 'latest' as 'latest' | 'popular',
  category: null as string | null,
})
 
// feature ๋‚ด๋ถ€์—์„œ๋งŒ ์“ฐ๋Š” derived atom
export const hasActiveFilterAtom = atom((get) => {
  const filter = get(studyFilterAtom)
  return filter.keyword.length > 0 || filter.tags.length > 0 || filter.category !== null
})

์ „์—ญ ๊ณต์œ  vs Feature ๋กœ์ปฌ โ€” ์„ ํƒ ๊ธฐ์ค€

์–ด๋””์— atom ์„ ๋ฐฐ์น˜ํ• ์ง€ ํŒ๋‹จํ•˜๋Š” ๊ธฐ์ค€:

โœ… src/atoms/ (์ „์—ญ)
  - ๋‘ ๊ฐœ ์ด์ƒ์˜ feature ์—์„œ ์‚ฌ์šฉ
  - ์•ฑ ์ „์ฒด ์ˆ˜๋ช… ๋™์•ˆ ์œ ์ง€
  - ์˜ˆ: userAtom, authTokenAtom

โœ… src/features/{feature}/atoms/ (๋กœ์ปฌ)
  - ์ด feature ์—์„œ๋งŒ ์‚ฌ์šฉ
  - feature ์˜ ๋ผ์ดํ”„์‚ฌ์ดํด๊ณผ ๋™์ผ
  - ์˜ˆ: studyFilterAtom, chatRoomStateAtom

๐Ÿ’ก ํŒ ์ฒ˜์Œ์—๋Š” ์ „์—ญ src/atoms/ ์œผ๋กœ ์‹œ์ž‘ํ•˜๊ณ , feature ๊ฐ€ ๋…๋ฆฝ์ ์œผ๋กœ ๋ฐฐํฌ/๋ถ„๋ฆฌ๋  ๊ฐ€๋Šฅ์„ฑ์ด ๋ณด์ด๋ฉด feature ๋‚ด๋ถ€๋กœ ์ด๋™ํ•˜๋Š” ์ „๋žต์ด ์ข‹์•„. ๊ณผ๋„ํ•œ ์‚ฌ์ „ ์„ค๊ณ„๋ณด๋‹ค ์ ์ง„์  ๋ฆฌํŒฉํ† ๋ง์ด ํ˜„์‹ค์ ์ด์•ผ.


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


โŒ ReferenceError: Cannot access 'atomX' before initialization

์ฆ์ƒ: ์•ฑ ์‹œ์ž‘ ์‹œ "Cannot access before initialization" ์—๋Ÿฌ

์›์ธ:

// src/atoms/study.ts
import { userFilteredStudiesAtom } from './derived'
// derived.ts ๊ฐ€ study.ts ๋ฅผ import ํ•˜๊ณ , study.ts ๊ฐ€ derived.ts ๋ฅผ import โ†’ ์ˆœํ™˜!

ํ•ด๊ฒฐ์ฑ…:

// ๊ณตํ†ต ๊ธฐ๋ฐ˜ atom ์„ core.ts ๋กœ ๋ถ„๋ฆฌ (์œ„ ์ˆœํ™˜ ์˜์กด์„ฑ ์„น์…˜ ์ฐธ๊ณ )
// ๋˜๋Š” derived atom ์„ ์‚ฌ์šฉํ•˜๋Š” ํŒŒ์ผ๋กœ ์ด๋™ (์ธ๋ผ์ธํ™”)

โŒ ESLint import/no-cycle ๊ฒฝ๊ณ 

์ฆ์ƒ: ESLint ๊ฐ€ ์ˆœํ™˜ import ๋ฅผ ๊ฒฝ๊ณ 

ํ•ด๊ฒฐ์ฑ…:

// .eslintrc.js ์— import/no-cycle ๊ทœ์น™ ์ถ”๊ฐ€ (์‚ฌ์ „ ๋ฐฉ์ง€)
rules: {
  'import/no-cycle': 'error'
}
 
// ๊ฒฝ๊ณ  ๋ฐœ์ƒ ์‹œ ์œ„์˜ "์ˆœํ™˜ ์˜์กด์„ฑ ๋ฐฉ์ง€" ์„น์…˜์˜ ํ•ด๊ฒฐ์ฑ… ์ ์šฉ

โŒ atom ์„ ์–ด๋””์„œ import ํ•ด์•ผ ํ• ์ง€ ๋ชจ๋ฅด๊ฒ ์Œ

์ฆ์ƒ: ํŒ€์›์ด ๊ฐ์ž ๋‹ค๋ฅธ ๊ฒฝ๋กœ๋กœ atom ์„ import ํ•ด์„œ ์ผ๊ด€์„ฑ ์—†์Œ

ํ•ด๊ฒฐ์ฑ…:

// src/atoms/index.ts ๋ฅผ ๊ณต๊ฐœ API ๋กœ ์„ค์ • ํ›„
// eslint ์˜ no-restricted-imports ๋กœ ์ง์ ‘ import ๊ธˆ์ง€
 
// .eslintrc.js
rules: {
  'no-restricted-imports': ['error', {
    patterns: ['@/atoms/*'], // ํ•˜์œ„ ํŒŒ์ผ ์ง์ ‘ import ๊ธˆ์ง€
  }],
}
 
// โœ… ํ—ˆ์šฉ
import { userAtom } from '@/atoms'
 
// โŒ ๊ธˆ์ง€ (eslint ์—๋Ÿฌ)
import { userAtom } from '@/atoms/auth'

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

๐Ÿ“‹ atom ๊ตฌ์กฐ ์„ค๊ณ„ ์›์น™

์›์น™์„ค๋ช…
*Atom ์ ‘๋ฏธ์‚ฌ๋ชจ๋“  atom ์ด๋ฆ„์— Atom ์ ‘๋ฏธ์‚ฌ โ†’ ์ฆ‰๊ฐ์ ์ธ ์‹๋ณ„
๋„๋ฉ”์ธ๋ณ„ ํŒŒ์ผ ๋ถ„๋ฆฌauth.ts, study.ts, search.ts, ui.ts ๋“ฑ
์˜์กด์„ฑ ๋ฐฉํ–ฅ ๋‹จ์ผํ™”A โ†’ B (OK), A โ†” B (์ˆœํ™˜ โ€” ๊ธˆ์ง€)
๊ณตํ†ต ๋ถ„๋ฆฌ์ˆœํ™˜ ๋ฐœ์ƒ ์‹œ ๊ณตํ†ต ๋ถ€๋ถ„์„ core.ts ๋กœ ์ถ”์ถœ
atoms/hooks ๋ ˆ์ด์–ดatoms ๋Š” ์ˆœ์ˆ˜ ์ƒํƒœ ์„ ์–ธ, hooks ๋Š” ๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง ์กฐํ•ฉ

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

์ƒํ™ฉโŒ ๋‚˜์œ ์˜ˆโœ… ์ข‹์€ ์˜ˆ
ํŒŒ์ผ ๊ตฌ์กฐ๋ชจ๋“  atom ์„ atoms.ts ํ•˜๋‚˜์—๋„๋ฉ”์ธ๋ณ„๋กœ ๋ถ„๋ฆฌ
์ˆœํ™˜ ์˜์กด์„ฑA ๊ฐ€ B ๋ฅผ import, B ๊ฐ€ A ๋ฅผ import๊ณตํ†ต core.ts ๋กœ ๋ถ„๋ฆฌ
atoms ๋ ˆ์ด์–ดfetch, sideEffect ๋ฅผ atoms ์—์„œ ์‹คํ–‰์‚ฌ์ด๋“œ ์ดํŽ™ํŠธ๋Š” hooks ๋ ˆ์ด์–ด๋กœ

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

Q1. ๐Ÿ—๏ธ ์•„ํ‚คํ…์ฒ˜ ์„ค๊ณ„ (ํŒ€ ์ „์ฒด ํšŒ์˜)

์˜์ˆ˜๋„ค ํŒ€์ด ๊ธฐ์ˆ  ๋ถ€์ฑ„ ํšŒ์˜๋ฅผ ํ–ˆ์–ด. ํ˜„์žฌ atoms.ts ํŒŒ์ผ ํ•˜๋‚˜์— 120๊ฐœ์˜ atom ์ด ์žˆ์–ด.
์˜ํ˜ธ ๋‹˜์ด ์ œ์•ˆํ•œ ๋ฆฌํŒฉํ† ๋ง ๋ฐฉํ–ฅ์œผ๋กœ ๊ฐ€์žฅ ์ ์ ˆํ•œ ๊ฒƒ์€?

  • A) atom ์˜ ์ˆซ์ž๋ฅผ ์ค„์ด๊ธฐ ์œ„ํ•ด ์—ฌ๋Ÿฌ atom ์„ ํ•˜๋‚˜์˜ ํฐ ๊ฐ์ฒด atom ์œผ๋กœ ํ•ฉ์นœ๋‹ค
  • B) ๋„๋ฉ”์ธ๋ณ„(auth, study, search, ui)๋กœ ํŒŒ์ผ์„ ๋ถ„๋ฆฌํ•˜๊ณ , index.ts ์—์„œ re-export ํ•˜์—ฌ ๊ณต๊ฐœ API ๋ฅผ ์ผ์›ํ™”ํ•œ๋‹ค
  • C) atom ์„ ์“ฐ๋Š” ์ปดํฌ๋„ŒํŠธ ํŒŒ์ผ ์•ˆ์— ๊ฐ๊ฐ ์„ ์–ธํ•œ๋‹ค
  • D) ๋ชจ๋“  atom ์„ ํ•˜๋‚˜์˜ Redux ์Šคํ† ์–ด๋กœ ๋งˆ์ด๊ทธ๋ ˆ์ด์…˜ํ•œ๋‹ค

โœ… ์ •๋‹ต: B

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

  • ์›๋ฆฌ ์„ค๋ช…: ๋„๋ฉ”์ธ๋ณ„ ํŒŒ์ผ ๋ถ„๋ฆฌ๋Š” ์„ธ ๊ฐ€์ง€ ๋ฌธ์ œ๋ฅผ ๋™์‹œ์— ํ•ด๊ฒฐํ•ด. (1) ๊ด€์‹ฌ์‚ฌ ๋ถ„๋ฆฌ: ๊ฐ™์€ ๋„๋ฉ”์ธ atom ์ด ํ•œ ํŒŒ์ผ์— โ†’ ์ฐพ๊ธฐ ์‰ฌ์›€. (2) ํŒ€ ํ˜‘์—…: ๊ฐ์ž ๋‹ค๋ฅธ ํŒŒ์ผ์—์„œ ์ž‘์—… โ†’ git conflict ๊ฐ์†Œ. (3) ๋ฒˆ๋“ค ์ตœ์ ํ™”: ํ•„์š”ํ•œ ๋„๋ฉ”์ธ ํŒŒ์ผ๋งŒ import ๊ฐ€๋Šฅ. index.ts re-export ๋Š” ๋‚ด๋ถ€ ๊ตฌ์กฐ ๋ณ€๊ฒฝ ์‹œ ์™ธ๋ถ€ import ๊ฒฝ๋กœ๋ฅผ ๋ฐ”๊พธ์ง€ ์•Š์•„๋„ ๋˜๊ฒŒ ํ•ด์ค˜.
  • ์˜ค๋‹ต ํ”ผ๋“œ๋ฐฑ: A ๋Š” ์˜คํžˆ๋ ค ์ปดํฌ๋„ŒํŠธ ๋ฆฌ๋ Œ๋”๋ง ์ตœ์ ํ™”๊ฐ€ ๊นจ์ ธ โ€” Jotai ์˜ ํ•ต์‹ฌ ์žฅ์ ์ธ ์›์ž ๋‹จ์œ„ ๊ตฌ๋…์ด ์‚ฌ๋ผ์ ธ. C ๋Š” atom ์ด ์ปดํฌ๋„ŒํŠธ ์ˆ˜๋งŒํผ ๋ถ„์‚ฐ โ†’ ์žฌ์‚ฌ์šฉ ๋ถˆ๊ฐ€. D ๋Š” ์™„์ „ํžˆ ๋‹ค๋ฅธ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋กœ์˜ ์ „ํ™˜ โ€” ๊ธฐ์ˆ  ๋ถ€์ฑ„ ํ•ด๊ฒฐ์ด ์•„๋‹ˆ์•ผ.
  • ๐Ÿ“Œ ํ•ต์‹ฌ ๊ธฐ์–ต๋ฒ•: "atom ๋„ ์ฝ”๋“œ์•ผ. ์ฝ”๋“œ๋Š” ๊ด€์‹ฌ์‚ฌ๋ณ„๋กœ ๋ชจ์•„์•ผ ํ•ด."

Q2. ๐Ÿ”ฅ ๊ธด๊ธ‰ ๋””๋ฒ„๊น… (์˜์ˆ˜์˜ ํ˜ธํ†ต)

๋ฐฐํฌ ํ›„ ์˜์ˆ˜ ๋‹˜์ด ์—ฐ๋ฝ์™”์–ด: "์•ฑ ์ฒ˜์Œ ๋กœ๋“œํ•  ๋•Œ ์ฝ˜์†”์— ReferenceError ๊ฐ€ ๊ฐ„ํ—์ ์œผ๋กœ ๋‚˜์š”."
์˜์ฒ ์ด๊ฐ€ ์ฝ”๋“œ๋ฅผ ๋ณด๋‹ˆ:

// atoms/auth.ts
import { selectedStudyAtom } from './study'
export const userStudyRoleAtom = atom((get) => {
  const studyId = get(selectedStudyAtom)?.id
  const user = get(userAtom)
  // ...
})
 
// atoms/study.ts
import { userAtom } from './auth'
export const studyMembersAtom = atom((get) => {
  const user = get(userAtom)
  // ...
})

์›์ธ๊ณผ ํ•ด๊ฒฐ์ฑ…์€?

  • A) atom ์˜ ์ดˆ๊ธฐ๊ฐ’์ด ์ž˜๋ชป๋๋‹ค. ์ดˆ๊ธฐ๊ฐ’์„ null ์—์„œ undefined ๋กœ ๋ฐ”๊พผ๋‹ค
  • B) auth.ts ์™€ study.ts ์‚ฌ์ด์— ์ˆœํ™˜ ์˜์กด์„ฑ์ด ์žˆ๋‹ค. ๊ณตํ†ต ๊ธฐ๋ฐ˜ atom ์„ core.ts ๋กœ ๋ถ„๋ฆฌํ•œ๋‹ค
  • C) Provider ๊ฐ€ ์—†์–ด์„œ ์ƒ๊ธฐ๋Š” ๋ฌธ์ œ๋‹ค. layout.tsx ์— Provider ๋ฅผ ์ถ”๊ฐ€ํ•œ๋‹ค
  • D) atom ์ด ๋„ˆ๋ฌด ๋งŽ์•„์„œ ์ƒ๊ธฐ๋Š” ๋ฌธ์ œ๋‹ค. atom ์ˆ˜๋ฅผ ์ค„์ธ๋‹ค

โœ… ์ •๋‹ต: B

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

  • ์›๋ฆฌ ์„ค๋ช…: auth.ts ๊ฐ€ study.ts ๋ฅผ import ํ•˜๊ณ , study.ts ๊ฐ€ auth.ts ๋ฅผ import ํ•˜๋ฉด ์ˆœํ™˜ ์˜์กด์„ฑ์ด ์ƒ๊ฒจ. JavaScript ๋ชจ๋“ˆ ๋กœ๋”๋Š” ์ˆœํ™˜ ์ฐธ์กฐ๊ฐ€ ์žˆ์„ ๋•Œ ์ผ๋ถ€ ๋ชจ๋“ˆ์ด ์™„์ „ํžˆ ์ดˆ๊ธฐํ™”๋˜๊ธฐ ์ „์— ์‚ฌ์šฉ๋˜๋Š” ์ƒํ™ฉ์ด ์ƒ๊ธฐ๊ณ , ์ด ๋•Œ "Cannot access before initialization" ์—๋Ÿฌ๊ฐ€ ๋‚˜. ํ•ด๊ฒฐ์ฑ…์€ userAtom ์ฒ˜๋Ÿผ ๋‘ ํŒŒ์ผ์ด ๊ณตํ†ต์œผ๋กœ ํ•„์š”ํ•œ atom ์„ core.ts ๊ฐ™์€ ๋ณ„๋„ ํŒŒ์ผ๋กœ ์ด๋™ํ•ด์„œ A โ†’ core, B โ†’ core ์˜ ๋‹จ๋ฐฉํ–ฅ ์˜์กด์„ฑ์„ ๋งŒ๋“œ๋Š” ๊ฑฐ์•ผ.
  • ๐Ÿ“Œ ํ•ต์‹ฌ ๊ธฐ์–ต๋ฒ•: "์ˆœํ™˜ ์˜์กด์„ฑ = ๋‘˜์ด ์„œ๋กœ ๋ฐ”๋ผ๋ด„. ํ•ด๊ฒฐ์ฑ… = ๊ณตํ†ต์œผ๋กœ ๋ฐ”๋ผ๋ณผ ์„ธ ๋ฒˆ์งธ๋ฅผ ๋งŒ๋“ค๊ธฐ."

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

atoms/ ๋ ˆ์ด์–ด์™€ hooks/ ๋ ˆ์ด์–ด๋ฅผ ๋ถ„๋ฆฌํ•˜๋Š” ์ด์œ ๋ฅผ ๊ฐœ๋ฐœ์ž ์นœ๊ตฌ์—๊ฒŒ ์„ค๋ช…ํ•ด๋ด.

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

"atoms/ ๋Š” '์ƒํƒœ๋ฅผ ์–ด๋–ป๊ฒŒ ์ €์žฅํ•  ๊ฒƒ์ธ๊ฐ€' ๋งŒ ๋‹ด๋‹นํ•˜๊ณ , hooks/ ๋Š” '์ปดํฌ๋„ŒํŠธ์—๊ฒŒ ์–ด๋–ค ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ์ค„ ๊ฒƒ์ธ๊ฐ€' ๋ฅผ ๋‹ด๋‹นํ•ด. ๋ถ„๋ฆฌํ•˜๋ฉด ๋‘ ๊ฐ€์ง€ ์ด์ ์ด ์žˆ์–ด. ์ฒซ์งธ, atom ์ด๋ฆ„์ด ๋ฐ”๋€Œ์–ด๋„ hooks ๋งŒ ๊ณ ์น˜๋ฉด ์ปดํฌ๋„ŒํŠธ๋Š” ์ˆ˜์ • ์•ˆ ํ•ด๋„ ๋ผ. ๋‘˜์งธ, ์ปดํฌ๋„ŒํŠธ๊ฐ€ atom ์˜ ๋ณต์žกํ•œ ์กฐํ•ฉ ๋กœ์ง์„ ๋ชฐ๋ผ๋„ ๋˜๋‹ˆ๊นŒ ๋‹จ์ˆœํ•ด์ ธ. hooks ๊ฐ€ atom ๋“ค์˜ ๋ณต์žก์„ฑ์„ ์บก์Аํ™”ํ•ด์ฃผ๋Š” ๊ฑฐ์•ผ."

๐Ÿ’ก ์ด ๊ฐœ๋…์„ ์ดํ•ดํ–ˆ๋‹ค๋ฉด: ๋ ˆ์ด์–ด๋“œ ์•„ํ‚คํ…์ฒ˜์˜ ํ•ต์‹ฌ ๊ฐ€์น˜์ธ "์บก์Аํ™”์™€ ์˜์กด์„ฑ ์—ญ์ „" ์„ ์ดํ•ดํ•œ ๊ฑฐ์•ผ. ๋‹ค์Œ ๊ฐ€์ด๋“œ๋กœ ๋„˜์–ด๊ฐ€๋„ ์ถฉ๋ถ„ํ•ด!


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

์˜ค๋Š˜ atoms.ts ๋ฅผ ์—ด์—ˆ์„ ๋•Œ ์˜ํ˜ธ ๋‹˜์ด ์ž ๊น ์นจ๋ฌตํ•˜์‹  ๊ฒŒ ์ž๊พธ ์ƒ๊ฐ๋‚œ๋‹ค. ๊ทธ ์นจ๋ฌต์ด ๋‚˜ํ•œํ… "์˜์ฒ ์•„, ์ด๊ฒŒ ๋งž๋‹ˆ?" ์ฒ˜๋Ÿผ ๋“ค๋ ธ์–ด.

์†”์งํžˆ atom ์ถ”๊ฐ€ํ•  ๋•Œ๋งˆ๋‹ค ๊ทธ๋ƒฅ ํŒŒ์ผ ๋งจ ๋ฐ‘์— ๋ถ™์ธ ๊ฒŒ ๋งž์•„. ๊ท€์ฐฎ๊ธฐ๋„ ํ–ˆ๊ณ , ์–ด๋”” ๋„ฃ์–ด์•ผ ํ•˜๋Š”์ง€ ๊ทœ์น™์ด ์—†์œผ๋‹ˆ๊นŒ. ๊ทผ๋ฐ ๊ทธ๊ฒŒ ์Œ“์ด๋‹ˆ๊นŒ ์ด์ œ 100๊ฐœ ๋„˜๋Š” atom ์ด ํ•œ ํŒŒ์ผ์—์„œ ๋’ค์—‰ํ‚จ ๊ฑฐ์ž–์•„.

๋ฆฌํŒฉํ† ๋งํ•˜๋ฉด์„œ auth.ts, study.ts, search.ts, ui.ts ๋กœ ๋‚˜๋ˆ„๊ณ  ๋‚˜๋‹ˆ๊นŒ, ์ง„์งœ ์‹ ๊ธฐํ•˜๊ฒŒ๋„ ๋ญ๊ฐ€ ์–ด๋”” ์žˆ๋Š”์ง€ ๋ฐ”๋กœ ๋ณด์ด๋”๋ผ๊ณ . 2๋ถ„์ด๋ฉด ์ฐพ์„ ๊ฑฐ 10๋ถ„ ๋’ค์ง€๋˜ ๋•Œ๊ฐ€ ์ด๋ฏธ ์˜›๋‚  ์ผ ๊ฐ™์•„.

๐Ÿ’ก ์˜ค๋Š˜์˜ ๊ตํ›ˆ: "์ฝ”๋“œ๊ฐ€ ์–ด๋”” ์žˆ๋Š”์ง€ ์ฐพ๋Š” ๋ฐ ์‹œ๊ฐ„์ด ๊ฑธ๋ฆฌ๊ธฐ ์‹œ์ž‘ํ•˜๋ฉด, ๊ทธ๊ฒŒ ๊ตฌ์กฐ๋ฅผ ๋ฐ”๊ฟ€ ์‹ ํ˜ธ๋‹ค."

์ˆœํ™˜ ์˜์กด์„ฑ ์–˜๊ธฐ๋„ ์ถฉ๊ฒฉ์ด์—ˆ์–ด. A ๊ฐ€ B ๋ฅผ ๋ณด๊ณ  B ๊ฐ€ A ๋ฅผ ๋ณด๋ฉด ์„œ๋กœ ๋ฌดํ•œ ๊ฑฐ์šธ์ด ๋œ๋‹ค ๋Š” ๋น„์œ ๊ฐ€ ๋„ˆ๋ฌด ์ฐฐ์กŒ์–ด์„œ ์ด์ œ ์ ˆ๋Œ€ ๋ชป ์žŠ์„ ๊ฒƒ ๊ฐ™์•„.

์˜ค๋Š˜ ํ•˜๋ฃจ ํด๋” ๊ตฌ์กฐ ๋‹ค๋“ฌ๊ณ  ๋‚˜๋‹ˆ๊นŒ ์™ ์ง€ ๋ญ”๊ฐ€ ์ •๋ˆ๋œ ๋А๋‚Œ. ์ง‘ ๋Œ€์ฒญ์†Œํ•œ ๊ฒƒ ๊ฐ™์€ ๊ธฐ๋ถ„์ด๋ž„๊นŒ. ํ‡ด๊ทผํ•˜๊ณ  ํ—ฌ์Šค์žฅ์ด๋‚˜ ๊ฐ€์•ผ๊ฒ ๋‹ค. ๋‹ค์Œ ๋‹ฌ ๋“ฑ๋กํ•œ PT ์„ธ์…˜ ์ด๋ฒˆ ์ฃผ๋„ ๋น ์ง€๋ฉด ์•ˆ ๋˜๋Š”๋ฐ.


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