๐ MobX6 ๊ธฐ์ค ๊ธฐ๋ฅ ์ ๋ฆฌ
1. observable
โ ์ค๋ช
- ์ํ๊ฐ์ผ๋ก ๊ด๋ฆฌํ ํ๋กํผํฐ.
- ๊ฐ์ด ๋ฐ๋๋ฉด MobX๊ฐ ๊ฐ์งํ๊ณ , ๊ด๋ จ๋ ๋ฆฌ์ก์ ์ด ์๋ ์คํ.
- ๊ธฐ๋ณธ์ ์ผ๋ก deep observable๋ก ๋์ (๊ฐ์ฒด/๋ฐฐ์ด ๋ด๋ถ๊น์ง ์ถ์ ).
โ ์์
class Store {
name = '์ด๊ธฐ๊ฐ';
items = [];
constructor() {
makeObservable(this, {
name: observable,
items: observable,
});
}
}
2. observable.ref
โ ์ค๋ช
- ๊ฐ์ฒด/๋ฐฐ์ด์ ์ฐธ์กฐ๋ง ์ถ์ ํ๊ณ , ๋ด๋ถ ์์ฑ์ ์ถ์ ํ์ง ์์.
- ์ฑ๋ฅ ์ต์ ํ์ฉ (๊น์ ๊ตฌ์กฐ๊ฐ ๋ถํ์ํ ๋).
โ ์์
class Store {
data = { a: 1, b: 2 };
constructor() {
makeObservable(this, {
data: observable.ref,
});
}
}
data.a = 3์ MobX ๋ฐ์ํ ์ ๋ฐ์ดํธ ์ ๋จ.
3. observable.shallow
โ ์ค๋ช
- ๋ฐฐ์ด/๊ฐ์ฒด์ ์ต์์ ๋ ๋ฒจ๊น์ง๋ง ์ถ์ .
- ๋ด๋ถ ์์ ๋ณ๊ฒฝ์ ๊ฐ์งํ์ง ์์.
โ ์์
class Store {
list = [{ id: 1 }, { id: 2 }];
constructor() {
makeObservable(this, {
list: observable.shallow,
});
}
}
list.push({ id: 3 })๋ ๊ฐ์งํ์ง๋ง, list[0].id = 99๋ ๊ฐ์ง ์ ๋จ.
4. observable.struct
โ ์ค๋ช
- ๊ฐ ๋ณ๊ฒฝ ๊ฐ์ง ์, ์ฐธ์กฐ ๋น๊ต๊ฐ ์๋ ๋ด์ฉ ๋น๊ต๋ก ํ๋จ.
- ๊ฐ์ ๋ด์ฉ์ด๋ฉด "๋์ผ ๊ฐ"์ผ๋ก ์ทจ๊ธ.
โ ์์
class Store {
position = { x: 0, y: 0 };
constructor() {
makeObservable(this, {
position: observable.struct,
});
}
}
{x: 0, y: 0}์ผ๋ก ๋ค์ ์ค์ ํด๋ ๋ณ๊ฒฝ ์ ๋ ๊ฑธ๋ก ์ธ์.
5. action
โ ์ค๋ช
- ์ํ ๋ณ๊ฒฝ ํจ์.
- ์ํ ๋ณ๊ฒฝ์ ์์น์ ์ผ๋ก action ์์์ ์ํ.
- MobX๊ฐ ํธ๋์ญ์ ์ฒ๋ผ ๊ฐ์ธ๊ณ , ๋ณ๊ฒฝ ๋ก๊ทธ๋ ๋จ๊น.
โ ์์
class Store {
name = '์ด๊ธฐ๊ฐ';
constructor() {
makeObservable(this, {
setName: action,
});
}
setName(newName: string) {
this.name = newName;
}
}
6. action.bound
โ ์ค๋ช
- ์ํ ๋ณ๊ฒฝ ํจ์ + this ๋ฐ์ธ๋ฉ ๊ณ ์ .
- ์ด๋ฒคํธ ํธ๋ค๋ฌ๋ก ๋๊ธธ ๋ ์ ์ฉ (๋ฆฌ์กํธ์์ ๋ง์ด ์).
โ ์์
class Store {
name = '';
constructor() {
makeObservable(this, {
setName: action.bound,
});
}
setName(newName: string) {
this.name = newName;
}
}
// React์์ ์ฌ์ฉ ์
<button onClick={store.setName.bind(null, 'ํ๊ธธ๋')}>์ด๋ฆ ๋ณ๊ฒฝ</button>
7. flow
โ ์ค๋ช
- ๋น๋๊ธฐ ์์ ์ MobX ์ํ ๋ณ๊ฒฝ ํ๋ฆ์ ํฌํจ.
- ๋ฐ๋์ generator ํจ์๋ก ์์ฑํด์ผ ํจ (function*).
โ ์์
class Store {
data = null;
constructor() {
makeObservable(this, {
fetchData: flow,
});
}
*fetchData() {
const response = yield fetch('/api/data');
const data = yield response.json();
this.data = data;
}
}
8. flow.bound
โ ์ค๋ช
- ๋น๋๊ธฐ ์์ + this ๋ฐ์ธ๋ฉ ๊ณ ์ .
- ๋น๋๊ธฐ ์์ ์ ์ด๋ฒคํธ ํธ๋ค๋ฌ๋ก ๋๊ธธ ๋ ์ ์ฉ.
โ ์์
class Store {
data = null;
constructor() {
makeObservable(this, {
fetchData: flow.bound,
});
}
*fetchData() {
const response = yield fetch('/api/data');
const data = yield response.json();
this.data = data;
}
}
// React ์์
<button onClick={store.fetchData}>๋ฐ์ดํฐ ๋ถ๋ฌ์ค๊ธฐ</button>
9. computed
โ ์ค๋ช
- ๊ณ์ฐ๋ ๊ฐ (derived value).
- ์์กดํ๋ observable ๊ฐ์ด ๋ณ๊ฒฝ๋ ๋๋ง ์ฌ๊ณ์ฐ.
- ๊ทธ ์ธ์ ์บ์๋ ๊ฐ ๋ฐํ (์ฑ๋ฅ ์ต์ ํ).
โ ์์
class Store {
firstName = '๊ธธ๋';
lastName = 'ํ';
constructor() {
makeObservable(this, {
fullName: computed,
});
}
get fullName() {
return `${this.lastName} ${this.firstName}`;
}
}
10. computed.struct
โ ์ค๋ช
- computed ๊ฒฐ๊ณผ์ ๋ณ๊ฒฝ ์ฌ๋ถ๋ฅผ ๋ด์ฉ ๋น๊ต๋ก ํ๋จ.
- ๋ด์ฉ์ด ๊ฐ์ผ๋ฉด ์๋ก ๊ณ์ฐํ์ง ์๊ณ ๊ธฐ์กด ๊ฐ ์ฌ์ฌ์ฉ.
โ ์์
class Store {
coords = { x: 0, y: 0 };
constructor() {
makeObservable(this, {
normalizedCoords: computed.struct,
});
}
get normalizedCoords() {
return { x: Math.round(this.coords.x), y: Math.round(this.coords.y) };
}
}
โ๏ธ makeAutoObservable
โ ์ค๋ช
- makeObservable๋ฅผ ์๋ํํ ๋ฒ์ .
- ์์ฑ์ observable, getter๋ computed, ๋ฉ์๋๋ action์ผ๋ก ์๋ ์ค์ .
- 90% ์ํฉ์์๋ ์ด๊ฑธ๋ก ์ถฉ๋ถ.
โ ์์
class Store {
name = '';
items = [];
constructor() {
makeAutoObservable(this);
}
setName(name: string) {
this.name = name;
}
get itemCount() {
return this.items.length;
}
}
๐ก ์ถ๊ฐ: runInAction
โ ์ค๋ช
- ๋น๋๊ธฐ ํจ์ ์์์ ์ผํ์ฑ ์ํ ๋ณ๊ฒฝํ ๋ ์ฌ์ฉ.
- ๋ณ๋ action ๋ฉ์๋ ์ ๋ง๋ค๊ณ ๋ฐ๋ก ๊ฐ์ธ๋ ์ฉ๋.
โ ์์
class Store {
data = null;
async fetchData() {
const response = await fetch('/api/data');
const data = await response.json();
runInAction(() => {
this.data = data;
});
}
}
๐ ์ต์ข ์์ฝํ (๋ณด์ํ)
ํค์๋ ์ค๋ช ๋น๊ณ
observable | ์ํ๊ฐ | deep observable |
observable.ref | ์ฐธ์กฐ๋ง ์ถ์ | ๋ด๋ถ ๋ณํ ๋ฌด์ |
observable.shallow | ์ต์์๋ง ์ถ์ | ๋ด๋ถ ๊น์ด ๋ฌด์ |
observable.struct | ๋ด์ฉ ๋น๊ต | ๊ฐ ์์ฒด ๋น๊ต |
action | ์ํ ๋ณ๊ฒฝ ํจ์ | ๋๊ธฐ ์ํ ๋ณ๊ฒฝ |
action.bound | ์ํ ๋ณ๊ฒฝ ํจ์ + this ๊ณ ์ | ์ด๋ฒคํธ ํธ๋ค๋ฌ ๋์ |
flow | ๋น๋๊ธฐ ์ํ ๋ณ๊ฒฝ | generator ๊ธฐ๋ฐ |
flow.bound | ๋น๋๊ธฐ ์ํ ๋ณ๊ฒฝ + this ๊ณ ์ | ๋น๋๊ธฐ ํธ๋ค๋ฌ ๋์ |
computed | ๊ณ์ฐ๋ ๊ฐ | ์บ์ฑ๋จ |
computed.struct | ๋ด์ฉ ๋น๊ต๋ ๊ณ์ฐ ๊ฐ | ๊ฐ ๋น๊ต๋ก ์บ์ฑ |
makeAutoObservable | ์๋ ์ค์ | ๋๋ถ๋ถ ์ปค๋ฒ |
runInAction | ์ฆ์ ์ํ ๋ณ๊ฒฝ | ๋น๋๊ธฐ ์์์ ์์ฃผ ์ฌ์ฉ |
์๋๋ MobX 6 ์ฐ๋ฉด์ ๊ฐ๋ฐ์๋ค์ด ์ค๋ฌด์์ ๋ง์ด ๊ถ๊ธํดํ๊ฑฐ๋, ๊ผญ ์์๋ฌ์ผ ํ๋ ์ถ๊ฐ ์ค๋ช
/ํ/ํจํด๋ค์
๋๋ค.
ํ ๋ฒ์ ์ ๋ฆฌํด๋๋ฆฌ๋, ํ์ํ์ ๋ถ๋ถ ๋ง์ํ์๋ฉด ๊ทธ ์ฃผ์ ๋ง ๋ฐ๋ก ์์+์ค๋ช
๋ ๋๋ฆด ์ ์์ด์.
๐ ํ์ํ ์ถ๊ฐ ์ค๋ช ๋ฆฌ์คํธ (MobX 6 ๊ธฐ์ค)
1. makeAutoObservable vs makeObservable ์ธ์ ์จ์ผ ํ๋?
โ ์์
๊ตฌ๋ถ ์ถ์ฒ ์ํฉ
makeAutoObservable | ๋๋ถ๋ถ์ ๊ฐ๋จํ ์คํ ์ด (์๋ ์ค์ ์ผ๋ก ์ถฉ๋ถ) |
makeObservable | ์ผ๋ถ ์์ฑ์ shallow, ref, struct ๋ฑ ์ธ๋ถ ์ต์ ํ์ํ ๋ |
๐ ์ค๋ฌด ๊ธฐ์ค ํ
- ๋จ์ ๋ฐ์ดํฐ ๊ด๋ฆฌ์ฉ ์คํ ์ด๋ makeAutoObservable.
- ์ฑ๋ฅ ํ๋, ์ธ๋ถ ๋ฐ์ดํฐ ์ฐ๋์ด ํ์ํ ์คํ ์ด๋ makeObservable.
2. runInAction ์ ํํ ์ฐ์์
โ ์์
- ๋น๋๊ธฐ ํจ์์์ ์ํ ๋ณ๊ฒฝํ ๋ runInAction์ผ๋ก ๊ฐ์ธ์ฃผ๋ฉด, ๋ฐ๋ก action ๋ง๋ค ํ์ ์์.
- ๋น๋๊ธฐ ํจ์์์ ์ฌ๋ฌ ์ํ๋ฅผ ํ ๋ฒ์ ์ ๋ฐ์ดํธํ ๋๋ ์ ์ฉ.
โ ์์
async loadData() {
const result = await fetchData();
runInAction(() => {
this.data = result.data;
this.status = 'success';
});
}
3. ๋น๋๊ธฐ ์ฒ๋ฆฌ ํจํด (flow vs async/await + runInAction)
โ ์์
ํจํด ์ค๋ช
flow | ๊ณต์ ๊ถ์ฅ, ์ํ ๋ณ๊ฒฝ ์๋ ์ถ์ (generator ๊ธฐ๋ฐ) |
async/await + runInAction | ์ต์ ์ฝ๋ ์คํ์ผ, ๊ธฐ์กด ๋น๋๊ธฐ ํจ์์ ํผ์ฉ ์ฌ์ |
โ ์ถ์ฒ
- ๋น๋๊ธฐ ํจ์ ์์ฒด๋ฅผ MobX๊ฐ ๊ด๋ฆฌํด์ผ ํ๋ฉด flow.
- ์ธ๋ถ ์ ํธํจ์๋ก ๋นผ์ ๊ด๋ฆฌํ ๋๋ async/await + runInAction.
4. ํด๋์ค ์คํ ์ด vs ํจ์ํ ์คํ ์ด ๋น๊ต
โ ์์
๊ตฌ๋ถ ํน์ง
ํด๋์ค ์คํ ์ด | ์ ํต์ ์ธ ๋ฐฉ์, OOP ์คํ์ผ, ํ์ ์์ ์ฑ ๋์ |
ํจ์ํ ์คํ ์ด (Hooks + MobX) | ํจ์ํ ์ปดํฌ๋ํธ์ ์ต์ ํ, ์ปดํฌ๋ํธ ๋ด๋ถ์์ ๊ด๋ฆฌํ ๋ ์ ํฉ |
โ ์ค๋ฌด ์ถ์ฒ
- ์ ์ญ ์ํ: ํด๋์ค ์คํ ์ด
- ๋ก์ปฌ ์ํ (์ปดํฌ๋ํธ ์ ์ฉ): ํจ์ํ ์คํ ์ด
5. ๋ฆฌ์กํธ์์ MobX ์คํ ์ด ์ฃผ์ /์ฌ์ฉ ๋ฐฉ๋ฒ
โ ์์
- Provider + useStore() ํจํด ์ถ์ฒ
- Context๋ก ์คํ ์ด ์ฃผ์ ํด์ ์ ์ญ ์ ๊ทผ
โ ์์
const StoreContext = createContext<RootStore | null>(null);
export const useStore = () => {
const store = useContext(StoreContext);
if (!store) throw new Error('Store is not provided');
return store;
};
<StoreContext.Provider value={rootStore}>
<App />
</StoreContext.Provider>
6. ๋ฆฌ์กํธ ์ปดํฌ๋ํธ์์ observable ๊ฐ ์ฌ์ฉํ๊ธฐ
โ ์์
- observer()๋ก ๊ฐ์ธ์ผ ์๋ ๋ฐ์.
- MobX ์ํ ๋ฐ๋ก ์ฐธ์กฐ ๊ฐ๋ฅ.
โ ์์
const ItemList = observer(() => {
const { itemStore } = useStore();
return (
<ul>
{itemStore.items.map((item) => (
<li key={item.id}>{item.name}</li>
))}
</ul>
);
});
7. Devtools ์ธํ ๋ฐ ํ์ฉ๋ฒ
โ ์์
- MobX Devtools ํ์ฑํํด์ ์ํ ๋ณ๊ฒฝ ์ถ์ .
- ํฌ๋กฌ ํ์ฅํ๋ก๊ทธ๋จ ์ถ์ฒ.
โ ์ค์น
npm install mobx-react-lite mobx-react-devtools
โ ์ฌ์ฉ ์์
import { makeAutoObservable } from 'mobx';
import { observer } from 'mobx-react-lite';
import { enableLogging } from 'mobx-logger';
enableLogging({
actions: true,
reactions: true,
transactions: true,
compute: true,
});
8. MobX์์ ์ธ๋ถ API ๋ฐ์ดํฐ ๊ด๋ฆฌ ํจํด
โ ์์
- ๋ฐ์ดํฐ ์์ฒญ์ flow ๋๋ async ํจ์์์ ์งํ.
- ์ํ ์ ๋ฐ์ดํธ๋ runInAction์ผ๋ก ๊ฐ์ธ์ ์์ ํ๊ฒ.
โ ์์
class Store {
items = [];
isLoading = false;
constructor() {
makeAutoObservable(this);
}
async fetchItems() {
this.isLoading = true;
try {
const response = await fetch('/api/items');
const data = await response.json();
runInAction(() => {
this.items = data;
this.isLoading = false;
});
} catch (error) {
runInAction(() => {
this.isLoading = false;
});
}
}
}
9. computed ์์ฑ ์ฑ๋ฅ ์ต์ ํ ๋ฐ ์๋ชป ์ฐ๋ ๊ฒฝ์ฐ
โ ์์
- computed๋ ์์กดํ๋ observable์ด ์ ๋ฐ๋๋ฉด ์บ์๋ ๊ฐ ๋ฐํ.
- getter ๋ด๋ถ์์ observable ๋ณ๊ฒฝ ์๋ํ๋ฉด ์ ๋จ.
โ ์๋ชป๋ ์์
get doubledCount() {
this.count++; // โ computed์์ ์ํ ๋ณ๊ฒฝ ๊ธ์ง
return this.count * 2;
}
10. ๋ฐฐ์ด/๊ฐ์ฒด observable ๊ด๋ฆฌ ํ (shallow, ref ์ฐจ์ด)
โ ์์
๋ฐฉ์ ์ค๋ช
observable | ๊น์ ๊ตฌ์กฐ ์ถ์ |
shallow | ์ต์์ ๋ ๋ฒจ๋ง ์ถ์ |
ref | ์ฐธ์กฐ๋ง ์ถ์ , ๋ด๋ถ ๋ณ๊ฒฝ ๊ฐ์ง ์ ํจ |
โ ์ค๋ฌด ๊ธฐ์ค ์ ํ ๊ฐ์ด๋
- ์ธ๋ถ API ์๋ต ๋ฐ์ดํฐ: shallow
- ๋ด๋ถ ๊ด๋ฆฌ ๋ฐ์ดํฐ: observable
- Immutable ๋ฐ์ดํฐ: ref
๐ ํ๋์ ์ถ๊ฐ ์ค๋ช ๋ฆฌ์คํธ
์ฃผ์ ์ค๋ช ํ์ ์ฌ๋ถ (โ๏ธ ์์ฒญ ๊ฐ๋ฅ)
makeAutoObservable vs makeObservable | โ๏ธ |
runInAction ์ ํํ ์ฐ์์ | โ๏ธ |
flow vs async/await ํจํด ์ ํ | โ๏ธ |
ํด๋์ค ์คํ ์ด vs ํจ์ํ ์คํ ์ด | โ๏ธ |
๋ฆฌ์กํธ์์ ์คํ ์ด ์ฃผ์ /์ฌ์ฉ๋ฒ | โ๏ธ |
observable ๊ฐ ๋ฆฌ์กํธ์์ ์ฐ๊ธฐ | โ๏ธ |
Devtools ์ธํ ๊ณผ ํ์ฉ๋ฒ | โ๏ธ |
์ธ๋ถ API ๋ฐ์ดํฐ ๊ด๋ฆฌ ํจํด | โ๏ธ |
computed ์ฑ๋ฅ ์ต์ ํ ๋ฐ ์ฃผ์์ | โ๏ธ |
๋ฐฐ์ด/๊ฐ์ฒด observable ๊ด๋ฆฌ๋ฒ | โ๏ธ |
'๊ณต๋ถ > frontend' ์นดํ ๊ณ ๋ฆฌ์ ๋ค๋ฅธ ๊ธ
[React] ๋ฆฌ์กํธ ๊ทธ๋ํ/์ฐจํธ ๋ผ์ด๋ธ๋ฌ๋ฆฌ ๋ชจ์ (0) | 2025.03.12 |
---|---|
ํ์ด์ง ๋ผ์ฐํ (0) | 2025.03.10 |
React Context (0) | 2025.02.23 |
?. (์ต์ ๋ ์ฒด์ด๋)๊ณผ ?? (๋ ๋ณํฉ ์ฐ์ฐ์)๋? (0) | 2025.02.13 |
React Hook ์ต์ ํ: useMemo, memo, useCallback (1) | 2025.02.06 |