๐ก 08. Provider ์ ์ค์ฝํ โ atom ์ ์๋ช ์ฃผ๊ธฐ ์ ์ด
๐ ๊ฐ์
Provider-less ๋ชจ๋์ ์ํ์ฑ, Provider ์ค์ฝํ ์ ์ด, createStore๋ก store๋ฅผ ์ง์ ๋ง๋๋ ๊ณ ๊ธ ํจํด์ ๋ฐฐ์๋๋ค.
๐ฏ ์ด ์น์ ์ ์ฝ๊ณ ๋๋ฉด:
- Provider-less ๋ชจ๋๊ฐ SSR ํ๊ฒฝ์์ ์ ๋ณด์ ์ํ์ด ๋๋์ง ์ค๋ช ํ ์ ์๋ค.
createStore()๋ก ๋ ๋ฆฝ store ๋ฅผ ๋ง๋ค๊ณ<Provider store={store}>์ ์ฃผ์ ํ๋ ๊ณ ๊ธ ํจํด์ ์ฌ์ฉํ ์ ์๋ค.- ๋ชจ๋ฌ, ์์ ฏ, Next.js layout ์์ Provider ์ค์ฝํ๋ฅผ ์ค๊ณํ๋ ํ๋จ๋ ฅ์ ๊ฐ์ง๋ค.
๐ ๋ชฉ์ฐจ
- ๐ ์ด ๋ฌธ์๋ฅผ ์ฝ๊ธฐ ์ ์
- ๐ค ์ Provider ๋ฅผ ์์์ผ ํ๋๊ฐ?
- ๐๏ธ Provider-less ๋ชจ๋ vs Provider ์ค์ฝํ
- ๐ง createStore() โ store ๋ฅผ ์ง์ ์ ์ดํ๋ค
- ๐ฏ Provider ์ store ์ฃผ์ ํจํด
- ๐๏ธ Next.js layout.tsx ์ Provider ์ค์ ํ๊ธฐ
- ๐ช ๋ชจ๋ฌ ยท ์์ ฏ์์ ๋ ๋ฆฝ store ์ฌ์ฉ ํจํด
- ๐ useStore() ํ ์ผ๋ก ํ์ฌ store ์ ๊ทผํ๊ธฐ
- ๐ฅ ์๋ฌ ํด๊ฒฐ ์นดํ๋ก๊ทธ
- ๐ ์ด๋ฒ์ ๋ฐฐ์ด ๋ด์ฉ ์ด์ ๋ฆฌ
- ๐ ๋ง๋ฌด๋ฆฌ ํด์ฆ
- ๐ฃ ์์ฒ ์ด์ ํด๊ทผ ์ผ๊ธฐ
- ๐ ๋ ์์๋ณด๊ธฐ
๐ ์ด ๋ฌธ์๋ฅผ ์ฝ๊ธฐ ์ ์
โฑ๏ธ ์์ ์ฝ๊ธฐ ์๊ฐ: 25๋ถ (์ ์ฒด) / ํต์ฌ ํํธ๋ง: 15๋ถ
๐บ๏ธ ์ด ๋ฌธ์์ ๋ฐฐ๊ฒฝ ์ธ๊ณ๊ด: '์์๋ค ์ปค๋ฎค๋ํฐ' ๊ธด๊ธ ์ฌํ
์ด๋ ํ์์ผ ์ค์ 10์. ์์ ๋์ด ์ฌ๋์ ๋นจ๊ฐ ๋ฉ์์ง๋ฅผ ๋ ๋ ธ๋ค.
- ๐ ์์ ๋ (PM/๋ฐฑ์๋): "๐จ ๊ธด๊ธ. ๋ฐฉ๊ธ A ์ ์ ๋ก ๋ก๊ทธ์ธํ๋๋ฐ ํ๋ฉด์ B ์ ์ ์ด๋ฆ์ด๋ ์ด๋ฉ์ผ์ด ๋ฌ๋ค. ๋ฐฐํฌํ ์ง ๋ ์๊ฐ๋ ์ ๋๋๋ฐ ์ด๊ฒ ๋ญ๋?"
- ๐ฆ ์ํธ ๋ (ํ๋ก ํธ ๋ฆฌ๋): (์ฝ๋๋ฅผ ํ์ด๋ณด๊ณ ) "์์ฒ ๋,
layout.tsx์ Provider ๋น ์ ธ์๋ค์. ์ด๊ฑฐ SSR ๋ณด์ ์ด์์ ๋๋ค." - ๐ฃ ์์ฒ : (์์๋) "Provider ์? ์ Provider ์์ด๋ ์ ๋์๋๋ฐ์..."
- ๐ฆ ์ํธ ๋: "๋ก์ปฌ์์ ์์ฒญ์ด ํ๋๋ผ์ ๊ทธ๋์. ์๋ฒ์์ ์ฌ๋ฌ ์์ฒญ์ด ๋์์ ๋ค์ด์์ ์ ์ญ store ๋ฅผ ๊ณต์ ํด์. ์์ ๋ ๊ณ์ ์ผ๋ก ๋ก๊ทธ์ธํ ์ฌ๋์ด ์์ฒ ๋ ๋ฐ์ดํฐ๋ฅผ ๋ณด๊ฒ ๋๋ ๊ฑฐ์์."
- ๐ฃ ์์ฒ : "๊ทธ๋ฌ๋ฉด... ์ง๊ธ ๋ช ๋ช ์ด๋ ๋ค๋ฅธ ์ฌ๋ ์ ๋ณด๋ฅผ ๋ดค์์ง..."
- ๐ ์์ ๋: "๊ทธ๊ฑฐ ์๊ฐํ์ง ๋ง๊ณ ์ผ๋จ ๊ณ ์ณ. ์ง๊ธ ๋น์ฅ."
๐ค ์ Provider ๋ฅผ ์์์ผ ํ๋๊ฐ? ๐ข
๐ฏ ์ด ์น์ ์ ์ฝ๊ณ ๋๋ฉด:
- Provider ๊ฐ ํ์ํ ์ธ ๊ฐ์ง ์ํฉ์ ์ค๋ช ํ ์ ์๋ค
- ์ ์ญ ์ฑ๊ธํด store ์ ์ํ์ฑ์ ๊ตฌ์ฒด์ ์ผ๋ก ์ดํดํ๋ค
๐ค ์ ๊น, ๋จผ์ ์๊ฐํด๋ด
Next.js App Router ์ Server Component ๋ ์์ฒญ๋ง๋ค ์คํ๋ผ. ๊ทผ๋ฐ JavaScript ๋ชจ๋ ์์ค์ ์ ์ญ ๋ณ์๋ ์๋ฒ ์ธ์คํด์ค ์๋ช ์ฃผ๊ธฐ ๋์ ์ ์ง๋ผ. ์ด ๋ ๊ฐ์ง๋ฅผ ํฉ์น๋ฉด ์ด๋ค ๋ฌธ์ ๊ฐ ์๊ธธ๊น?
Jotai ์ Provider ๋ ์ธ ๊ฐ์ง ์ํฉ์์ ํ์์ผ:
- ์๋ธํธ๋ฆฌ๋ง๋ค ๋ค๋ฅธ ์ํ ๊ฒฉ๋ฆฌ โ ๊ฐ์ atom ์ด๋ผ๋ Provider ๊ฐ ๋ค๋ฅด๋ฉด ๋ค๋ฅธ ๊ฐ์ ๊ฐ์ง
- atom ์ด๊ธฐ๊ฐ ์ฃผ์
โ
store.set()์ผ๋ก ์๋ฒ ๋ฐ์ดํฐ๋ฅผ ๋ฏธ๋ฆฌ ์ฌ์ด๋๊ธฐ - SSR ์์ฒญ ๊ฒฉ๋ฆฌ โ ์์ฒญ๋ง๋ค ์ store ์ธ์คํด์ค ์์ฑ์ผ๋ก ๋ณด์ ์ฌ๊ณ ๋ฐฉ์ง
SSR ์๋ฒ์์ Provider ์์ด ์ฐ๋ฉด:
์์ฒญ 1 (์์ฒ ์ธ์
): userAtom โ { name: '์์ฒ ', role: 'admin' }
์์ฒญ 2 (์์ ์ธ์
): store.get(userAtom) โ '์์ฒ ' ๐ฑ
โ ์ด์ ์์ฒญ์ ์ ์ญ store ๊ฐ์ ๊ทธ๋๋ก ์ฝ์
๐ก ํ ์ค๋ก ๊ธฐ์ตํ๊ธฐ
Provider-less ๋ "๊ณต์ฉ ๋์ฅ๊ณ ". ์ด์ ์ฌ๋ ์์์ด ๋จ์์์ด. Provider ๋ "๊ฐ์ธ ๋์ฅ๊ณ " โ ์์ฒญ๋ง๋ค ์ ๋์ฅ๊ณ ๊ฐ ๋์.
๐๏ธ Provider-less ๋ชจ๋ vs Provider ์ค์ฝํ ๐ข
๐ฏ ์ด ์น์ ์ ์ฝ๊ณ ๋๋ฉด:
- ์ธ์ Provider-less ๊ฐ ๊ด์ฐฎ๊ณ ์ธ์ ์ํํ์ง ํ๋จํ ์ ์๋ค
- Provider ์ค์ฝํ๊ฐ React Context ์ ๋์ผํ๊ฒ ๋์ํ๋ ์ด์ ๋ฅผ ์ดํดํ๋ค
Provider-less ๋ชจ๋ โ CSR ์ ์ฉ SPA ์์๋ง ์์
// โ
CSR ์ ์ฉ SPA ์์๋ ์ด๊ฒ ๊ด์ฐฎ์
// ๐ฃ ์์ฒ : "๋ก์ปฌ์์๋ ํญ์ ์ด๊ฒ ์ ๋ผ์ ๋ฌธ์ ์๋ค๊ณ ์๊ฐํ์ด์"
const userAtom = atom<User | null>(null)
const App = () => (
// Provider ์์ โ ๋ด๋ถ์ ์ผ๋ก ์ ์ญ ์ฑ๊ธํด store ์ฌ์ฉ
<div>
<Header />
<Main />
</div>
)๋ด๋ถ์ ์ผ๋ก getDefaultStore() ๊ฐ ๋ฐํํ๋ ์ ์ญ store ํ๋๋ฅผ ์ฐ๋ ๊ฑฐ์ผ. ๋ธ๋ผ์ฐ์ ์์ ํญ ํ๋ = ์ ์ ํ๋๋ผ์ ์์ ํด.
Provider ์ค์ฝํ โ ์๋ธํธ๋ฆฌ๋ง๋ค ๊ฒฉ๋ฆฌ
// โ
Provider ๋ก ๊ฐ์ธ๋ฉด ํด๋น ์๋ธํธ๋ฆฌ๋ ๋
๋ฆฝ store ๋ฅผ ์ฌ์ฉํจ
// ๐ฆ ์ํธ: "React Context ์ ๋๊ฐ์ด ๋์ํด์. ๊ฐ์ฅ ๊ฐ๊น์ด Provider ์ store ๋ฅผ ์"
const App = () => (
<>
<Provider>
{/* ์ด ์์ ์ปดํฌ๋ํธ๋ค์ Provider A ์ store ๋ฅผ ์ */}
<StudySection />
</Provider>
<Provider>
{/* ์ด ์์ ์ปดํฌ๋ํธ๋ค์ ์์ ํ ๋ค๋ฅธ Provider B ์ store ๋ฅผ ์ */}
{/* ๊ฐ์ atom ์ด๋ผ๋ ๊ฐ์ด ๋ค๋ฅผ ์ ์์ */}
<ChatSection />
</Provider>
</>
)| ํ๊ฒฝ | Provider ํ์ ์ฌ๋ถ | ์ด์ |
|---|---|---|
| CSR ์ ์ฉ SPA (React) | ์ ํ ์ฌํญ | ๋ธ๋ผ์ฐ์ ํญ = ์ ์ ํ๋ |
| Next.js App Router (SSR) | ํ์ | ์๋ฒ ์์ฒญ ๊ฐ store ๊ณต์ ์ํ |
| Next.js Pages Router | ํ์ | ๋์ผํ ์ด์ |
| ๋ชจ๋ฌ / ์์ ฏ ๋ ๋ฆฝ ์ํ | ๊ถ์ฅ | ์ค์ฝํ ๊ฒฉ๋ฆฌ ๋ชฉ์ |
๐ง createStore() โ store ๋ฅผ ์ง์ ์ ์ดํ๋ค ๐ก
๐ฏ ์ด ์น์ ์ ์ฝ๊ณ ๋๋ฉด:
createStore()์get,set,sub์ธ ๊ฐ์ง ๋ฉ์๋๋ฅผ ํ์ฉํ ์ ์๋ค- React ๋ฐ๊นฅ์์ store ๋ฅผ ์ง์ ์ ์ดํด์ผ ํ๋ ์ํฉ์ ์ดํดํ๋ค
Jotai ๊ณต์ ๋ ํผ๋ฐ์ค์ ๋ฐ๋ฅด๋ฉด createStore() ๋ ๋น store ๋ฅผ ๋ง๋ค๊ณ , ์ด๋ฅผ <Provider store={store}> ์ ์ฃผ์
ํ๊ฑฐ๋ React ์ธ๋ถ์์ ์ง์ ์ฌ์ฉํ ์ ์์ด.
import { atom, createStore } from 'jotai'
// ๐ฆ ์ํธ: "createStore ๋ ์ธ ๊ฐ์ง ๋ฉ์๋๋ฅผ ์ ๊ณตํด์"
const store = createStore()
const userAtom = atom<User | null>(null)
const studyFiltersAtom = atom({ category: 'all', sortBy: 'latest' })
// 1. get โ ํ์ฌ ๊ฐ ์ฝ๊ธฐ
const currentUser = store.get(userAtom)
console.log(currentUser) // null
// 2. set โ ๊ฐ ์ฐ๊ธฐ
store.set(userAtom, { name: '์์ฒ ', role: 'member' })
console.log(store.get(userAtom)) // { name: '์์ฒ ', role: 'member' }
// 3. sub โ ๋ณํ ๊ตฌ๋
(unsubscribe ํจ์ ๋ฐํ)
// ๐ฃ ์์ฒ : "React ํ
์์ด๋ ๋ณํ๋ฅผ ๊ฐ์งํ ์ ์๊ตฐ์!"
const unsub = store.sub(userAtom, () => {
console.log('user ๋ณ๊ฒฝ๋จ:', store.get(userAtom))
})
store.set(userAtom, { name: '์ํธ', role: 'admin' })
// ์ฝ์: "user ๋ณ๊ฒฝ๋จ: { name: '์ํธ', role: 'admin' }"
unsub() // ๊ตฌ๋
ํด์ ์ธ์ createStore ๋ฅผ ์ง์ ์ฐ๋๊ฐ?
// ์๋๋ฆฌ์ค 1: SSR ์์ ์๋ฒ ๋ฐ์ดํฐ๋ฅผ ๋ฏธ๋ฆฌ ์ฌ์ด๋๊ธฐ
// ๐ฆ ์ํธ: "์ด ํจํด์ useHydrateAtoms ๊ฐ ๋์ค๊ธฐ ์ ์ ๋ง์ด ์ผ์ด์"
async function getServerSideStore(userId: string) {
const store = createStore()
const user = await fetchUser(userId)
store.set(userAtom, user) // ์๋ฒ ๋ฐ์ดํฐ ์ฃผ์
return store
}
// ์๋๋ฆฌ์ค 2: React ์ธ๋ถ (e.g., ์ด๋ฒคํธ ํธ๋ค๋ฌ, WebSocket ์ฝ๋ฐฑ)์์ ์ํ ๋ณ๊ฒฝ
// ๐ฃ ์์ฒ : "WebSocket ๋ฉ์์ง ๋ฐ์ผ๋ฉด ํ
์์ด๋ atom ๋ฐ๊ฟ ์ ์๊ฒ ๋ค์!"
ws.onmessage = (event) => {
const data = JSON.parse(event.data)
store.set(messagesAtom, (prev) => [...prev, data])
}
// ์๋๋ฆฌ์ค 3: ํ
์คํธ โ ๊ฒฉ๋ฆฌ๋ store ๋ก ์ํ ์ค์ผ ๋ฐฉ์ง
// (13๋ฒ ๊ฐ์ด๋์์ ์์ธํ ๋ค๋ฃธ)
const testStore = createStore()
testStore.set(userAtom, mockUser)๐ก ํ ์ค๋ก ๊ธฐ์ตํ๊ธฐ
createStore()๋ "๋น ๋์ฅ๊ณ ์ ์กฐ๊ธฐ". ๋ง๋ค๊ณ , ์์(set) ๋ฃ๊ณ , ๊บผ๋ด(get) ๋จน๊ณ , ๋ณํ๋ฅผ ๊ฐ์(sub) ํ ์ ์์ด.
๐ฏ Provider ์ store ์ฃผ์ ํจํด ๐ก
๐ฏ ์ด ์น์ ์ ์ฝ๊ณ ๋๋ฉด:
<Provider store={store}>ํจํด์ผ๋ก ์ธ๋ถ์์ ๋ง๋ store ๋ฅผ React ํธ๋ฆฌ์ ์ฐ๊ฒฐํ ์ ์๋ค- store ๋ฅผ ์ธ๋ถ์์ ๊ด๋ฆฌํด์ผ ํ๋ ์ค์ ์๋๋ฆฌ์ค๋ฅผ ์ดํดํ๋ค
import { createStore, Provider } from 'jotai'
// ๐ฆ ์ํธ: "store ๋ฅผ ๋ฐ์์ ๋ง๋ค์ด์ Provider ์ ๋๊ฒจ์ฃผ๋ ๊ฒ ํฌ์ธํธ์์"
const myStore = createStore()
// ์๋ฒ์์ ๋ฐ์์จ ์ด๊ธฐ ๋ฐ์ดํฐ ์ฃผ์
myStore.set(userAtom, { name: '์์ฒ ', role: 'member' })
myStore.set(studyFiltersAtom, { category: 'frontend', sortBy: 'latest' })
const Root = () => (
// ์ด Provider ํ์์ ๋ชจ๋ atom ๊ตฌ๋
์ myStore ์์ ๊ฐ์ ๊ฐ์ ธ์ด
<Provider store={myStore}>
<App />
</Provider>
)Before / After โ ์์ ๋ ๋ณด์ ์ฌ๊ณ ์์ต
// โ Before โ ์์ฒ ์ด์ Provider-less ์ฝ๋ (๋ณด์ ์ฌ๊ณ ์์ธ)
// ๐ฃ ์์ฒ : "๋ฐฐํฌ ์ ๊น์ง ์๋ฌด๋ ์ด๊ฒ ๋ฌธ์ ์ธ์ง ๋ชฐ๋์ด์..."
// app/layout.tsx
export default function RootLayout({ children }: { children: React.ReactNode }) {
return (
<html lang="ko">
<body>
{children} {/* Provider ์์ โ ์ ์ญ ์ฑ๊ธํด store โ ์์ฒญ ๊ฐ ์ค์ผ */}
</body>
</html>
)
}// โ
After โ ์ํธ ๋์ด ์์ ํ ์ฝ๋ (์์ฒญ๋ง๋ค ๊ฒฉ๋ฆฌ)
// ๐ฆ ์ํธ: "'use client' ์ง์์ด๋ ํ์์์. layout ์ ๊ธฐ๋ณธ์ด Server Component ๋๊น"
// app/layout.tsx
'use client'
import { Provider } from 'jotai'
export default function RootLayout({ children }: { children: React.ReactNode }) {
return (
<html lang="ko">
<body>
{/*
Provider ๊ฐ ๋ ๋๋ ๋๋ง๋ค ์ store ์ธ์คํด์ค๋ฅผ ๋ด๋ถ์ ์ผ๋ก ์์ฑ.
SSR ์์๋ ์์ฒญ๋ง๋ค ์ Provider ๋ ๋ โ ์์ ํ ๊ฒฉ๋ฆฌ.
*/}
<Provider>
{children}
</Provider>
</body>
</html>
)
}โ ๏ธ ์ค์:
<Provider>์storeprop ์ ์ง์ ๋๊ธฐ์ง ์์ผ๋ฉด, Provider ๋ ๋ด๋ถ์ ์ผ๋กcreateStore()๋ฅผ ํธ์ถํด์ ์ store ๋ฅผ ๋ง๋ค์ด. SSR ์์๋ ์์ฒญ๋ง๋ค Provider ๊ฐ ์๋ก ๋ ๋๋๋ฏ๋ก, ์์ฒญ๋ง๋ค ์ store ์ธ์คํด์ค๊ฐ ์๊ฒจ์ ๊ฒฉ๋ฆฌ๊ฐ ๋ผ.
๐๏ธ Next.js layout.tsx ์ Provider ์ค์ ํ๊ธฐ ๐ก
๐ฏ ์ด ์น์ ์ ์ฝ๊ณ ๋๋ฉด:
- App Router ์์ 'use client' ๊ฐ ์ ํ์ํ์ง ์ดํดํ๋ค
- JotaiProvider ๋ํผ ์ปดํฌ๋ํธ ํจํด์ผ๋ก ๊น๋ํ๊ฒ ์ค๊ณํ ์ ์๋ค
Next.js App Router ์ layout.tsx ๋ ๊ธฐ๋ณธ์ ์ผ๋ก Server Component ์ผ. ๊ทธ๋ฐ๋ฐ Provider ๋ React Context ๋ฅผ ์ฌ์ฉํ๋ Client Component ์ผ. ๊ทธ๋์ 'use client' ๋ฅผ ๋ถ์ธ ๋ํผ ์ปดํฌ๋ํธ๋ฅผ ๋ง๋๋ ๊ฒ ๊ด๋ก์ผ.
// ๐ฆ ์ํธ: "layout ์ 'use client' ๋ฅผ ์ง์ ๋ถ์ด๋ฉด ์ ์ฒด๊ฐ Client Component ๊ฐ ๋ผ์.
// ๋ํผ ์ปดํฌ๋ํธ๋ก ๋ถ๋ฆฌํ๋ฉด layout ์ ๋๋จธ์ง ๋ถ๋ถ์ Server Component ๋ก ์ ์ง๋ผ์."
// app/providers.tsx โ Client Component ๋ํผ
'use client'
import { Provider } from 'jotai'
import type { ReactNode } from 'react'
// ์์๋ค ์ฑ ์ ์ฉ JotaiProvider ๋ํผ
export function JotaiProvider({ children }: { children: ReactNode }) {
return <Provider>{children}</Provider>
}// app/layout.tsx โ Server Component ์ ์ง
import { JotaiProvider } from './providers'
export default function RootLayout({ children }: { children: React.ReactNode }) {
return (
<html lang="ko">
<body>
{/*
JotaiProvider ๋ Client Component,
ํ์ง๋ง children ์ ์ฌ์ ํ Server Component ๋ก ๋ ๋๋ ์ ์์
*/}
<JotaiProvider>
{children}
</JotaiProvider>
</body>
</html>
)
}์ค์ฒฉ Provider โ ํน์ ๊ธฐ๋ฅ์๋ง ๋ค๋ฅธ store ์ ์ฉ
// ๐ฃ ์์ฒ : "์ฑํ
๊ธฐ๋ฅ๋ง ๋ฐ๋ก store ๋ฅผ ์ฐ๊ณ ์ถ์๋ฐ ๊ฐ๋ฅํ๊ฐ์?"
// ๐ฆ ์ํธ: "Provider ๋ ์ค์ฒฉ ๊ฐ๋ฅํด์. ์์ชฝ Provider ๊ฐ ์ฐ์ ์์๋ฅผ ๊ฐ์ ธ์."
// app/(chat)/layout.tsx
'use client'
import { Provider, createStore } from 'jotai'
// ์ฑํ
์ ์ฉ store โ ์ฑ ์ ์ญ store ์ ์์ ํ ๊ฒฉ๋ฆฌ๋จ
const chatStore = createStore()
export default function ChatLayout({ children }: { children: React.ReactNode }) {
return (
// ์ ์ญ Provider ํ์์ ๋ ๋ค๋ฅธ Provider โ ์ค์ฒฉ OK
<Provider store={chatStore}>
{children}
</Provider>
)
}๐ช ๋ชจ๋ฌ ยท ์์ ฏ์์ ๋ ๋ฆฝ store ์ฌ์ฉ ํจํด ๐ด
๐ฏ ์ด ์น์ ์ ์ฝ๊ณ ๋๋ฉด:
- ๋ชจ๋ฌ์ด๋ ์๋ฒ ๋ ์์ ฏ์์ ๋ถ๋ชจ store ๋ก๋ถํฐ ์์ ํ ๋ ๋ฆฝ๋ ์ํ๋ฅผ ๊ด๋ฆฌํ ์ ์๋ค
- ์ธ์ ๋ ๋ฆฝ store ๊ฐ ํ์ํ๊ณ ์ธ์ ๊ทธ๋ฅ atom ์ ์จ๋ ๋๋์ง ํ๋จํ ์ ์๋ค
์์๋ค ์ฑ์์ "์คํฐ๋ ์ ์ฒญ ๋ชจ๋ฌ"์ด ์๊ฒผ์ด. ์ด ๋ชจ๋ฌ์ ์์ฒด์ ์ธ ํผ ์ํ(์ ๋ ฅ๊ฐ, ์ ์ถ ์ค ์ฌ๋ถ, ์๋ฌ)๋ฅผ ๊ฐ๊ณ ์๋๋ฐ, ๋ชจ๋ฌ์ด ๋ซํ๋ฉด ์ํ๊ฐ ๊น๋ํ๊ฒ ์ด๊ธฐํ๋์ด์ผ ํด.
// ๐ฃ ์์ฒ ์ ์ฒ์ ์ ๊ทผ โ ์ ์ญ atom ์ผ๋ก ํผ ์ํ ๊ด๋ฆฌ
// ๐ฃ ์์ฒ : "์ด๋ ๊ฒ ํ๋ฉด ๋ชจ๋ฌ ๋ซ๊ณ ๋ค์ ์ด๋ฉด ์ด์ ๊ฐ์ด ๋จ์์์ด์..."
const applicationFormAtom = atom({
motivation: '',
schedule: '',
isSubmitting: false,
})
// โ ์ ์ญ atom ์ ๋ชจ๋ฌ์ด ๋ซํ๋ ๊ฐ์ด ์ ์ง๋จ โ ๋งค๋ฒ ์๋์ผ๋ก ์ด๊ธฐํํด์ผ ํจ// โ
์ํธ ๋์ด ์ ์ํ ํจํด โ ๋ชจ๋ฌ ์์ฒด์ Provider ๋ฅผ ์์์ ๋
๋ฆฝ store ์ฌ์ฉ
// ๐ฆ ์ํธ: "๋ชจ๋ฌ์ด unmount ๋๋ฉด Provider ๋ ๊ฐ์ด ์ฌ๋ผ์ง๊ณ , store ๋ GC ๋ผ์.
// ๋๋ถ์ ์ํ ์ด๊ธฐํ ๋ก์ง์ ๋ฐ๋ก ์งค ํ์๊ฐ ์์ด์."
// ๋ชจ๋ฌ ๋ด๋ถ ์ ์ฉ atom (๋
๋ฆฝ store ์์๋ง ์ฌ์ฉ๋จ)
const applicationFormAtom = atom({
motivation: '',
schedule: '',
isSubmitting: false,
})
// ๋ชจ๋ฌ ์ปดํฌ๋ํธ
function StudyApplicationModal({ studyId }: { studyId: string }) {
return (
// ์ด Provider ๋ ๋ชจ๋ฌ ์ ์ฉ ๋
๋ฆฝ store ๋ฅผ ๋ง๋ค์ด๋
<Provider>
<ModalContent studyId={studyId} />
</Provider>
)
}
function ModalContent({ studyId }: { studyId: string }) {
// ๐ฆ ์ํธ: "์ด useAtom ์ ๋ชจ๋ฌ Provider ์ store ๋ฅผ ์ โ ์ ์ญ store ์ ์์ ํ ๋ณ๊ฐ"
const [form, setForm] = useAtom(applicationFormAtom)
const handleSubmit = async () => {
setForm((prev) => ({ ...prev, isSubmitting: true }))
await submitApplication(studyId, form)
// ๋ชจ๋ฌ์ ๋ซ์ผ๋ฉด Provider ๊ฐ unmount โ store ์๋ ์ ๋ฆฌ
}
return (
<form onSubmit={handleSubmit}>
<textarea
value={form.motivation}
onChange={(e) => setForm((prev) => ({ ...prev, motivation: e.target.value }))}
placeholder="์ง์ ๋๊ธฐ๋ฅผ ์
๋ ฅํ์ธ์"
/>
<button disabled={form.isSubmitting}>
{form.isSubmitting ? '์ ์ถ ์ค...' : '์ ์ฒญํ๊ธฐ'}
</button>
</form>
)
}๐ก ๋ ๋ฆฝ store ํจํด์ ํต์ฌ ์ฅ์
- ๋ชจ๋ฌ์ด ๋ซํ ๋ ์ํ ์ด๊ธฐํ ๋ก์ง ๋ถํ์ โ unmount ์ ์๋ ์ ๋ฆฌ
- ๊ฐ์ ๋ชจ๋ฌ์ ์ฌ๋ฌ ๊ฐ ๋์์ ์ด์ด๋ ๊ฐ๊ฐ ๋ ๋ฆฝ์ ์ธ ์ํ
- ์ ์ญ store ์ atom ์ด๋ฆ ์ถฉ๋ ๊ฑฑ์ ์์
๐ useStore() ํ ์ผ๋ก ํ์ฌ store ์ ๊ทผํ๊ธฐ ๐ก
๐ฏ ์ด ์น์ ์ ์ฝ๊ณ ๋๋ฉด:
useStore()๋ก ํ์ฌ ์ปดํฌ๋ํธ๊ฐ ์ํ Provider ์ store ๋ฅผ ์ง์ ๊บผ๋ผ ์ ์๋ค- React ๋ฐ๊นฅ์ผ๋ก store ๋ฅผ ์ ๋ฌํด์ผ ํ๋ ์ํฉ์์ ํ์ฉํ ์ ์๋ค
import { useStore } from 'jotai'
// ๐ฃ ์์ฒ : "WebSocket ์ด๋ฒคํธ์์ atom ์ ์
๋ฐ์ดํธํ๊ณ ์ถ์๋ฐ ํ
๋ฐ๊นฅ์ด์์..."
// ๐ฆ ์ํธ: "useStore ๋ก ํ์ฌ store ๋ฅผ ๊ฐ์ ธ์์ React ๋ฐ๊นฅ์ ์ ๋ฌํ๋ฉด ๋ผ์"
function StudyChatRoom({ roomId }: { roomId: string }) {
const store = useStore()
useEffect(() => {
const ws = new WebSocket(`wss://api.example.com/chat/${roomId}`)
ws.onmessage = (event) => {
const message = JSON.parse(event.data)
// ๐ฆ ์ํธ: "ํ
๋ฐ๊นฅ์์๋ store ๋ฅผ ํตํด atom ์ ์
๋ฐ์ดํธํ ์ ์์ด์"
store.set(messagesAtom, (prev) => [...prev, message])
}
return () => ws.close()
}, [roomId, store])
const messages = useAtomValue(messagesAtom)
return <MessageList messages={messages} />
}store ์ฐธ์กฐ ์์ ์ฑ ์ฃผ์
// โ ๏ธ useStore() ๊ฐ ๋ฐํํ๋ store ๋ Provider ๊ฐ ๋ฐ๋์ง ์๋ ํ ์์ ์ ์ด์ผ
// ํ์ง๋ง useEffect ์์กด์ฑ ๋ฐฐ์ด์ ๋ฃ๋ ๊ฒ ์ ํํด
function Component() {
const store = useStore()
useEffect(() => {
// store ๋ฅผ ์์กด์ฑ ๋ฐฐ์ด์ ํฌํจ
const unsub = store.sub(userAtom, () => {
console.log('user ๋ณ๊ฒฝ:', store.get(userAtom))
})
return unsub
}, [store]) // โ
store ํฌํจ
}๐ฅ ์๋ฌ ํด๊ฒฐ ์นดํ๋ก๊ทธ
์๋ฌ ๋ฉ์์ง๊ฐ ๋จ๋ฉด Ctrl+F ๋ก ๊ฒ์ํด๋ด. ๋๋ถ๋ถ ์ฌ๊ธฐ ์์ด.
โ SSR ์์ ๋ค๋ฅธ ์ ์ ์ ๋ณด๊ฐ ๋ณด์ด๋ ํ์
์ฆ์: A ์ ์ ๋ก ๋ก๊ทธ์ธํ๋๋ฐ ํ๋ฉด์ B ์ ์ ์ด๋ฆ/๋ฐ์ดํฐ๊ฐ ํ์๋จ
์์ธ: Next.js App Router ์์ <Provider> ์์ด ์ ์ญ atom ์ฌ์ฉ
ํด๊ฒฐ์ฑ :
// app/layout.tsx ๋๋ app/providers.tsx
'use client'
import { Provider } from 'jotai'
export function JotaiProvider({ children }: { children: React.ReactNode }) {
return <Provider>{children}</Provider>
}โ "Cannot read properties of undefined" โ store ์ ๊ฐ์ด ์๋ ๊ฒฝ์ฐ
์์ธ: createStore() ๋ก ๋ง๋ store ์ set() ์ ์ ํ๋๋ฐ get() ์ ํธ์ถํจ
ํด๊ฒฐ์ฑ :
const store = createStore()
// ๋ฐ๋์ set ๋จผ์ , ๊ทธ๋ค์ get
store.set(userAtom, initialUser)
const user = store.get(userAtom) // โ
โ Provider ๋ฅผ ์ฐ๋๋ฐ๋ ์ํ๊ฐ ๊ณต์ ๋๋ ๊ฒ ๊ฐ์
์์ธ: ๋ชจ๋ ์์ค์ ๋ณ์(atom ๋ฐ์์ ์ ์ธํ ์ํ)๋ฅผ atom ๊ณผ ํจ๊ป ์ฌ์ฉํ๊ณ ์์ ๋
ํด๊ฒฐ์ฑ :
// โ ๋ชจ๋ ์์ค ๋ณ์๋ Provider ์ ๋ฌด๊ดํ๊ฒ ๊ณต์ ๋จ
let sharedCounter = 0 // ์ด๊ฑด Provider ๊ฐ ๋ฌ๋ผ๋ ๊ณต์ ๋จ
// โ
๋ชจ๋ ์ํ๋ atom ์ผ๋ก ๊ด๋ฆฌ โ Provider ์ค์ฝํ์ ๋ฐ๋ผ ๊ฒฉ๋ฆฌ๋จ
const counterAtom = atom(0)โ layout.tsx ์์ Provider ์ค์ ํ๋๋ฐ "You're importing a component that needs context" ์๋ฌ
์์ธ: Server Component ์ธ layout.tsx ์์ Client Component ์ธ Provider ๋ฅผ ์ง์ import
ํด๊ฒฐ์ฑ :
// โ layout.tsx ์ 'use client' ์์ด Provider ์ง์ ์ฌ์ฉ
import { Provider } from 'jotai' // ์๋ฌ!
// โ
๋ํผ ์ปดํฌ๋ํธ ๋ถ๋ฆฌ
// app/providers.tsx ('use client' ํฌํจ)
// app/layout.tsx ์์ JotaiProvider ๋ฅผ import๐ ์ด๋ฒ์ ๋ฐฐ์ด ๋ด์ฉ ์ด์ ๋ฆฌ
๐ Provider ํจํด ํต์ฌ ์์ฝ
| ๊ฐ๋ | ์ค๋ช | ์ธ์ ์ฐ๋๊ฐ |
|---|---|---|
| Provider-less | ์ ์ญ ์ฑ๊ธํด store ์ฌ์ฉ | CSR ์ ์ฉ SPA ์์๋ง |
<Provider> | ํ์ ํธ๋ฆฌ์ ๋ ๋ฆฝ store ์ ๊ณต | Next.js SSR, ์๋ธํธ๋ฆฌ ๊ฒฉ๋ฆฌ |
createStore() | ๋น store ์ง์ ์์ฑ | store ๋ฅผ ๋ฏธ๋ฆฌ ์ด๊ธฐํํ๊ฑฐ๋ React ์ธ๋ถ ์ฌ์ฉ ์ |
<Provider store={store}> | ์ธ๋ถ store ์ฃผ์ | ์๋ฒ ๋ฐ์ดํฐ ๋ฏธ๋ฆฌ ์ฃผ์ , ํ ์คํธ |
useStore() | ํ์ฌ Provider ์ store ์ ๊ทผ | React ๋ฐ๊นฅ ์ฐ๋ (WebSocket ๋ฑ) |
โ ๏ธ ์ ๋ ํ์ง ๋ง ๊ฒ
| ์ํฉ | โ ๋์ ์ | โ ์ข์ ์ |
|---|---|---|
| Next.js SSR | Provider ์์ด ์ ์ญ atom ์ฌ์ฉ | layout.tsx ์ <Provider> ๋ํผ ์ถ๊ฐ |
layout.tsx ์ง์ ์์ | layout.tsx ์ 'use client' ์ง์ ์ถ๊ฐ | providers.tsx ๋ํผ ์ปดํฌ๋ํธ ๋ถ๋ฆฌ |
| ๋ชจ๋ฌ ํผ ์ํ | ์ ์ญ atom ์ผ๋ก ํผ ์ํ ๊ด๋ฆฌ ํ ์๋ ์ด๊ธฐํ | ๋ชจ๋ฌ์ <Provider> ์์์ ์๋ ์ ๋ฆฌ |
๐ ๋ง๋ฌด๋ฆฌ ํด์ฆ
Q1. ๐ฅ ๊ธด๊ธ ๋๋ฒ๊น (์์์ ํธํต)
์์ ๋์ด "๋ฐฐํฌํ๊ณ 10๋ถ ๋ง์ A ์ ์ ๊ฐ B ์ ์ ์ด๋ฆ์ ๋ดค๋ค" ๋ฉฐ ๋ก๊ทธ๋ฅผ ๋์ก์ด.
์์ฒ ์ด๊ฐ ํ์ธํ๋app/layout.tsx์ Provider ๊ฐ ์์์ด. ์ด๋ ์ํธ ๋์ด ์ ํํ ์ค๋ช ํ ๋ด์ฉ์ผ๋ก ๊ฐ์ฅ ์ ์ ํ ๊ฒ์?
- A) atom ์ ์ด๊ธฐ๊ฐ์ด ์๋ชป ์ค์ ๋์ด์ ์๊ธฐ๋ ๋ฌธ์ ๋ค
- B) Next.js SSR ์์ Provider ์์ด atom ์ ์ฐ๋ฉด ์๋ฒ ์ธ์คํด์ค ๋ ๋ฒจ์ ์ ์ญ store ๊ฐ ๊ณต์ ๋์ด ์์ฒญ ๊ฐ ๋ฐ์ดํฐ๊ฐ ์์ธ๋ค
- C) TypeScript ํ์ ์ด ์๋ชป ๋์ด์ ๋ฐ์ดํฐ๊ฐ ์ค์ผ๋ ๊ฒ์ด๋ค
- D) atom ์ ์ปดํฌ๋ํธ ๋ด๋ถ์ ์ ์ธํ์ง ์์์ ์๊ธฐ๋ ๋ฌธ์ ๋ค
โ ์ ๋ต: B
๐ก ์์ธ ํด์ค:
- ์๋ฆฌ ์ค๋ช
: Jotai ์ Provider-less ๋ชจ๋๋ ๋ด๋ถ์ ์ผ๋ก
getDefaultStore()๊ฐ ๋ฐํํ๋ ์ ์ญ ์ฑ๊ธํด store ๋ฅผ ์ฌ์ฉํด. ๋ธ๋ผ์ฐ์ ์์๋ ํญ = ์ ์ ํ๋๋ผ์ ๊ด์ฐฎ์ง๋ง, SSR ์๋ฒ์์๋ ํ๋์ Node.js ํ๋ก์ธ์ค๊ฐ ์ฌ๋ฌ ์์ฒญ์ ์ฒ๋ฆฌํ๊ณ , ๊ฐ ์์ฒญ์ด ๊ฐ์ ์ ์ญ store ๋ฅผ ๊ณต์ ํ๊ฒ ๋ผ. A ์ ์ ์ ์์ฒญ์ด store ์ ๋ฐ์ดํฐ๋ฅผ ์ฐ๋ฉด, B ์ ์ ์ ์์ฒญ์์ ๊ทธ ๋ฐ์ดํฐ๋ฅผ ์ฝ์ ์ ์์ด. - ์ค๋ต ํผ๋๋ฐฑ: A, C, D ๋ ๊ทผ๋ณธ ์์ธ(store ๊ฒฉ๋ฆฌ ์คํจ)์ ์ค๋ช ํ์ง ๋ชปํด.
- ๐ ํต์ฌ ๊ธฐ์ต๋ฒ: "SSR + Jotai =
<Provider>ํ์." ์๋ฒ๋ ๊ณต์ ์ฃผ๋ฐฉ, Provider ๋ ๊ฐ์์ ์ํ.
Q2. ๐ฏ ์ค๋ฌด ๋๋ ๋ง (์์ฒ ์ ์ ํ)
์์ฒ ์ด๊ฐ "์คํฐ๋ ์ ์ฒญ ๋ชจ๋ฌ"์ ๋ง๋ค์์ด. ํผ ์ํ(์ ๋ ฅ๊ฐ, ์ ์ถ ์ค ์ฌ๋ถ)๋ฅผ atom ์ผ๋ก ๊ด๋ฆฌํ๊ณ ์ถ์ด.
๋ชจ๋ฌ์ ๋ซ๊ณ ๋ค์ ์ด๋ฉด ์ด๊ธฐ ์ํ์ฌ์ผ ํด. ์ํธ ๋์ด ์ ์ํ ๊ฐ์ฅ ์ฐ์ํ ํด๊ฒฐ์ฑ ์?
- A) ๋ชจ๋ฌ์ ๋ซ์ ๋
setForm(initialForm)์ ์ง์ ํธ์ถํด์ ์๋์ผ๋ก ์ด๊ธฐํํ๋ค - B) ๋ชจ๋ฌ ์ปดํฌ๋ํธ๋ฅผ
<Provider>๋ก ๊ฐ์ธ์ ๋ ๋ฆฝ store ๋ฅผ ์ฌ์ฉํ๋ฉด, ๋ชจ๋ฌ unmount ์ ์ํ๊ฐ ์๋ ์ ๋ฆฌ๋๋ค - C) atom ์
useState๋ก ๋์ฒดํ๋ค - D) ์ ์ญ ์ํ๋ก ๊ด๋ฆฌํ๋
useEffect๋ก ๋ง์ดํธ ์ ์ด๊ธฐํํ๋ค
โ ์ ๋ต: B
๐ก ์์ธ ํด์ค:
- ์๋ฆฌ ์ค๋ช
: ๋ชจ๋ฌ ์ปดํฌ๋ํธ๋ฅผ
<Provider>๋ก ๊ฐ์ธ๋ฉด, ํด๋น Provider ํ์์ atom ๊ตฌ๋ ์ ๋ ๋ฆฝ๋ store ๋ฅผ ์ฌ์ฉํด. ๋ชจ๋ฌ์ด ๋ซํ๋ฉด(์ปดํฌ๋ํธ unmount) Provider ๋ ๊ฐ์ด ์ฌ๋ผ์ง๊ณ , ๋ด๋ถ store ๋ ๊ฐ๋น์ง ์ปฌ๋ ์ ๋์์ด ๋ผ. ๋ค์์ ๋ชจ๋ฌ์ด ์ด๋ฆฌ๋ฉด(์ปดํฌ๋ํธ mount) ์ Provider ๊ฐ ์ store ๋ฅผ ๋ง๋ค์ด์ ํญ์ ์ด๊ธฐ ์ํ๋ก ์์ํด. - ์ค๋ต ํผ๋๋ฐฑ: A, D ๋ ์๋ ์ด๊ธฐํ๋ก ์ฝ๋ ๋ณต์ก๋๋ฅผ ๋์ฌ. C ๋ atom ์ ์ฅ์ (๊ตฌ๋ ์ ํ์ฑ)์ ํฌ๊ธฐํ๋ ์ ํ์ด์ผ.
- ๐ ํต์ฌ ๊ธฐ์ต๋ฒ: "Provider unmount = store ์๋ ์ฒญ์". ์ฒญ์๋ถ๋ฅผ ๊ณ ์ฉํ ํ์ ์์ด.
Q3. ๐ฆ ์๋์ด ๋ฉด์ ์ง๋ฌธ (์์ฒ ์ ๋ฉด์ ๋์ )
๋ฉด์ ๊ด์ด ๋ฌผ์์ด: "
createStore()์ ๋จ์ํ<Provider>๋ฅผ ์ฐ๋ ๊ฒ์ ์ฐจ์ด๋ ๋ฌด์์ธ๊ฐ์? ์ธ์ createStore()๋ฅผ ์ง์ ์ฌ์ฉํ๋ ๊ฒ ์ ๋ฆฌํ๊ฐ์?"
์์ ๋ต๋ณ:
"
<Provider>๋ง ์ฐ๋ฉด Provider ๋ด๋ถ์์ ์๋์ผ๋กcreateStore()๋ฅผ ํธ์ถํด์ store ๋ฅผ ์์ฑํด์. ์ด ๊ฒฝ์ฐ store ์ ์ง์ ์ ๊ทผํ ์ ์์ด์.createStore()๋ฅผ ์ง์ ์ฌ์ฉํ๋ฉด store ์ธ์คํด์ค๋ฅผ ์ธ๋ถ์์ ์ฐธ์กฐํ ์ ์์ด์, React ๋ฐ๊นฅ(WebSocket ์ฝ๋ฐฑ, ํ ์คํธ ์ฝ๋)์์๋store.get(),store.set()์ผ๋ก atom ์ ์ง์ ์ ์ดํ ์ ์์ด์. ๋ํstore.set()์ผ๋ก ์ด๊ธฐ ๋ฐ์ดํฐ๋ฅผ ๋ฏธ๋ฆฌ ์ฌ์ด๋๊ณ<Provider store={store}>์ ์ฃผ์ ํ๋ ํจํด์ SSR ์๋ฒ ๋ฐ์ดํฐ ์ฃผ์ ์ด๋ ํ ์คํธ ๊ฒฉ๋ฆฌ์ ํ์ฉํ ์ ์์ด์."
๐ก ์ด ๋ต๋ณ์ ์์ ์ ์ธ์ด๋ก ์ค๋ช ํ ์ ์๋ค๋ฉด: Provider ์ store ์ ๊ด๊ณ๋ฅผ ์ ํํ ์ดํดํ ๊ฑฐ์ผ. ๋ฉด์ ํต๊ณผ!
๐ฃ ์์ฒ ์ด์ ํด๊ทผ ์ผ๊ธฐ
์ค๋์ ์ง์ง ๋ฑ์ค๊ธฐ๊ฐ ์๋ํ๋ ํ๋ฃจ์๋ค.
๋ด๊ฐ Provider ๋ฅผ ๋น ๋จ๋ฆฐ ๊ฒ ์์ธ์ด์๋ค๋. ๋ก์ปฌ์์ ์์ญ ๋ฒ ํ ์คํธํ ๋๋ ์๋ฌด ๋ฌธ์ ๋ ์์๋๋ฐ, ์๋ฒ์์ ์์ฒญ์ด ๋์์ ๋ค์ด์ค๋ ์๊ฐ ์ ์ญ store ๊ฐ ์์ด๋ ๊ฑฐ์์ด. A ์ ์ ๊ฐ ๋ก๊ทธ์ธํ๊ณ B ์ ์ ๊ฐ ๋ก๊ทธ์ธํ๋ ์ฌ์ด์ B ์ ์ ํ๋ฉด์ A ์ ์ ์ ๋ณด๊ฐ ๋จ๋ ๊ฑฐ. ์ด๊ฒ ๋จ์ ๋ฒ๊ทธ๊ฐ ์๋๋ผ ๊ฐ์ธ์ ๋ณด ๋ณด์ ์ด์๋ผ๋ ๊ฑธ ์ํธ ๋์ด ์ฐจ๋ถํ๊ฒ ์ค๋ช ํด์ฃผ์ จ์ ๋ ์ผ๋ง๋ ์์๋์ด ๋ฌ๋์ง...
๐ก ์ค๋์ ๊ตํ: "SSR ์์ ์ ์ญ store ๋ ๋ชจ๋ ์ ์ ๊ฐ ๊ณต์ ํ๋ ๊ณต์ฉ ๋์ฅ๊ณ ๋ค. Provider ๋ก ๊ฐ์ธ์ผ ๊ฐ ์์ฒญ๋ง๋ค ๊ฐ์ธ ๋์ฅ๊ณ ๋ฅผ ๋ฐ๋๋ค."
๊ทธ๋๋ createStore() ๋ก store ๋ฅผ ์ง์ ๋ง๋ค์ด์ ์ฃผ์
ํ๋ ํจํด, ๋ชจ๋ฌ์ Provider ๋ฅผ ์์์ ์ํ๋ฅผ ์๋ ์ ๋ฆฌํ๋ ํจํด, ์ด๋ฐ ๊ณ ๊ธ ๊ธฐ๋ฒ๋ค๊น์ง ์ค๋ ํ ๋ฒ์ ๋ฐฐ์ ๋ค. ์ํธ ๋์ด ๋ฌด์ญ๊ฒ ํผ๋ผ ์ค ์์๋๋ฐ ์คํ๋ ค "์ด๋ฒ์ ์ ๋๋ก ๋ฐฐ์ ์ผ๋๊น ๋ค์์ ์ ๊ทธ๋ด ๊ฑฐ์์" ๋ผ๊ณ ํด์ฃผ์
์ ๊ณ ๋ง์ ๋ค.
์ง ๊ฐ๋ ๊ธธ์ ํธ์์ ์ผ๊ฐ๊น๋ฐฅ์ด๋ผ๋ ๋จน์ด์ผ์ง. ์ค๋ ์ ์ฌ๋ ์ ๋๋ก ๋ชป ๋จน์๋๋ฐ. ๋ณด์ ์ด์ ์์ตํ๋๋ผ ์จ ํ์ด ๊ณ ์ํ๋๋ฐ ๋ด๊ฐ ๊ฐ์์ด๋ผ๋ ๋๋ ค์ผ ํ๋... ์ผ๋จ ์ค๋์ ์ง์์ ์ค๋ ๋ฐฐ์ด ๋ด์ฉ ๋ฑ ํ ๋ฒ ๋ ์ ๋ฆฌํ๊ณ ์ผ์ฐ ์์.