ํ๋ก๊ทธ๋๋ฐ์ ํ๋ค ๋ณด๋ฉด “์ด๋ค ์ํ๊ฐ ๋ฐ๋์์ ๋, ๊ด๋ จ๋ ๋ค๋ฅธ ์ฝ๋๋ค๋ ์๋์ผ๋ก ๋ฐ์ํด์ผ ํ๋ ์ํฉ”์ด ์์ฃผ ๋ฐ์ํฉ๋๋ค.
์๋ฅผ ๋ค์ด, ์ฃผ์ ๊ฐ๊ฒฉ์ด ๋ณํ๋ฉด ํฌ์์๋ค์ด ์๋ฆผ์ ๋ฐ์์ผ ํ๊ณ , ๋ ์จ ์ ๋ณด๊ฐ ๋ฐ๋๋ฉด ํ๋ฉด์ ํ์๋ ๋ ์จ ์์ ฏ๋ ์๋์ผ๋ก ์
๋ฐ์ดํธ๋์ด์ผ ํฉ๋๋ค.
์ด๋ฐ ์ํฉ์์ ์์ฃผ ์ฐ์ด๋ ๋์์ธ ํจํด์ด ๋ฐ๋ก ์ต์ ๋ฒ ํจํด(Observer Pattern) ์ ๋๋ค.
์ต์ ๋ฒ ํจํด์ด๋?
์ต์ ๋ฒ ํจํด์ ํ ๊ฐ์ฒด์ ์ํ ๋ณํ๊ฐ ์์ ๋, ๊ทธ ๊ฐ์ฒด๋ฅผ ๊ตฌ๋ (๊ด์ฐฐ)ํ๋ ๋ค๋ฅธ ๊ฐ์ฒด๋ค์๊ฒ ์๋์ผ๋ก ํต๋ณด๊ฐ ์ ๋ฌ๋๋ ํจํด์ ๋๋ค.
- ์ํ๋ฅผ ๊ฐ์ง ๊ฐ์ฒด: Subject (๋๋ Publisher)
- ์ํ ๋ณํ๋ฅผ ๊ฐ์งํ๊ณ ๋ฐ์ํ๋ ๊ฐ์ฒด: Observer (๋๋ Subscriber)
๋ชฉ์
์ต์ ๋ฒ ํจํด์ ๋ชฉ์ ์ ๋ชจ๋ ๊ฐ์ ์์กด์ฑ์ ๋ฎ์ถ๊ณ ๊ฒฐํฉ๋๋ฅผ ์ค์ด๋ ๊ฒ์ ๋๋ค.
๋ฐํ(Publish)–๊ตฌ๋ (Subscribe) ๋ชจ๋ธ๋ก ์ค๋ช ํ ์ ์์ต๋๋ค.
- ๊ตฌ๋ (Subscribe): "์ด ์ผ์ด ๋ฐ์ํ๋ฉด ๋์๊ฒ ์๋ ค์ค!"๋ผ๊ณ ์์ฒญํ๋ ๊ฒ
- ๋ฐํ(Publish): ์ด๋ค ์ผ์ด ๋ฐ์ํ์ ๋ ๊ตฌ๋ ์๋ค์๊ฒ ์๋ฆผ์ ๋ณด๋ด๋ ๊ฒ
์์
์ ๋ฌธ ๊ตฌ๋ ์ ์๋ก ๋ค์ด๋ณด๊ฒ ์ต๋๋ค.
- ์ ๋ฌธ์ฌ(Subject)๋ ์๋ก์ด ์ ๋ฌธ์ด ๋์ค๋ฉด, ๊ตฌ๋ ์ ๋ฆฌ์คํธ์ ์๋ ์ฌ๋๋ค์๊ฒ ์ ๋ฌธ์ ๋ฟ๋ฆฝ๋๋ค.
- ๊ตฌ๋ ์(Observer)๋ ๊ตฌ๋ ์ ์ ์ฒญํ๋ค๋ฉด ์๋์ผ๋ก ์ ๋ฌธ์ ๋ฐ์ต๋๋ค.
- ๋๊ตฐ๊ฐ ๊ตฌ๋ ์ ์ทจ์ํ๋ฉด, ๊ทธ ์ฌ๋์ ๋ ์ด์ ์ ๋ฌธ์ ๋ฐ์ง ์์ต๋๋ค.
๊ตฌ๋ ์๊ฐ ์ ๋ฌธ์ฌ์ ๋งค๋ฒ “์ค๋ ์ ๋ฌธ ๋์๋์?” ํ๊ณ ๋ฌผ์ด๋ณด๋ ๋ฐฉ์์ด ์๋๋ผ, ์ ๋ฌธ์ฌ๊ฐ ์์์ ๋ณด๋ด์ฃผ๋ ๊ตฌ์กฐ๋ผ์ ํจ์ฌ ํจ์จ์ ์ ๋๋ค.
๊ฐ๋จํ ์ฝ๋ ์์ (JavaScript)
๊ธฐ๋ณธ ์ฝ๋ ๊ตฌ์กฐ
์ต์ ๋ฒ ํจํด์ ๊ตฌํํ๊ธฐ ์ํด ํ์ํ ์ญํ ์ ์ธ ๊ฐ์ง์ ๋๋ค.
- ๊ตฌ๋ ์๋ฅผ ๋ฑ๋กํ๋ ๊ธฐ๋ฅ (subscribe)
- ๊ตฌ๋ ์๋ฅผ ํด์งํ๋ ๊ธฐ๋ฅ (unsubscribe)
- ์ด๋ฒคํธ ๋ฐ์ ์ ์๋ฆผ์ ๋ณด๋ด๋ ๊ธฐ๋ฅ (notify)
class Observable {
constructor() {
this._observers = new Set();
}
subscribe(observer) {
this._observers.add(observer);
}
unsubscribe(observer) {
this._observers.delete(observer);
}
notify(data) {
this._observers.forEach(observer => observer(data));
}
}
์ฌ๊ธฐ์๋ Set ์๋ฃ๊ตฌ์กฐ๋ฅผ ์ด์ฉํด ์ค๋ณต ์๋ ๊ตฌ๋ ์ ๋ฆฌ์คํธ๋ฅผ ๊ด๋ฆฌํ์ต๋๋ค.
๊ตฌ๋ ์๋ subscribe๋ฅผ ํตํด ๋ฑ๋ก๋๊ณ , notify๊ฐ ํธ์ถ๋๋ฉด ๊ตฌ๋ ์๋ค์๊ฒ ๋ฐ์ดํฐ๊ฐ ์ ๋ฌ๋ฉ๋๋ค.
ํธ์ถ ๊ด๊ณ ํ๋ฆ
์ต์ ๋ฒ ํจํด์์ ํธ์ถ ๊ด๊ณ๋ ๋จ์ํฉ๋๋ค.
Observable → Observer
- Observer๋ ๋ฐ์ดํฐ๋ฅผ ์ง์ ์์ฒญ(pull)ํ์ง ์๊ณ , Observable์ด ์์์ ์๋ ค์ฃผ๊ธฐ๋ฅผ ๊ธฐ๋ค๋ฆฝ๋๋ค.
- ๋ฐ๋ผ์ Observer๋ ๋จ์ํ ๊ตฌ๋ ๋ง ํ๊ณ ๊ฒฐ๊ณผ๋ฅผ ์ ๋ฌ๋ฐ์ต๋๋ค.
์ด๊ฒ์ด ๋ฐ๋ก ๋จ๋ฐฉํฅ ๋ฐ์ดํฐ ํ๋ฆ (Unidirectional Data Flow) ์ ๋๋ค.
MVC ํจํด์ Observer Pattern ์ ์ฉ : M–V ๊ด๊ณ
์ต์ ๋ฒ ํจํด์ MVC(Model–View–Controller) ๊ตฌ์กฐ์์๋ ์์ฃผ ์ฌ์ฉ๋ฉ๋๋ค.
- Model: ์ํ๋ฅผ ๊ด๋ฆฌํ๊ณ , ๋ณํ๊ฐ ์๊ธฐ๋ฉด ์๋ฆผ์ ๋ฐํํฉ๋๋ค. (Observable ์ญํ )
- View: ์ํ๋ฅผ ๊ตฌ๋ ํ๊ณ , ๋ณํ๊ฐ ์๊ธฐ๋ฉด ํ๋ฉด์ ์ ๋ฐ์ดํธํฉ๋๋ค. (Observer ์ญํ )
์ด๋ฒคํธ ๋ฐ์ → ์ํ ๋ณ๊ฒฝ(Model) → ํ๋ฉด ๋ฐ์(View)
Todo App ์์
์๋ฅผ ๋ค์ด Todo ์ฑ์์ Model์ด todos ๋ฐ์ดํฐ๋ฅผ ๊ด๋ฆฌํ๊ณ , View๋ ์ด๋ฅผ ํ๋ฉด์ ํ์ํ๋ค๊ณ ํด๋ด ์๋ค.
Model์ด Observable์ ์์๋ฐ์ผ๋ฉด, ๊ตฌ๋ ๊ด๋ฆฌ ๋ก์ง์ ๋ฐ๋ก ์์ฑํ ํ์๊ฐ ์์ต๋๋ค.
class TodoModel extends Observable {
constructor() {
super();
this.todos = [];
}
addTodo(todo) {
this.todos = [...this.todos, todo];
this.notify(this.todos); // Observer๋ค์๊ฒ ์
๋ฐ์ดํธ๋ todos ์ ๋ฌ
}
}
- TodoModel์ ์๋ก์ด todo๋ฅผ ์ถ๊ฐํ๊ณ ,
- ์ํ๊ฐ ๋ฐ๋ ๋๋ง๋ค notify๋ก ๊ตฌ๋ ์(View)์ ์๋ฆผ์ ๋ณด๋ ๋๋ค.
Store-TextView ์์
// Subject (๋ฐํ์)
const store = {
state: { issueState: "open" },
listeners: [],
setState(patch) {
this.state = { ...this.state, ...patch };
this.notify();
},
subscribe(fn) {
this.listeners.push(fn);
},
notify() {
this.listeners.forEach(fn => fn(this.state));
}
};
// Observer (๊ตฌ๋
์)
const TextView = {
render(state) {
console.log("ํ์ฌ ์ํ:", state.issueState);
}
};
// ๊ตฌ๋
์ฐ๊ฒฐ
store.subscribe(TextView.render);
// ์คํ ์์
store.setState({ issueState: "closed" });
// ์ถ๋ ฅ: "ํ์ฌ ์ํ: closed"
์ฌ๊ธฐ์ store๋ Subject, TextView๋ Observer ์ญํ ์ ํฉ๋๋ค.
์ด๋ ๋ฏ, store์ ์ํ๊ฐ ๋ฐ๋๋ฉด, ์๋์ผ๋ก TextView.render๊ฐ ํธ์ถ๋๋ฉฐ ํ๋ฉด์ ๋ค์ ๋ ๋๋งํฉ๋๋ค.
- ์ต์ ๋ฒ ํจํด์ ๋ฐํ–๊ตฌ๋ ๋ชจ๋ธ๋ก, ๋ชจ๋ ๊ฐ ๊ฒฐํฉ๋๋ฅผ ๋ฎ์ถ๊ณ ๋จ๋ฐฉํฅ ๋ฐ์ดํฐ ํ๋ฆ์ ๋ณด์ฅํฉ๋๋ค.
- MVC ๊ตฌ์กฐ์์ Model์ Observable, View๋ Observer๊ฐ ๋์ด ์์ฐ์ค๋ฝ๊ฒ ์ํ ๋ณํ๊ฐ UI์ ๋ฐ์๋ฉ๋๋ค.
- ์ค๋ฌด์์๋ React, Vue ๊ฐ์ ํ๋ ์์ํฌ ๋ด๋ถ์์๋ ์ต์ ๋ฒ ํจํด์ด ๊ธฐ๋ณธ ์๋ฆฌ๋ก ํ์ฉ๋๊ณ ์์ต๋๋ค
์ต์ ๋ฒ ํจํด์ ์ฅ์
- ๊ฒฐํฉ๋ ๊ฐ์
- Subject๋ Observer์ ๊ตฌ์ฒด์ ์ธ ๋์์ ๋ชฐ๋ผ๋ ๋ฉ๋๋ค. ๊ทธ๋ฅ “์ํ ๋ฐ๋์์ด!”๋ง ์๋ฆฌ๋ฉด ๋.
- ์ ์ฐ์ฑ ์ฆ๊ฐ
- ์๋ก์ด Observer๋ฅผ ์ถ๊ฐํ๋๋ผ๋ Subject ์ฝ๋๋ฅผ ์์ ํ ํ์๊ฐ ์์ต๋๋ค.
- ์๋ํ๋ ๋ฐ์
- ์ํ ๋ณ๊ฒฝ → ์๋ฆผ → UI ์ ๋ฐ์ดํธ๊ฐ ์๋์ผ๋ก ์ฐ๊ฒฐ๋ฉ๋๋ค.
- ๋ทฐ๋ฅผ ์ง์ ์ผ์ผํ ํธ์ถํ์ง ์๊ณ ๋, ๋ชจ๋ธ์ ๊ด์ฌ์ด ์๋ค๊ณ ๊ตฌ๋ ์ ํด๋๊ณ , ์๋์ผ๋ก ๋ณ๊ฒฝ๋๋๋ก ํ ์ ์์ต๋๋ค.
- = Controller ์์ ์ด๋ฒคํธ ๋ฐ์์ ๊ด๋ จ๋ ๋ทฐ๋ฅผ ์ง์ ์ผ์ผํ ์ ๋ฐ์ดํธํ์ง ์์๋ ๋ฉ๋๋ค.
- Model ๊ฐ๋ง ๋ฐ๋๋ฉด ๊ทธ์ ์ฐ๊ดํ View ๊ฐ ์๋์ผ๋ก ๋ฐ๋๋๋ค
์ฃผ์ํ ์
- ์ด๋ฒคํธ๊ฐ ์ด๋๋ก ํ๋ฌ๊ฐ๋์ง ์ถ์ ์ด ์ด๋ ค์ธ ์ ์์ต๋๋ค.
Observer๊ฐ ๋ง์์ง๋ฉด, ๋๊ฐ ๋ฌด์์ ๋ฐ์ํ๋์ง ์ฝ๋๋ง ๋ณด๊ณ ํ์ ํ๊ธฐ ํ๋ค์ด์ง ์ ์์ต๋๋ค. - ๊ตฌ๋
ํด์ (unsubscribe)๋ฅผ ๊ด๋ฆฌํ์ง ์์ผ๋ฉด ๋ฉ๋ชจ๋ฆฌ ๋์๊ฐ ๋ฐ์ํ ์ ์์ต๋๋ค.
์ฌ๋ผ์ง ๊ฐ์ฒด๋ ๊ณ์ ์๋ฆผ์ ๋ฐ์ ์ ์๊ธฐ ๋๋ฌธ์ ๋๋ค.
์ต์ ๋ฒ ํจํด์ “์ํ ๋ณํ → ์๋ ๋ฐ์”์ด๋ผ๋ ๋จ์ํ ์์ด๋์ด์์ ์์ํฉ๋๋ค.
ํ๋ก ํธ์๋ ํ๋ ์์ํฌ(React, Vue)์์ ์ํ ๊ด๋ฆฌ(Store–View ๊ตฌ์กฐ)์ ๋ง์ด ์์ฉ๋๊ณ ,
๋ฐฑ์๋์์๋ ์ด๋ฒคํธ ๊ธฐ๋ฐ ์ํคํ
์ฒ๋ฅผ ์ค๊ณํ ๋ ์์ฃผ ์ฌ์ฉ๋ฉ๋๋ค.
ํต์ฌ์ “๋ด๊ฐ ์ผ์ผ์ด ์ํ๋ฅผ Pulling ํ์ง ์์๋, ์ํ๊ฐ ๋ฐ๋๋ฉด ์๋์ผ๋ก ์๋ฆผ์ด ์ ๋ฌ๋๋ค”๋ ์ ์ ๋๋ค.
๊ทธ๋ผ ์ด์ด์ React์์ ์ต์ ๋ฒ ํจํด์ด ์ด๋ป๊ฒ ํ์ฉ๋๋์ง๋ฅผ ์์๋ก ๋ณด์ฌ๋๋ฆฌ๊ฒ ์ต๋๋ค.
React์์์ ์ต์ ๋ฒ ํจํด
React๋ ๋ด๋ถ์ ์ผ๋ก ์ต์ ๋ฒ ํจํด๊ณผ ์ ์ฌํ ๊ฐ๋ ์ ์ฌ์ฉํฉ๋๋ค.
- ์ํ(state) ๊ฐ ๋ฐ๋๋ฉด,
- ์ด๋ฅผ ๊ตฌ๋ ํ๊ณ ์๋ ์ปดํฌ๋ํธ๊ฐ ์๋์ผ๋ก ๋ค์ ๋ ๋๋ง๋ฉ๋๋ค.
์ฆ, store → view ๊ตฌ์กฐ์ ๊ฑฐ์ ๋๊ฐ์ต๋๋ค.
๊ฐ๋จํ ์์
import React, { useState } from "react";
function App() {
const [issueState, setIssueState] = useState("open");
return (
<div>
<TextView issueState={issueState} />
<Menu setIssueState={setIssueState} />
</div>
);
}
function TextView({ issueState }) {
return <p>ํ์ฌ ์ํ: {issueState}</p>;
}
function Menu({ setIssueState }) {
return (
<div>
<button onClick={() => setIssueState("open")}>Open</button>
<button onClick={() => setIssueState("closed")}>Close</button>
</div>
);
}
export default App;
๋์ ์ค๋ช
- App ์ปดํฌ๋ํธ๋ ์ํ(issueState)๋ฅผ ๊ฐ์ง๊ณ ์์ต๋๋ค.
→ ์ด๊ฒ ๋ฐ๋ก Subject ์ญํ ์ ๋๋ค. - TextView ์ปดํฌ๋ํธ๋ issueState๋ฅผ props๋ก ๋ฐ์ ํ๋ฉด์ ๋ ๋๋งํฉ๋๋ค.
→ ์ด๊ฒ ๋ฐ๋ก Observer์ ๋๋ค. - ๋ฒํผ์ ๋๋ฌ setIssueState๋ก ์ํ๋ฅผ ๋ฐ๊พธ๋ฉด, React๋ ์๋์ผ๋ก ํด๋น ์ํ๋ฅผ ๊ตฌ๋ ํ๋ ์ปดํฌ๋ํธ๋ฅผ ์ฐพ์ ๋ฆฌ๋ ๋๋งํฉ๋๋ค.
์ ๋ฆฌ
- ์ ํต์ ์ธ ์ต์ ๋ฒ ํจํด์์๋ Subject → notify → Observer ๊ตฌ์กฐ๋ก ๋์ํฉ๋๋ค.
- React์์๋ ์ด ํ๋ฆ์ด state → ๋ฆฌ๋ ๋๋ง → UI ์ ๋ฐ์ดํธ๋ก ์ถ์ํ๋์ด ์์ต๋๋ค.
- ๋๋ถ์ ๊ฐ๋ฐ์๋ notify() ๊ฐ์ ์ฝ๋๋ฅผ ์ง์ ์์ฑํ ํ์ ์์ด, useState๋ useReducer๋ง์ผ๋ก ์ต์ ๋ฒ ํจํด์ ์ด์ ์ ๋๋ฆด ์ ์์ต๋๋ค.
์ต์ ๋ฒ ํจํด์ ๋จ์ํ ์ด๋ก ์ด ์๋๋ผ, React์ ๊ทผ๊ฐ์ ๋ น์ ์๋ ๊ฐ๋ ์ด๋ผ๊ณ ์ดํดํ ์ ์์ต๋๋ค.
'๐ ํ๋ก ํธ์๋(FE)' ์นดํ ๊ณ ๋ฆฌ์ ๋ค๋ฅธ ๊ธ
| Webpack Tree Shaking ๋์ ์๋ฆฌ (0) | 2025.09.17 |
|---|---|
| Webpack + Babel + TypeScript ์ค์ ๊ฐ์ด๋ (0) | 2025.09.16 |
| [Javascript] ์ด๋ฒคํธ ์ ํ & ์ด๋ฒคํธ ์์ (1) | 2025.09.08 |
| [CoderPad] ๋ฆฌ์กํธ ๊ฐ์ ์คํฌ๋กค (Virtualized List) (5) | 2025.06.22 |
| Next.js 12 ์์ React Query v4 + SSR ํ์ฉํ๊ธฐ (0) | 2025.05.01 |