ð 07. ìŽë²€íž 룚í â JSê° ë©ì¶ì§ ìë ìŽì , ë¹ëêž°ê° ìëíë ì§ì§ ì늬
ð ê°ì
ìœ ì€í, íì€í¬ í, ë§ìŽí¬ë¡íì€í¬ íì êŽê³, ë ëë§ íìŽë°, setTimeout(0)ì ì§ì§ ì믞륌 ì€ë¬Ž êŽì ìŒë¡ ìì ì ë³µí©ëë€.
ð¯ ìŽ ì¹ì ì ìœê³ ë멎:
- ìœ ì€í, ì¹ API, íì€í¬ í, ë§ìŽí¬ë¡íì€í¬ íì êŽê³ë¥Œ 귞늌ìŒë¡ ì€ëª í ì ìë€.
setTimeout(fn, 0)ìŽ ì ìŠì ì€íëì§ ìëì§ ìŽì 륌 ìë€.- Promise
.thenìŽsetTimeoutë³Žë€ ëšŒì ì€íëë ìŽì 륌 ì€ëª í ì ìë€.
ð 목찚
- ð ìŽ ë¬žì륌 ìœêž° ì ì
- ð€ 1. ì ìììŒ íëê°?
- ðïž 2. JS ë°íì ìí€í ì²
- ð 3. ìŽë²€íž 룚íì ëì ìì
- ðŒ 4. ì€ë¬Ž íšíŽ ë° íšì
- ð ë§ë¬ŽëЬ íŽìŠ
- ð£ ìì² ìŽì íŽê·Œ ìŒêž°
- ð ë ìì볎Ʞ
ð ìŽ ë¬žì륌 ìœêž° ì ì
â±ïž ìì ìœêž° ìê°: 18ë¶(ì 첎) / íµì¬ íížë§: 11ë¶
ðºïž ìŽ ë¬žìì íëŠ
[ëšìŒ ì€ë ëì íì€] â [ìœì€í + í 구조] â [ë§ìŽí¬ë¡ vs ë§€í¬ë¡ íì€í¬] â [ì€ë¬Ž ì ì©]
ð¯ ìŽ ë¬žì륌 ë€ ìœìŒë©Ž í ì ìë ê²
-
setTimeout(fn, 0)ìœë°±ìŽ Promise.thenë³Žë€ ëì€ì ì€íëë ìŽì 륌 ì€ëª íë€. - ꞎ ëêž° ìì ìŽ UI륌 ë©ì¶ê² íë ìŽì ì íŽê²°ì± ì ìë€.
-
queueMicrotask륌 ìžì ì°ëì§ ìŽíŽíë€.
ðºïž ìŽ ë¬žìì 배겜 ìžê³êŽ: 'ììë€ ì»€ë®€ëí°'
ð£ ìì² : "ìíž ë, ìŽìí ë²ê·žê° ììŽì. ê²ì 결곌 5ì² ê°ë¥Œ DOMì í ë²ì ë ëë§íë ìœë륌 ì€ííëë... ëžëŒì°ì ê° ìì í ìŒìŽë²ë žìŽì! ì€í¬ë¡€ë ì ëê³ , íŽëŠë ì ëê³ , ëª ìŽ ì§ëììŒ ê²°ê³Œê° ë¿ë €ì§ëë° â ìŽ ëì ì ì ê° ì묎ê²ë 못 íŽì. JavaScriptê° ì±êž ì€ë ëëŒì ê·žë° ê±Žê°ì?"
ðŠ ìíž: "ì ííŽ. JSë ì±êž ì€ë ëìŒ. ë¬Žê±°ìŽ ëêž° ìì ìŽ ìœ ì€íì ì°šì§í멎 ìŽë²€íž 룚íê° ë€ë¥ž ìŒì ì²ëЬ 못 íŽ â íŽëŠ, ì€í¬ë¡€, ë ëë§ ì ë¶ ë©ì¶°. ìŽê² ìŽë²€íž 룚íì ìë ë°©ìì 몚륎멎 ë§ëë ëíì ìž UX ì¬ììŽìŒ. ì€ë ìŽë²€íž 룚í ëŽë¶ 구조륌 ìŽíŽí멎, ëžëŒì°ì 륌 ë©ì¶ì§ ìë ìœë륌 ì§€ ì ìê² ëŒ."
ð€ 1. ì ìììŒ íëê°?
ìì ëì UX ìŽì ì ë³Žê° ìŽ ëªšë ê³µë¶ì ìììŽìë€. "ë²íŒ ë륎멎 íë©ŽìŽ 3ìŽ ëì ìŒìŽì." ê·ž ììž íë륌 íê³ ë€ë©Ž JS ë°íì ì ì²Žê° ë³Žìžë€.
JSë ì±êž ì€ë ë ë€. í ë²ì íëì ìì ë§ ì€íí ì ìë€. ê·žë°ë° ìŽë»ê²:
- HTTP ìì²ì í멎ìë íë©ŽìŽ ë©ì¶ì§ ìëê°?
- íìŽëšžê° ë§ë£ë멎 ìëìŒë¡ ìœë°±ìŽ ì€íëëê°?
- íŽëŠ, ì€í¬ë¡€ ìŽë²€ížê° ë€ë¥ž ìœëì ëìì ì²ëЬëëê°?
ìŽ ëªšë ê²ìŽ ìŽë²€íž 룚í(Event Loop) ê° êŽëЬíë ë¹ëêž° ì²ëЬ ë©ì»€ëìŠ ëë¶ìŽë€.
ðïž 2. JS ë°íì ìí€í ì²
ëšìŒ ì€ë ëì ë¹ë°
âââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââ
â JS ë°íì (ëžëŒì°ì ) â
â â
â ââââââââââââââââ ââââââââââââââââââââââââââââââââââââ â
â â JS ìì§ â â Web APIs â â
â â (V8 ë±) â â (setTimeout, fetch, DOM events) â â
â â â â â â
â â ââââââââââ â â ë¹ëêž° ìì
ì JS ì€ë ë ë°ìì â â
â â âìœ ì€í â â â ì²ëЬ â ìë£ ì íì ìœë°± ì¶ê° â â
â â ââââââââââ â ââââââââââââââââââââââââââââââââââââ â
â â â â â
â â ââââââââââ â ââââââââââââââââââŒâââââââââââââââââââ â
â â â í â â â íì€í¬ í (Macro) â â
â â â(ë©ëªšëЬ)â â â setTimeout, setInterval, I/O â â
â â ââââââââââ â âââââââââââââââââââââââââââââââââââââ â
â ââââââââââââââââ â â
â â² ââââââââââââŒâââââââââââââââââââ â
â â â ë§ìŽí¬ë¡íì€í¬ í â â
â â â Promise.then, queueMicrotaskâ â
â â âââââââââââââââââââââââââââââââ â
â â â â
â âââââ ìŽë²€íž 룚íê° ì€ì¬ âââââ â
âââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââ
ìœ ì€í (Call Stack)
íšìê° ì€íë ëë§ë€ 찚곡찚곡 ììŽë ê³µê°ì ëë€. ìì² ìŽê° ê²ìêž 2,000ê°ë¥Œ í ë²ì í멎ì ê·žëŠ¬ë €ë€ ëžëŒì°ì 륌 ë©ì¶ê² íë ìí©ì ìœ ì€íì êŽì ìì ìŽíŽíŽ ë³Žê² ìµëë€.
function a() { b(); }
function b() { c(); }
function c() { console.log("ì€í!"); }
a();
// ìœ ì€í ë³í:
// [GEC] â [GEC, a] â [GEC, a, b] â [GEC, a, b, c]
// "ì€í!" ì¶ë ¥
// [GEC, a, b, c] â [GEC, a, b] â [GEC, a] â [GEC] â []
// ì€íìŽ ë¹ìì§ë©Ž ìŽë²€íž 룚íê° í륌 íìžì¹ API & íì€í¬ í
setTimeoutìŽë fetch ê°ì ìì
ì JS ìì§ ë°ìì ì²ëЬë©ëë€. ìì
ìŽ ëë멎 ìœë°± íšìë€ìŽ 'ì€'ì ìì êž°ë€ëЬëë°, ìŽê³³ìŽ ë°ë¡ íì€í¬ íì
ëë€.
console.log("ëêž° 1");
setTimeout(() => {
console.log("íìŽëšž ìœë°±"); // íì€í¬ í â ìœ ì€íìŽ ë¹ ë ì€í
}, 0);
console.log("ëêž° 2");
// ì¶ë ¥ ìì:
// "ëêž° 1" â ìœ ì€íìì ìŠì
// "ëêž° 2" â ìœ ì€íìì ìŠì
// "íìŽëšž ìœë°±" â ìœ ì€íìŽ ë¹ìì§ í íì€í¬ íììë§ìŽí¬ë¡íì€í¬ í (Microtask Queue)
Promise.then, queueMicrotask, MutationObserverë ë§ìŽí¬ë¡íì€í¬ í ì ë€ìŽê°ë€. ìŽ íë íì€í¬ íë³Žë€ ì°ì ììê° ëë€.
ð 3. ìŽë²€íž 룚íì ëì ìì
ì€í ì°ì ìì
ìŽë²€íž 룚íë ì íŽì§ ììì ë°ëŒ í륌 íìží©ëë€. í¹í ë§ìŽí¬ë¡íì€í¬ íë 'í¹ê¶ìžµ'곌 ê°ìì, ìŒë° íì€í¬ íë³Žë€ ëšŒì , ê·žëŠ¬ê³ ìì í ë¹ìì§ ëê¹ì§ ì²ëЬë©ëë€.
1. ìœ ì€íì ëêž° ìœë ì€í
2. ìœ ì€íìŽ ë¹ìŽì§ë©Ž:
2-1. ë§ìŽí¬ë¡íì€í¬ í ì ë¶ ìì§ (íëì© êºŒëŽ ì€í)
â ë§ìŽí¬ë¡íì€í¬ìì ì ë§ìŽí¬ë¡íì€í¬ ì¶ê°ëŒë ì ë¶ ì²ëЬ
2-2. ë ëë§ ì
ë°ìŽíž (íì ì, ëžëŒì°ì )
2-3. íì€í¬ íìì íì€í¬ íë êºŒëŽ ì€í
2-4. ë€ì 2-1ë¶í° ë°ë³µ
// ì€í ìì ììž¡ ì°ìµ
console.log("1ïžâ£ ëêž°");
setTimeout(() => console.log("4ïžâ£ íì€í¬ í (setTimeout)"), 0);
Promise.resolve()
.then(() => console.log("3ïžâ£ ë§ìŽí¬ë¡íì€í¬ (Promise.then)"))
.then(() => console.log("3ïžâ£-2 ë§ìŽí¬ë¡íì€í¬ (ë ë²ì§ž then)"));
console.log("2ïžâ£ ëêž°");
// ì¶ë ¥:
// 1ïžâ£ ëêž°
// 2ïžâ£ ëêž°
// 3ïžâ£ ë§ìŽí¬ë¡íì€í¬ (Promise.then) â íì€í¬ íë³Žë€ ëšŒì !
// 3ïžâ£-2 ë§ìŽí¬ë¡íì€í¬ (ë ë²ì§ž then) â ë§ìŽí¬ë¡íì€í¬ íê° ë¹ ëê¹ì§
// 4ïžâ£ íì€í¬ í (setTimeout) â ë§ìŽí¬ë¡íì€í¬ ë€ ëë íë ëë§ íìŽë°
ëžëŒì°ì ê° í멎ì 귞늬ë ê²ë íëì 'íì€í¬'ì ëë€. ìœ ì€íìŽ ë°ì멎 ìŽ ìì€í ë ëë§ ìì ì¡°ì°š ë€ë¡ ë°ëŠ¬ê² ë©ëë€.
// ììë€ ì»€ë®€ëí° â ì DOM ì
ë°ìŽížê° í ë²ì ë°ìëëê°?
const list = document.getElementById("post-list");
// ìŽ ë£šíë ëêž°ì ìŒë¡ ì€íëë ëì DOMì 100ë² ì¡°ìíì§ë§
// í멎ìë ëš í ë²ë§ ë ëë§ëë€
for (let i = 0; i < 100; i++) {
const item = document.createElement("li");
item.textContent = `ê²ìêž ${i}`;
list.appendChild(item);
}
// ìœ ì€íìŽ ë¹ìì§ í, ìŽë²€íž 룚íê° ë ëë§ ì
ë°ìŽíž ëšê³ìì í ë²ì ë°ì
// â ì ꞎ 룚íë UI륌 ë©ì¶ëê°?
for (let i = 0; i < 10_000_000; i++) {
// 10ë§ ë² ë°ë³µ â ìœ ì€íìŽ ì€ë«ëì ì°šìì
// ìŽë²€íž 룚íê° ë ëë§, íŽëŠ, ì€í¬ë¡€ì ì²ëЬ ëª»íš â UI ë©ì¶€
}ðŒ 4. ì€ë¬Ž íšíŽ ë° íšì
ìì ëììŽëì íŒëë°±ì ë°ì ìíž ëŠ¬ë ëìŽ ìì² ìŽìê² ì ìí ë¹ë²ì ëë€. "ìì² ë, ë¬Žê±°ìŽ ìì ì í꺌ë²ì íë € íì§ ë§ê³ , ì€ê°ì€ê° ìš ìŽ í(ìŽë²€íž 룚í ì ìŽê¶)ì ì€ìŒ íŽì."
// â
ë¬Žê±°ìŽ ìì
ì ëë ì€í â UI ë©ì¶€ ë°©ì§
// â í ë²ì ì²ëЬ â UI ëžë¡í¹
function renderAllPosts(posts) {
posts.forEach((post) => {
const el = createPostElement(post);
container.appendChild(el);
});
}
// â
ì²í¬ ëšì + setTimeoutìŒë¡ ìŽë²€íž 룚íì ì볎
function renderPostsInChunks(posts, chunkSize = 50) {
let index = 0;
function renderChunk() {
const chunk = posts.slice(index, index + chunkSize);
chunk.forEach((post) => {
const el = createPostElement(post);
container.appendChild(el);
});
index += chunkSize;
if (index < posts.length) {
setTimeout(renderChunk, 0); // ìŽë²€íž 룚íì ì ìŽê¶ ë°í â ë ëë§ + ìŽë²€íž ì²ëЬ ê°ë¥
}
}
renderChunk();
}
// â
requestAnimationFrame â ë ëë§ ì§ì ì ì€í (ì ëë©ìŽì
ì ìµì )
function animate() {
// ë§€ íë ì(16ms)ë§ë€ ì€í
updateAnimation();
requestAnimationFrame(animate); // ë€ì íë ì ììœ
}
requestAnimationFrame(animate);
// â
queueMicrotask â Promise ì€ë²í€ë ììŽ ë§ìŽí¬ë¡íì€í¬ íì ì¶ê°
queueMicrotask(() => {
console.log("ë§ìŽí¬ë¡íì€í¬ë¡ ì€í");
});
// Promise.resolve().then(fn)곌 ëìŒíì§ë§ ë ëª
ìì ìŽê³ ê°ë²ŒìsetTimeout(fn, 0) íšíŽì ì€ì ì믞:
setTimeout(fn, 0)ì "ì§êž ë¹ì¥"ìŽ ìëëŒ "íì¬ ì€í ì€ìž ëêž° ìœëì ë§ìŽí¬ë¡íì€í¬ê° ì ë¶ ëë ë€, ë€ì ë ëë§ ìŽíì" ì€ííë€ë ì믞ë€. UI ì ë°ìŽížë DOM ìœêž° ìì ì íì¬ ë ëë§ ìŽíë¡ ë¯žë£° ë ì ì©íë€.
ð ë§ë¬ŽëЬ íŽìŠ
Q1. ìë ìœëì ì¶ë ¥ ìì륌 ì íí ììž¡íëŒ.
console.log("A");
setTimeout(() => console.log("B"), 0);
Promise.resolve().then(() => {
console.log("C");
setTimeout(() => console.log("D"), 0);
});
console.log("E");â
ì ëµ: A, E, C, B, D
ð¡ ììž íŽì€:
A: ëêž° ìœë ìŠì ì€ísetTimeout B: íì€í¬ íì ì¶ê° (0ms í íì ë€ìŽê°)Promise.resolve().then(...): ë§ìŽí¬ë¡íì€í¬ íì ìœë°± ì¶ê°E: ëêž° ìœë ìŠì ì€í- ìœ ì€íìŽ ë¹ â ë§ìŽí¬ë¡íì€í¬ í ì²ëЬ:
Cì¶ë ¥, 귞늬ê³setTimeout Dê° íì€í¬ íì ì¶ê°ëš
- ë§ìŽí¬ë¡íì€í¬ íê° ë¹ â ë ëë§ â íì€í¬ í ì²ëЬ:
Bì¶ë ¥ (뚌ì ë±ë¡ë setTimeout)- ë€ì ë§ìŽí¬ë¡íì€í¬ í ì²ëЬ(ìì) â ë ëë§ â
Dì¶ë ¥
- ð íµì¬ êž°ìµë²: "ëêž° â ë§ìŽí¬ë¡íì€í¬ ì ë¶ â ë ëë§ â íì€í¬ íë â ë€ì ë§ìŽí¬ë¡íì€í¬"
Q2. ꞎ ëêž° 룚íê° UI륌 ë©ì¶ë ìŽì ì íŽê²°ì± ì?
â
ì ëµ: ìœ ì€íìŽ ë¹ìì§ì§ ìì ìŽë²€íž 룚íê° ë ëë§ê³Œ ìŽë²€íž ì²ëŠ¬ë¥Œ 못 íë€. setTimeout(fn, 0) ì²í¬ ë¶í ëë requestAnimationFrameìŒë¡ ì²ëŠ¬ë¥Œ ëë ìŒ íë€.
ð¡ ììž íŽì€:
- ìŽë²€íž 룚íë ìœ ì€íìŽ ìì í ë¹ìŽìŒë§ ë€ì ìì (ë ëë§, ìŽë²€íž ì²ëЬ)ì ìííë€.
- 10ë§ ë² ë°ë³µíë 룚íê° ì€í ì€ìŽë©Ž, ê·ž ëì íŽëŠ, ì€í¬ë¡€, í멎 ì ë°ìŽíž ì ë¶ ì°šëšëë€.
- íŽê²°ì±
1:
setTimeout(fn, 0)ìŒë¡ ì²í¬ ë¶í â ë§€ ì²í¬ í ìŽë²€íž 룚íì ì ìŽê¶ ë°í - íŽê²°ì±
2:
requestAnimationFrameâ íë ì ëšìë¡ ë¶í - íŽê²°ì± 3: Web Worker â ë³ë ì€ë ëìì ë¬Žê±°ìŽ ì°ì° ì²ëЬ (UI ì€ë ëì ìí¥ ìì)
- ð íµì¬ êž°ìµë²: "ꞎ ëêž° ìì
ì ìŽë²€íž 룚í륌 굶ꞎë€. ì€ê°ì€ê°
setTimeout(0)ìŒë¡ ëš¹ìŽë¥Œ ì€ëŒ."
Q3. ìì² ìŽì í ì€íž íì â ìì ëììŽëì UX 늬뷰
ìì ëìŽ ê±±ì ì€ë¬ìŽ ëª©ìëŠ¬ë¡ ë§í©ëë€. "ìì² ë, ê²ì ê²°ê³Œê° ë° ë íë©ŽìŽ 0.5ìŽ ì ë ëë ë겚 볎ì¬ì. íŽëŠë ì ì ëš¹íë ê² ê°ê³ ì. ìŽê±° ê°ì í ë°©ë²ìŽ ììê¹ì?"
ìì² ìŽì ìœë: ê²ì 결곌 2000ê°ë¥Œ 룚íë¡ DOMì ì§ì ì¶ê°. ìŽë»ê² UX륌 ê°ì íŽìŒ íëê°?
â
ì ëµ: ì²í¬ ëšì ë ëë§ + setTimeout(fn, 0)ìŒë¡ ìŽë²€íž 룚íì 죌Ʞì ìŒë¡ ì ìŽê¶ì ì볎íê±°ë, ê°ì ì€í¬ë¡€(Virtual Scroll)ì ëì
íë€.
ð¡ ììž íŽì€:
// â
ì²í¬ ë¶í ë ëë§
function renderResults(results) {
const CHUNK_SIZE = 50;
let index = 0;
function renderNext() {
const end = Math.min(index + CHUNK_SIZE, results.length);
for (; index < end; index++) {
appendItem(results[index]);
}
if (index < results.length) {
requestAnimationFrame(renderNext); // ë€ì íë ìì ìŽìŽì
}
}
renderNext();
}- ë§€ 50ê° ë ëë§ í
requestAnimationFrameìŒë¡ ì ìŽê¶ ë°í â ì€ê°ì€ê° íë©ŽìŽ ì ë°ìŽížëìŽ ì¬ì©ìë ì ì§ì ìŒë¡ ëŽì©ì 볌 ì ìì - ê°ì ì€í¬ë¡€: í멎ì 볎ìŽë íëª©ë§ DOMì ì ì§ (react-window, tanstack-virtual ë±) â 2000ê°ë¥Œ í ë²ì DOMì ë£ì§ ìë 귌볞ì íŽê²°ì±
- ð íµì¬ êž°ìµë²: "DOMì í ë²ì 2000ê° ë£ë 걎 íí í¬íë€. íë ì ëšìë¡ ëë ë£ê±°ë, ê°ì ì€í¬ë¡€ë¡ 볎ìŽë ê²ë§ ë ëë§íŽëŒ."
ð£ ìì² ìŽì íŽê·Œ ìŒêž°
ì€ë ìŽë²€íž 룚í ë°°ì°ë©Žì ì§ì§ë¡ "ì, JSê° ìŽë ê² êµŽë¬ê°ë구ë!" íë ëëìŽ ë€ìë€. ê·žëì ë¹ëêž°ê° 'ìŽë»ê² ëìê°ëì§'륌 ê°ìŒë¡ë§ ììëë°, ì€ëì ìœ ì€í â ë§ìŽí¬ë¡íì€í¬ í â íì€í¬ í ììê° ëšžëŠ¿ìì ê·žë €ì¡ë€.
ê²ì 결곌 2000ê° ë ëë§ ë²ê·ž... ì¬ì€ ì ê±° ëŽê° ë§ë ìœëìŒ. ìì ëìŽ UX 늬뷰ìì ì¡ìì€ì 믌ë§íëë°, ìŽì ì UIê° ë©ì·ëì§ ìŽíŽê° ëë€. ìŽë² 죌ì requestAnimationFrame ìšì ìì í ê±°ë€.
ð¡ ì€ëì êµí: "ìë°ì€í¬ëŠœížë ì±êž ì€ë ëì§ë§, ìŽë²€íž 룚í ëë¶ì ì°ëЬìê² ìŸì í ë¹ëêž° í겜ì ì ì¬í©ëë€. ìœ ì€íì ë묎 ì€ë ë ì íì¬ ìŽë²€íž 룚í륌 êµ¶êž°ì§ ë§ìžì. ë¬Žê±°ìŽ ìì ì ìê² ëëë ê²ìŽ ì§ì í ìëìŽì 믞ëì ëë€."
íŽê·Œíê³ íê° ì°ì±
í멎ì 뚞늿ììŒë¡ ì€í ìì ì뮬ë ìŽì
íŽëŽ€ë€. ìŽì setTimeout(fn, 0)ìŽ ì "ìŠì"ê° ìëì§ ìì í ìŽíŽëë€. ì€ëì ë¿ë¯íê² ì ì ìì ê² ê°ë€.