์ฝ˜ํ…์ธ  ๋Œ€ํ‘œ ์ด๋ฏธ์ง€ - ๐Ÿš€ ์ €์ˆ˜์ค€ ์›น ์ตœ์ ํ™”์™€ ๋ฉ”๋ชจ๋ฆฌ ๊ด€๋ฆฌ: ๊ฐ€๋น„์ง€ ์ปฌ๋ ‰์…˜ ์™„๋ฒฝ ์ดํ•ดํ•˜๊ธฐ

๐Ÿš€ ์ €์ˆ˜์ค€ ์›น ์ตœ์ ํ™”์™€ ๋ฉ”๋ชจ๋ฆฌ ๊ด€๋ฆฌ: ๊ฐ€๋น„์ง€ ์ปฌ๋ ‰์…˜ ์™„๋ฒฝ ์ดํ•ดํ•˜๊ธฐ

์›น ์„ฑ๋Šฅ์„ ๊ทน๋Œ€ํ™”ํ•˜๋Š” ๋ฉ”๋ชจ๋ฆฌ ๊ด€๋ฆฌ์˜ ๋ชจ๋“  ๊ฒƒ

๐Ÿ’ก ์™œ ๋ฉ”๋ชจ๋ฆฌ ๊ด€๋ฆฌ๊ฐ€ ์ค‘์š”ํ• ๊นŒ?

์•ˆ๋…•! ์˜ค๋Š˜์€ ์›น ๊ฐœ๋ฐœ์—์„œ ์ •๋ง ์ค‘์š”ํ•˜์ง€๋งŒ ๋งŽ์€ ๊ฐœ๋ฐœ์ž๋“ค์ด ๊ฐ„๊ณผํ•˜๋Š” ์ฃผ์ œ์— ๋Œ€ํ•ด ์ด์•ผ๊ธฐํ•ด๋ณผ๊ฒŒ. ๋ฐ”๋กœ ์ €์ˆ˜์ค€ ์›น ์ตœ์ ํ™”์™€ ๋ฉ”๋ชจ๋ฆฌ ๊ด€๋ฆฌ์•ผ. ๐ŸŽฏ

ํ˜น์‹œ ์ด๋Ÿฐ ๊ฒฝํ—˜ ์žˆ์–ด? ์ฒ˜์Œ์—” ๋น ๋ฅด๊ฒŒ ์ž‘๋™ํ•˜๋˜ ์›น ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์ด ์‹œ๊ฐ„์ด ์ง€๋‚˜๋ฉด์„œ ์ ์  ๋А๋ ค์ง€๊ณ , ๊ฒฐ๊ตญ ๋ธŒ๋ผ์šฐ์ €๊ฐ€ ๋จนํ†ต์ด ๋˜๋Š” ์ƒํ™ฉ ๋ง์ด์•ผ. ์ด๊ฒŒ ๋ฐ”๋กœ ๋ฉ”๋ชจ๋ฆฌ ๋ˆ„์ˆ˜(Memory Leak)์˜ ์ „ํ˜•์ ์ธ ์ฆ์ƒ์ด์•ผ. ๐Ÿ˜ฐ

ํ˜„๋Œ€ ์›น ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์€ ์ ์  ๋” ๋ณต์žกํ•ด์ง€๊ณ  ์žˆ์–ด. Single Page Application(SPA), ์‹ค์‹œ๊ฐ„ ๋ฐ์ดํ„ฐ ์ฒ˜๋ฆฌ, ๋ณต์žกํ•œ ์‚ฌ์šฉ์ž ์ธํ„ฐ๋ž™์…˜... ์ด ๋ชจ๋“  ๊ฒƒ๋“ค์ด ๋ฉ”๋ชจ๋ฆฌ๋ฅผ ์†Œ๋น„ํ•˜์ง€. ๊ทธ๋ž˜์„œ ๋ฉ”๋ชจ๋ฆฌ๋ฅผ ํšจ์œจ์ ์œผ๋กœ ๊ด€๋ฆฌํ•˜๋Š” ๊ฒƒ์ด ์›น ์„ฑ๋Šฅ ์ตœ์ ํ™”์˜ ํ•ต์‹ฌ์ด ๋˜์—ˆ์–ด.

์žฌ๋Šฅ๋„ท ๊ฐ™์€ ํ”Œ๋žซํผ์—์„œ๋„ ์›น ์ตœ์ ํ™” ์ „๋ฌธ๊ฐ€๋“ค์ด ๋งŽ์ด ํ™œ๋™ํ•˜๊ณ  ์žˆ๋Š”๋ฐ, ์ด๋“ค์ด ๊ฐ€์žฅ ๊ฐ•์กฐํ•˜๋Š” ๊ฒƒ ์ค‘ ํ•˜๋‚˜๊ฐ€ ๋ฐ”๋กœ ๋ฉ”๋ชจ๋ฆฌ ๊ด€๋ฆฌ์•ผ. ์™œ๋ƒํ•˜๋ฉด ์‚ฌ์šฉ์ž ๊ฒฝํ—˜(UX)์— ์ง์ ‘์ ์ธ ์˜ํ–ฅ์„ ๋ฏธ์น˜๊ธฐ ๋•Œ๋ฌธ์ด์ง€! ๐ŸŽจ

๋ฉ”๋ชจ๋ฆฌ ๊ด€๋ฆฌ ํ”„๋กœ์„ธ์Šค ๋ฉ”๋ชจ๋ฆฌ ํ• ๋‹น Memory Allocation ๋ฉ”๋ชจ๋ฆฌ ์‚ฌ์šฉ Memory Usage ๋ฉ”๋ชจ๋ฆฌ ํ•ด์ œ Memory Release ๊ฐ€๋น„์ง€ ์ปฌ๋ ‰ํ„ฐ ์ตœ์ ํ™”๋œ ๋ฉ”๋ชจ๋ฆฌ ์„ฑ๋Šฅ ํ–ฅ์ƒ

๐Ÿง  ๋ฉ”๋ชจ๋ฆฌ ๊ด€๋ฆฌ์˜ ๊ธฐ๋ณธ ๊ฐœ๋…

๋ฉ”๋ชจ๋ฆฌ ์ƒ๋ช…์ฃผ๊ธฐ(Memory Lifecycle)

๋ชจ๋“  ํ”„๋กœ๊ทธ๋ž˜๋ฐ ์–ธ์–ด์—์„œ ๋ฉ”๋ชจ๋ฆฌ ๊ด€๋ฆฌ๋Š” ๊ธฐ๋ณธ์ ์œผ๋กœ ์„ธ ๊ฐ€์ง€ ๋‹จ๊ณ„๋ฅผ ๊ฑฐ์ณ. ์ด๊ฑด ๋งˆ์น˜ ์šฐ๋ฆฌ๊ฐ€ ๋ฌผ๊ฑด์„ ์‚ฌ์šฉํ•˜๋Š” ๊ณผ์ •๊ณผ ๋น„์Šทํ•ด! ๐Ÿ”„

1. ํ• ๋‹น(Allocation)
ํ•„์š”ํ•œ ๋ฉ”๋ชจ๋ฆฌ๋ฅผ ์šด์˜์ฒด์ œ๋กœ๋ถ€ํ„ฐ ๋ฐ›์•„์˜ค๋Š” ๋‹จ๊ณ„์•ผ. JavaScript์—์„œ๋Š” ๋ณ€์ˆ˜๋ฅผ ์„ ์–ธํ•˜๊ฑฐ๋‚˜ ๊ฐ์ฒด๋ฅผ ์ƒ์„ฑํ•  ๋•Œ ์ž๋™์œผ๋กœ ์ผ์–ด๋‚˜์ง€.

2. ์‚ฌ์šฉ(Use)
ํ• ๋‹น๋œ ๋ฉ”๋ชจ๋ฆฌ์— ๋ฐ์ดํ„ฐ๋ฅผ ์ฝ๊ณ  ์“ฐ๋Š” ๋‹จ๊ณ„์•ผ. ์šฐ๋ฆฌ๊ฐ€ ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•˜๋ฉด์„œ ๊ฐ€์žฅ ๋งŽ์ด ํ•˜๋Š” ์ž‘์—…์ด์ง€.

3. ํ•ด์ œ(Release)
๋” ์ด์ƒ ํ•„์š” ์—†๋Š” ๋ฉ”๋ชจ๋ฆฌ๋ฅผ ์‹œ์Šคํ…œ์— ๋ฐ˜ํ™˜ํ•˜๋Š” ๋‹จ๊ณ„์•ผ. ์—ฌ๊ธฐ์„œ ๊ฐ€๋น„์ง€ ์ปฌ๋ ‰์…˜์ด ์ค‘์š”ํ•œ ์—ญํ• ์„ ํ•ด! ๐ŸŽฏ

๐Ÿ’ก ์‹ค์ „ ์˜ˆ์ œ: JavaScript ๋ฉ”๋ชจ๋ฆฌ ํ• ๋‹น

// 1. ์›์‹œ ํƒ€์ž… ํ• ๋‹น
let number = 42;  // ์ˆซ์ž๋ฅผ ์œ„ํ•œ ๋ฉ”๋ชจ๋ฆฌ ํ• ๋‹น
let text = "Hello";  // ๋ฌธ์ž์—ด์„ ์œ„ํ•œ ๋ฉ”๋ชจ๋ฆฌ ํ• ๋‹น

// 2. ๊ฐ์ฒด ํ• ๋‹น
let user = {
    name: "๊น€๊ฐœ๋ฐœ",
    age: 28,
    skills: ["JavaScript", "React", "Node.js"]
};  // ๊ฐ์ฒด์™€ ๋ฐฐ์—ด์„ ์œ„ํ•œ ๋ฉ”๋ชจ๋ฆฌ ํ• ๋‹น

// 3. ํ•จ์ˆ˜ ํ• ๋‹น
function calculateTotal(items) {
    return items.reduce((sum, item) => sum + item.price, 0);
}  // ํ•จ์ˆ˜ ๊ฐ์ฒด๋ฅผ ์œ„ํ•œ ๋ฉ”๋ชจ๋ฆฌ ํ• ๋‹น

์Šคํƒ(Stack) vs ํž™(Heap) ๋ฉ”๋ชจ๋ฆฌ

JavaScript ์—”์ง„์€ ๋‘ ๊ฐ€์ง€ ์ฃผ์š” ๋ฉ”๋ชจ๋ฆฌ ์˜์—ญ์„ ์‚ฌ์šฉํ•ด. ์ด ๋‘˜์˜ ์ฐจ์ด๋ฅผ ์ดํ•ดํ•˜๋Š” ๊ฒŒ ์ •๋ง ์ค‘์š”ํ•ด! ๐Ÿ“š

์Šคํƒ ๋ฉ”๋ชจ๋ฆฌ(Stack Memory)
โ€ข ์›์‹œ ํƒ€์ž… ๋ฐ์ดํ„ฐ์™€ ํ•จ์ˆ˜ ํ˜ธ์ถœ ์ •๋ณด๋ฅผ ์ €์žฅ
โ€ข LIFO(Last In First Out) ๊ตฌ์กฐ๋กœ ๋™์ž‘
โ€ข ํฌ๊ธฐ๊ฐ€ ์ œํ•œ์ ์ด์ง€๋งŒ ์ ‘๊ทผ ์†๋„๊ฐ€ ๋งค์šฐ ๋น ๋ฆ„
โ€ข ์ž๋™์œผ๋กœ ๊ด€๋ฆฌ๋˜๋ฉฐ ํ•จ์ˆ˜๊ฐ€ ์ข…๋ฃŒ๋˜๋ฉด ์ž๋™ ํ•ด์ œ
โ€ข ์˜ˆ: ์ˆซ์ž, ๋ถˆ๋ฆฐ, undefined, null, ์‹ฌ๋ณผ

ํž™ ๋ฉ”๋ชจ๋ฆฌ(Heap Memory)
โ€ข ์ฐธ์กฐ ํƒ€์ž… ๋ฐ์ดํ„ฐ๋ฅผ ์ €์žฅ
โ€ข ๋™์ ์œผ๋กœ ํฌ๊ธฐ๊ฐ€ ๋ณ€ํ•  ์ˆ˜ ์žˆ์Œ
โ€ข ์Šคํƒ๋ณด๋‹ค ๋А๋ฆฌ์ง€๋งŒ ํ›จ์”ฌ ํฐ ๋ฐ์ดํ„ฐ๋ฅผ ์ €์žฅ ๊ฐ€๋Šฅ
โ€ข ๊ฐ€๋น„์ง€ ์ปฌ๋ ‰ํ„ฐ๊ฐ€ ๊ด€๋ฆฌ
โ€ข ์˜ˆ: ๊ฐ์ฒด, ๋ฐฐ์—ด, ํ•จ์ˆ˜

๐ŸŽ“ ๊ฐœ๋ฐœ์ž ํŒ ์Šคํƒ ์˜ค๋ฒ„ํ”Œ๋กœ์šฐ(Stack Overflow)๋Š” ์žฌ๊ท€ ํ•จ์ˆ˜๊ฐ€ ๋„ˆ๋ฌด ๊นŠ๊ฒŒ ํ˜ธ์ถœ๋  ๋•Œ ๋ฐœ์ƒํ•ด. ๋ฐ˜๋ฉด ํž™ ๋ฉ”๋ชจ๋ฆฌ ๋ถ€์กฑ์€ ๋„ˆ๋ฌด ๋งŽ์€ ๊ฐ์ฒด๋ฅผ ์ƒ์„ฑํ•  ๋•Œ ๋ฐœ์ƒํ•˜์ง€. ๊ฐ๊ฐ์˜ ํŠน์„ฑ์„ ์ดํ•ดํ•˜๊ณ  ์ ์ ˆํžˆ ์‚ฌ์šฉํ•˜๋Š” ๊ฒŒ ์ค‘์š”ํ•ด!

๐Ÿ—‘๏ธ ๊ฐ€๋น„์ง€ ์ปฌ๋ ‰์…˜(Garbage Collection) ์™„์ „ ์ •๋ณต

๊ฐ€๋น„์ง€ ์ปฌ๋ ‰์…˜์ด๋ž€?

๊ฐ€๋น„์ง€ ์ปฌ๋ ‰์…˜(GC)์€ ๋” ์ด์ƒ ์‚ฌ์šฉ๋˜์ง€ ์•Š๋Š” ๋ฉ”๋ชจ๋ฆฌ๋ฅผ ์ž๋™์œผ๋กœ ์ฐพ์•„์„œ ํ•ด์ œํ•˜๋Š” ๋ฉ”์ปค๋‹ˆ์ฆ˜์ด์•ผ. ๋งˆ์น˜ ์ง‘์•ˆ์˜ ์“ฐ๋ ˆ๊ธฐ๋ฅผ ์ž๋™์œผ๋กœ ์น˜์›Œ์ฃผ๋Š” ๋กœ๋ด‡ ์ฒญ์†Œ๊ธฐ ๊ฐ™์€ ๊ฑฐ์ง€! ๐Ÿค–

C๋‚˜ C++ ๊ฐ™์€ ์–ธ์–ด์—์„œ๋Š” ๊ฐœ๋ฐœ์ž๊ฐ€ ์ง์ ‘ malloc()๊ณผ free()๋ฅผ ์‚ฌ์šฉํ•ด์„œ ๋ฉ”๋ชจ๋ฆฌ๋ฅผ ๊ด€๋ฆฌํ•ด์•ผ ํ•ด. ํ•˜์ง€๋งŒ JavaScript, Python, Java ๊ฐ™์€ ํ˜„๋Œ€ ์–ธ์–ด๋“ค์€ ๊ฐ€๋น„์ง€ ์ปฌ๋ ‰ํ„ฐ๊ฐ€ ์ด ์ผ์„ ์ž๋™์œผ๋กœ ์ฒ˜๋ฆฌํ•ด์ค˜.

๊ทธ๋ ‡๋‹ค๊ณ  ํ•ด์„œ ๊ฐœ๋ฐœ์ž๊ฐ€ ๋ฉ”๋ชจ๋ฆฌ ๊ด€๋ฆฌ์— ์‹ ๊ฒฝ ์“ฐ์ง€ ์•Š์•„๋„ ๋œ๋‹ค๋Š” ๋œป์€ ์•„๋‹ˆ์•ผ! ๊ฐ€๋น„์ง€ ์ปฌ๋ ‰ํ„ฐ๋„ ์™„๋ฒฝํ•˜์ง€ ์•Š๊ฑฐ๋“ . ๐Ÿ˜…

์ฃผ์š” ๊ฐ€๋น„์ง€ ์ปฌ๋ ‰์…˜ ์•Œ๊ณ ๋ฆฌ์ฆ˜

1. ์ฐธ์กฐ ์นด์šดํŒ…(Reference Counting)

๊ฐ€์žฅ ๋‹จ์ˆœํ•œ GC ์•Œ๊ณ ๋ฆฌ์ฆ˜์ด์•ผ. ๊ฐ ๊ฐ์ฒด๊ฐ€ ๋ช‡ ๋ฒˆ ์ฐธ์กฐ๋˜๋Š”์ง€ ์„ธ๋Š” ๋ฐฉ์‹์ด์ง€.

์ž‘๋™ ์›๋ฆฌ:
โ€ข ๊ฐ์ฒด๊ฐ€ ์ƒ์„ฑ๋˜๋ฉด ์ฐธ์กฐ ์นด์šดํŠธ = 1
โ€ข ๋‹ค๋ฅธ ๋ณ€์ˆ˜๊ฐ€ ์ฐธ์กฐํ•˜๋ฉด ์นด์šดํŠธ ์ฆ๊ฐ€
โ€ข ์ฐธ์กฐ๊ฐ€ ์ œ๊ฑฐ๋˜๋ฉด ์นด์šดํŠธ ๊ฐ์†Œ
โ€ข ์นด์šดํŠธ๊ฐ€ 0์ด ๋˜๋ฉด ๋ฉ”๋ชจ๋ฆฌ ํ•ด์ œ

๋ฌธ์ œ์ : ์ˆœํ™˜ ์ฐธ์กฐ(Circular Reference)
function createCircularReference() {
    let obj1 = {};
    let obj2 = {};
    
    obj1.ref = obj2;  // obj1์ด obj2๋ฅผ ์ฐธ์กฐ
    obj2.ref = obj1;  // obj2๊ฐ€ obj1์„ ์ฐธ์กฐ
    
    // ํ•จ์ˆ˜๊ฐ€ ๋๋‚˜๋„ ์„œ๋กœ๋ฅผ ์ฐธ์กฐํ•˜๊ณ  ์žˆ์–ด์„œ
    // ์ฐธ์กฐ ์นด์šดํŠธ๊ฐ€ 0์ด ๋˜์ง€ ์•Š์Œ!
    // = ๋ฉ”๋ชจ๋ฆฌ ๋ˆ„์ˆ˜ ๋ฐœ์ƒ ๐Ÿ’ฅ
}
์ด๋Ÿฐ ์ด์œ ๋กœ ํ˜„๋Œ€ JavaScript ์—”์ง„์€ ์ฐธ์กฐ ์นด์šดํŒ…์„ ์ฃผ์š” ์•Œ๊ณ ๋ฆฌ์ฆ˜์œผ๋กœ ์‚ฌ์šฉํ•˜์ง€ ์•Š์•„.

2. ๋งˆํฌ ์•ค ์Šค์œ•(Mark-and-Sweep)

ํ˜„์žฌ ๋Œ€๋ถ€๋ถ„์˜ JavaScript ์—”์ง„(V8, SpiderMonkey ๋“ฑ)์ด ์‚ฌ์šฉํ•˜๋Š” ์•Œ๊ณ ๋ฆฌ์ฆ˜์ด์•ผ! ๐ŸŽฏ

์ž‘๋™ ์›๋ฆฌ:

Phase 1: ๋งˆํฌ(Mark) ๋‹จ๊ณ„
1. ๋ฃจํŠธ(Root)์—์„œ ์‹œ์ž‘ (์ „์—ญ ๊ฐ์ฒด, ํ˜„์žฌ ์‹คํ–‰ ์ค‘์ธ ํ•จ์ˆ˜์˜ ์ง€์—ญ ๋ณ€์ˆ˜ ๋“ฑ)
2. ๋ฃจํŠธ์—์„œ ๋„๋‹ฌ ๊ฐ€๋Šฅํ•œ ๋ชจ๋“  ๊ฐ์ฒด๋ฅผ "๋งˆํฌ"
3. ๋งˆํฌ๋œ ๊ฐ์ฒด์—์„œ ์ฐธ์กฐํ•˜๋Š” ๊ฐ์ฒด๋“ค๋„ ์žฌ๊ท€์ ์œผ๋กœ ๋งˆํฌ

Phase 2: ์Šค์œ•(Sweep) ๋‹จ๊ณ„
1. ํž™ ๋ฉ”๋ชจ๋ฆฌ๋ฅผ ์ฒ˜์Œ๋ถ€ํ„ฐ ๋๊นŒ์ง€ ์ˆœํšŒ
2. ๋งˆํฌ๋˜์ง€ ์•Š์€ ๊ฐ์ฒด๋“ค์„ ๋ฉ”๋ชจ๋ฆฌ์—์„œ ์ œ๊ฑฐ
3. ๋งˆํฌ๋ฅผ ๋ชจ๋‘ ์ œ๊ฑฐํ•˜์—ฌ ๋‹ค์Œ GC ์‚ฌ์ดํด ์ค€๋น„

// ๋„๋‹ฌ ๊ฐ€๋Šฅํ•œ ๊ฐ์ฒด ์˜ˆ์ œ
let globalUser = {
    name: "๊น€๊ฐœ๋ฐœ",
    profile: {
        age: 28,
        email: "dev@example.com"
    }
};

// globalUser๋Š” ๋ฃจํŠธ์—์„œ ๋„๋‹ฌ ๊ฐ€๋Šฅ
// profile ๊ฐ์ฒด๋„ globalUser๋ฅผ ํ†ตํ•ด ๋„๋‹ฌ ๊ฐ€๋Šฅ
// ๋”ฐ๋ผ์„œ ๋‘˜ ๋‹ค GC ๋Œ€์ƒ์ด ์•„๋‹˜ โœ…

function temporaryFunction() {
    let tempData = {
        value: "์ž„์‹œ ๋ฐ์ดํ„ฐ"
    };
    // tempData๋Š” ํ•จ์ˆ˜ ์‹คํ–‰ ์ค‘์—๋งŒ ๋„๋‹ฌ ๊ฐ€๋Šฅ
    // ํ•จ์ˆ˜ ์ข…๋ฃŒ ํ›„ ๋„๋‹ฌ ๋ถˆ๊ฐ€๋Šฅ = GC ๋Œ€์ƒ โœ…
}

temporaryFunction();
์žฅ์ :
โ€ข ์ˆœํ™˜ ์ฐธ์กฐ ๋ฌธ์ œ ํ•ด๊ฒฐ
โ€ข ์‹ค์ œ๋กœ ์‚ฌ์šฉ๋˜์ง€ ์•Š๋Š” ๋ฉ”๋ชจ๋ฆฌ๋ฅผ ์ •ํ™•ํžˆ ์ฐพ์•„๋ƒ„

๋‹จ์ :
โ€ข GC ์‹คํ–‰ ์ค‘ ํ”„๋กœ๊ทธ๋žจ์ด ์ผ์‹œ ์ •์ง€๋จ (Stop-the-World)
โ€ข ํฐ ํž™์—์„œ๋Š” ์‹คํ–‰ ์‹œ๊ฐ„์ด ์˜ค๋ž˜ ๊ฑธ๋ฆด ์ˆ˜ ์žˆ์Œ

3. ์„ธ๋Œ€๋ณ„ ๊ฐ€๋น„์ง€ ์ปฌ๋ ‰์…˜(Generational GC)

V8 ์—”์ง„์ด ์‚ฌ์šฉํ•˜๋Š” ์ตœ์ ํ™”๋œ ๋ฐฉ์‹์ด์•ผ. "๋Œ€๋ถ€๋ถ„์˜ ๊ฐ์ฒด๋Š” ์งง์€ ์‹œ๊ฐ„๋งŒ ์‚ด์•„์žˆ๋‹ค"๋Š” ์•ฝํ•œ ์„ธ๋Œ€ ๊ฐ€์„ค(Weak Generational Hypothesis)์— ๊ธฐ๋ฐ˜ํ•ด. ๐Ÿ“Š

๋ฉ”๋ชจ๋ฆฌ ๊ตฌ์กฐ:

Young Generation (์‹ ์ƒ ์˜์—ญ)
โ€ข ์ƒˆ๋กœ ์ƒ์„ฑ๋œ ๊ฐ์ฒด๋“ค์ด ์ €์žฅ๋จ
โ€ข ํฌ๊ธฐ๊ฐ€ ์ž‘์Œ (1~8MB)
โ€ข Minor GC๊ฐ€ ์ž์ฃผ ์‹คํ–‰๋จ (๋น ๋ฆ„)
โ€ข Scavenge ์•Œ๊ณ ๋ฆฌ์ฆ˜ ์‚ฌ์šฉ

Old Generation (๋…ธ๋…„ ์˜์—ญ)
โ€ข Young Generation์—์„œ ์‚ด์•„๋‚จ์€ ๊ฐ์ฒด๋“ค์ด ์ด๋™
โ€ข ํฌ๊ธฐ๊ฐ€ ํผ
โ€ข Major GC๊ฐ€ ๊ฐ€๋” ์‹คํ–‰๋จ (๋А๋ฆผ)
โ€ข Mark-Sweep-Compact ์•Œ๊ณ ๋ฆฌ์ฆ˜ ์‚ฌ์šฉ

// Young Generation ์˜ˆ์ œ
function processData() {
    // ์ด๋Ÿฐ ์ž„์‹œ ๊ฐ์ฒด๋“ค์€ ๋Œ€๋ถ€๋ถ„ Young Generation์—์„œ ์ •๋ฆฌ๋จ
    let temp1 = { data: new Array(1000) };
    let temp2 = { data: new Array(1000) };
    let result = temp1.data.concat(temp2.data);
    return result;
}

// Old Generation ์˜ˆ์ œ
const cache = new Map();  // ์˜ค๋ž˜ ์‚ด์•„๋‚จ๋Š” ๊ฐ์ฒด

function addToCache(key, value) {
    cache.set(key, value);
    // cache๋Š” ๊ณ„์† ์ฐธ์กฐ๋˜๋ฏ€๋กœ Old Generation์œผ๋กœ ์ด๋™
}
Scavenge ์•Œ๊ณ ๋ฆฌ์ฆ˜ (Young Generation):
1. Young Generation์„ From-Space์™€ To-Space๋กœ ๋ถ„ํ• 
2. ์ƒˆ ๊ฐ์ฒด๋Š” From-Space์— ํ• ๋‹น
3. GC ์‹คํ–‰ ์‹œ ์‚ด์•„์žˆ๋Š” ๊ฐ์ฒด๋งŒ To-Space๋กœ ๋ณต์‚ฌ
4. From-Space ์ „์ฒด๋ฅผ ๋น„์›€
5. From-Space์™€ To-Space๋ฅผ ๊ต์ฒด

์ด ๋ฐฉ์‹์€ ๋งค์šฐ ๋น ๋ฅด์ง€๋งŒ ๋ฉ”๋ชจ๋ฆฌ๋ฅผ 2๋ฐฐ๋กœ ์‚ฌ์šฉํ•œ๋‹ค๋Š” ๋‹จ์ ์ด ์žˆ์–ด. ํ•˜์ง€๋งŒ Young Generation์ด ์ž‘๊ธฐ ๋•Œ๋ฌธ์— ํฐ ๋ฌธ์ œ๊ฐ€ ๋˜์ง€ ์•Š์•„!

V8 ์—”์ง„์˜ ๊ฐ€๋น„์ง€ ์ปฌ๋ ‰์…˜ ์ตœ์ ํ™”

Chrome๊ณผ Node.js์—์„œ ์‚ฌ์šฉํ•˜๋Š” V8 ์—”์ง„์€ ์ •๋ง ๋˜‘๋˜‘ํ•œ GC ์ตœ์ ํ™” ๊ธฐ๋ฒ•๋“ค์„ ์‚ฌ์šฉํ•ด. ๐Ÿš€

1. ์ฆ๋ถ„ ๋งˆํ‚น(Incremental Marking)
ํ•œ ๋ฒˆ์— ๋ชจ๋“  ๋งˆํ‚น์„ ํ•˜์ง€ ์•Š๊ณ , ์—ฌ๋Ÿฌ ๋ฒˆ์— ๋‚˜๋ˆ ์„œ ์กฐ๊ธˆ์”ฉ ๋งˆํ‚นํ•ด. ์ด๋ ‡๊ฒŒ ํ•˜๋ฉด Stop-the-World ์‹œ๊ฐ„์ด ์ค„์–ด๋“ค์–ด!

2. ๋™์‹œ ๋งˆํ‚น(Concurrent Marking)
๋ฉ”์ธ ์Šค๋ ˆ๋“œ๊ฐ€ JavaScript๋ฅผ ์‹คํ–‰ํ•˜๋Š” ๋™์•ˆ, ๋ณ„๋„์˜ ์Šค๋ ˆ๋“œ์—์„œ ๋งˆํ‚น ์ž‘์—…์„ ์ˆ˜ํ–‰ํ•ด. ๋ฉ€ํ‹ฐ์ฝ”์–ด CPU๋ฅผ ํ™œ์šฉํ•˜๋Š” ๊ฑฐ์ง€!

3. ์ง€์—ฐ ์Šค์œ•(Lazy Sweeping)
์Šค์œ• ์ž‘์—…์„ ํ•œ ๋ฒˆ์— ๋‹ค ํ•˜์ง€ ์•Š๊ณ , ํ•„์š”ํ•  ๋•Œ๋งˆ๋‹ค ์กฐ๊ธˆ์”ฉ ์ˆ˜ํ–‰ํ•ด. ๋ฉ”๋ชจ๋ฆฌ๊ฐ€ ํ•„์š”ํ•  ๋•Œ๋งŒ ์ •๋ฆฌํ•˜๋Š” ๊ฑฐ์•ผ.

4. ๋ณ‘๋ ฌ ์Šค์บ๋นˆ์ง€(Parallel Scavenge)
Young Generation์˜ Scavenge๋ฅผ ์—ฌ๋Ÿฌ ์Šค๋ ˆ๋“œ์—์„œ ๋™์‹œ์— ์ˆ˜ํ–‰ํ•ด์„œ ์†๋„๋ฅผ ๋†’์—ฌ!

โš ๏ธ ์ฃผ์˜์‚ฌํ•ญ ๊ฐ€๋น„์ง€ ์ปฌ๋ ‰์…˜์€ ์ž๋™์œผ๋กœ ์‹คํ–‰๋˜์ง€๋งŒ, ์‹คํ–‰ ์‹œ์ ์„ ๊ฐœ๋ฐœ์ž๊ฐ€ ์™„์ „ํžˆ ์ œ์–ดํ•  ์ˆ˜๋Š” ์—†์–ด. ๋”ฐ๋ผ์„œ ๋ฉ”๋ชจ๋ฆฌ ๋ˆ„์ˆ˜๋ฅผ ๋ฐฉ์ง€ํ•˜๋Š” ์ฝ”๋”ฉ ์Šต๊ด€์ด ์ •๋ง ์ค‘์š”ํ•ด!

๐Ÿ’ฃ ๋ฉ”๋ชจ๋ฆฌ ๋ˆ„์ˆ˜์˜ ์ฃผ์š” ์›์ธ๊ณผ ํ•ด๊ฒฐ์ฑ…

1. ์˜๋„ํ•˜์ง€ ์•Š์€ ์ „์—ญ ๋ณ€์ˆ˜

๋ฌธ์ œ ์ฝ”๋“œ:
function createUser() {
    // var, let, const ์—†์ด ๋ณ€์ˆ˜ ์„ ์–ธ
    user = {
        name: "๊น€๊ฐœ๋ฐœ",
        data: new Array(10000)
    };
    // user๊ฐ€ ์ „์—ญ ๋ณ€์ˆ˜๊ฐ€ ๋˜์–ด ๊ณ„์† ๋ฉ”๋ชจ๋ฆฌ์— ๋‚จ์Œ! ๐Ÿ’ฅ
}
ํ•ด๊ฒฐ์ฑ…:
function createUser() {
    // ํ•ญ์ƒ ๋ณ€์ˆ˜ ์„ ์–ธ ํ‚ค์›Œ๋“œ ์‚ฌ์šฉ
    const user = {
        name: "๊น€๊ฐœ๋ฐœ",
        data: new Array(10000)
    };
    return user;
}

// ๋˜๋Š” strict mode ์‚ฌ์šฉ
'use strict';
function createUser() {
    user = { name: "๊น€๊ฐœ๋ฐœ" };  // ReferenceError ๋ฐœ์ƒ!
}

2. ์žŠํ˜€์ง„ ํƒ€์ด๋จธ์™€ ์ฝœ๋ฐฑ

๋ฌธ์ œ ์ฝ”๋“œ:
function startMonitoring() {
    const hugeData = new Array(100000).fill('data');
    
    setInterval(() => {
        // hugeData๋ฅผ ์ฐธ์กฐํ•˜๋Š” ํด๋กœ์ €
        console.log(hugeData.length);
    }, 1000);
    
    // ํƒ€์ด๋จธ๋ฅผ ์ •๋ฆฌํ•˜์ง€ ์•Š์œผ๋ฉด hugeData๊ฐ€ ๊ณ„์† ๋ฉ”๋ชจ๋ฆฌ์— ๋‚จ์Œ!
}
ํ•ด๊ฒฐ์ฑ…:
function startMonitoring() {
    const hugeData = new Array(100000).fill('data');
    
    const timerId = setInterval(() => {
        console.log(hugeData.length);
    }, 1000);
    
    // ์ •๋ฆฌ ํ•จ์ˆ˜ ๋ฐ˜ํ™˜
    return () => {
        clearInterval(timerId);
        console.log('ํƒ€์ด๋จธ ์ •๋ฆฌ ์™„๋ฃŒ!');
    };
}

// ์‚ฌ์šฉ
const cleanup = startMonitoring();
// ๋‚˜์ค‘์— ์ •๋ฆฌ
cleanup();

3. DOM ์ฐธ์กฐ ์œ ์ง€

๋ฌธ์ œ ์ฝ”๋“œ:
const elements = [];

function addElement() {
    const div = document.createElement('div');
    div.innerHTML = 'ํฐ ๋ฐ์ดํ„ฐ'.repeat(10000);
    document.body.appendChild(div);
    
    // DOM ์š”์†Œ๋ฅผ ๋ฐฐ์—ด์— ์ €์žฅ
    elements.push(div);
}

function removeElement() {
    const div = elements[0];
    document.body.removeChild(div);
    
    // DOM์—์„œ๋Š” ์ œ๊ฑฐ๋˜์—ˆ์ง€๋งŒ elements ๋ฐฐ์—ด์— ์—ฌ์ „ํžˆ ์ฐธ์กฐ๊ฐ€ ๋‚จ์•„์žˆ์Œ!
    // ๋ฉ”๋ชจ๋ฆฌ ๋ˆ„์ˆ˜ ๋ฐœ์ƒ! ๐Ÿ’ฅ
}
ํ•ด๊ฒฐ์ฑ…:
const elements = [];

function addElement() {
    const div = document.createElement('div');
    div.innerHTML = 'ํฐ ๋ฐ์ดํ„ฐ'.repeat(10000);
    document.body.appendChild(div);
    elements.push(div);
}

function removeElement() {
    const div = elements.shift();  // ๋ฐฐ์—ด์—์„œ๋„ ์ œ๊ฑฐ
    if (div && div.parentNode) {
        div.parentNode.removeChild(div);
    }
}

// ๋˜๋Š” WeakMap ์‚ฌ์šฉ
const elementData = new WeakMap();

function addElementWithWeakMap() {
    const div = document.createElement('div');
    document.body.appendChild(div);
    
    // WeakMap์€ ํ‚ค๊ฐ€ GC๋˜๋ฉด ์ž๋™์œผ๋กœ ํ•ญ๋ชฉ๋„ ์ œ๊ฑฐ๋จ
    elementData.set(div, { data: 'ํฐ ๋ฐ์ดํ„ฐ'.repeat(10000) });
}

4. ํด๋กœ์ €์˜ ์ž˜๋ชป๋œ ์‚ฌ์šฉ

๋ฌธ์ œ ์ฝ”๋“œ:
function createClosure() {
    const largeData = new Array(1000000).fill('data');
    const smallData = 'small';
    
    // ์ด ํ•จ์ˆ˜๋Š” smallData๋งŒ ์‚ฌ์šฉํ•˜์ง€๋งŒ
    // ํด๋กœ์ € ํŠน์„ฑ์ƒ largeData๋„ ํ•จ๊ป˜ ์บก์ฒ˜๋จ!
    return function() {
        return smallData;
    };
}

const closures = [];
for (let i = 0; i < 100; i++) {
    closures.push(createClosure());
    // 100MB * 100 = 10GB ๋ฉ”๋ชจ๋ฆฌ ์‚ฌ์šฉ! ๐Ÿ’ฅ
}
ํ•ด๊ฒฐ์ฑ…:
function createClosure() {
    const largeData = new Array(1000000).fill('data');
    const smallData = 'small';
    
    // largeData ์ฒ˜๋ฆฌ ํ›„ ๋ช…์‹œ์ ์œผ๋กœ ์ฐธ์กฐ ์ œ๊ฑฐ
    processLargeData(largeData);
    
    // ํ•„์š”ํ•œ ๋ฐ์ดํ„ฐ๋งŒ ์บก์ฒ˜
    return function() {
        return smallData;
    };
}

// ๋˜๋Š” IIFE ํŒจํ„ด ์‚ฌ์šฉ
function createOptimizedClosure() {
    const largeData = new Array(1000000).fill('data');
    
    // ์ฆ‰์‹œ ์‹คํ–‰ํ•˜์—ฌ ํ•„์š”ํ•œ ๊ฐ’๋งŒ ์ถ”์ถœ
    const result = (function(data) {
        // largeData ์ฒ˜๋ฆฌ
        return data.length;
    })(largeData);
    
    // largeData๋Š” ์—ฌ๊ธฐ์„œ GC ๋Œ€์ƒ์ด ๋จ
    return function() {
        return result;
    };
}

5. ์ด๋ฒคํŠธ ๋ฆฌ์Šค๋„ˆ ๋ฏธ์ œ๊ฑฐ

๋ฌธ์ œ ์ฝ”๋“œ:
class DataManager {
    constructor() {
        this.data = new Array(100000).fill('data');
        
        // ์ด๋ฒคํŠธ ๋ฆฌ์Šค๋„ˆ ๋“ฑ๋ก
        window.addEventListener('resize', this.handleResize.bind(this));
        
        // bind()๋Š” ์ƒˆ๋กœ์šด ํ•จ์ˆ˜๋ฅผ ์ƒ์„ฑํ•˜๋ฏ€๋กœ
        // ๋‚˜์ค‘์— ์ œ๊ฑฐํ•  ์ˆ˜ ์—†์Œ!
    }
    
    handleResize() {
        console.log(this.data.length);
    }
}

// ์ธ์Šคํ„ด์Šค๋ฅผ ์ œ๊ฑฐํ•ด๋„ ์ด๋ฒคํŠธ ๋ฆฌ์Šค๋„ˆ๊ฐ€ ๋‚จ์•„์žˆ์–ด์„œ
// data๊ฐ€ ๋ฉ”๋ชจ๋ฆฌ์—์„œ ํ•ด์ œ๋˜์ง€ ์•Š์Œ!
let manager = new DataManager();
manager = null;  // ๋ฉ”๋ชจ๋ฆฌ ๋ˆ„์ˆ˜ ๋ฐœ์ƒ! ๐Ÿ’ฅ
ํ•ด๊ฒฐ์ฑ…:
class DataManager {
    constructor() {
        this.data = new Array(100000).fill('data');
        
        // ๋ฐ”์ธ๋”ฉ๋œ ํ•จ์ˆ˜๋ฅผ ์ €์žฅ
        this.boundHandleResize = this.handleResize.bind(this);
        window.addEventListener('resize', this.boundHandleResize);
    }
    
    handleResize() {
        console.log(this.data.length);
    }
    
    destroy() {
        // ์ด๋ฒคํŠธ ๋ฆฌ์Šค๋„ˆ ์ œ๊ฑฐ
        window.removeEventListener('resize', this.boundHandleResize);
        this.data = null;
    }
}

// ์‚ฌ์šฉ
let manager = new DataManager();
// ์ •๋ฆฌ
manager.destroy();
manager = null;

// ๋˜๋Š” ํ™”์‚ดํ‘œ ํ•จ์ˆ˜ ์‚ฌ์šฉ (React ์Šคํƒ€์ผ)
class ModernDataManager {
    constructor() {
        this.data = new Array(100000).fill('data');
        window.addEventListener('resize', this.handleResize);
    }
    
    // ํ™”์‚ดํ‘œ ํ•จ์ˆ˜๋Š” this๋ฅผ ์ž๋™์œผ๋กœ ๋ฐ”์ธ๋”ฉ
    handleResize = () => {
        console.log(this.data.length);
    }
    
    destroy() {
        window.removeEventListener('resize', this.handleResize);
        this.data = null;
    }
}

๐Ÿ”ง ์‹ค์ „ ๋ฉ”๋ชจ๋ฆฌ ์ตœ์ ํ™” ๊ธฐ๋ฒ•

1. ๊ฐ์ฒด ํ’€๋ง(Object Pooling)

์ž์ฃผ ์ƒ์„ฑ๋˜๊ณ  ์‚ญ์ œ๋˜๋Š” ๊ฐ์ฒด๊ฐ€ ์žˆ๋‹ค๋ฉด, ๊ฐ์ฒด ํ’€์„ ๋งŒ๋“ค์–ด์„œ ์žฌ์‚ฌ์šฉํ•˜๋Š” ๊ฒŒ ํšจ์œจ์ ์ด์•ผ! ๐ŸŽฑ

class ObjectPool {
    constructor(createFn, resetFn, initialSize = 10) {
        this.createFn = createFn;
        this.resetFn = resetFn;
        this.pool = [];
        
        // ์ดˆ๊ธฐ ๊ฐ์ฒด ์ƒ์„ฑ
        for (let i = 0; i < initialSize; i++) {
            this.pool.push(this.createFn());
        }
    }
    
    acquire() {
        // ํ’€์— ๊ฐ์ฒด๊ฐ€ ์žˆ์œผ๋ฉด ์žฌ์‚ฌ์šฉ
        if (this.pool.length > 0) {
            return this.pool.pop();
        }
        // ์—†์œผ๋ฉด ์ƒˆ๋กœ ์ƒ์„ฑ
        return this.createFn();
    }
    
    release(obj) {
        // ๊ฐ์ฒด ์ดˆ๊ธฐํ™” ํ›„ ํ’€์— ๋ฐ˜ํ™˜
        this.resetFn(obj);
        this.pool.push(obj);
    }
}

// ์‚ฌ์šฉ ์˜ˆ์ œ: ํŒŒํ‹ฐํด ์‹œ์Šคํ…œ
class Particle {
    constructor() {
        this.x = 0;
        this.y = 0;
        this.vx = 0;
        this.vy = 0;
        this.life = 1.0;
    }
    
    update(dt) {
        this.x += this.vx * dt;
        this.y += this.vy * dt;
        this.life -= dt;
    }
}

const particlePool = new ObjectPool(
    () => new Particle(),
    (particle) => {
        particle.x = 0;
        particle.y = 0;
        particle.vx = 0;
        particle.vy = 0;
        particle.life = 1.0;
    },
    100
);

// ํŒŒํ‹ฐํด ์ƒ์„ฑ
function createParticle(x, y) {
    const particle = particlePool.acquire();
    particle.x = x;
    particle.y = y;
    particle.vx = Math.random() * 2 - 1;
    particle.vy = Math.random() * 2 - 1;
    return particle;
}

// ํŒŒํ‹ฐํด ์ œ๊ฑฐ
function removeParticle(particle) {
    particlePool.release(particle);
}

// ๋งค ํ”„๋ ˆ์ž„๋งˆ๋‹ค ์ˆ˜๋ฐฑ ๊ฐœ์˜ ํŒŒํ‹ฐํด์„ ์ƒ์„ฑ/์‚ญ์ œํ•ด๋„
// ์‹ค์ œ๋กœ๋Š” ๊ฐ์ฒด๋ฅผ ์žฌ์‚ฌ์šฉํ•˜๋ฏ€๋กœ GC ๋ถ€๋‹ด์ด ์ ์Œ! โœ…

2. ์ง€์—ฐ ๋กœ๋”ฉ(Lazy Loading)

ํ•„์š”ํ•  ๋•Œ๋งŒ ๋ฐ์ดํ„ฐ๋ฅผ ๋กœ๋“œํ•˜๋ฉด ์ดˆ๊ธฐ ๋ฉ”๋ชจ๋ฆฌ ์‚ฌ์šฉ๋Ÿ‰์„ ํฌ๊ฒŒ ์ค„์ผ ์ˆ˜ ์žˆ์–ด! ๐Ÿ“ฆ

class ImageGallery {
    constructor(imageUrls) {
        this.imageUrls = imageUrls;
        this.loadedImages = new Map();
    }
    
    // ์ด๋ฏธ์ง€๋ฅผ ์‹ค์ œ๋กœ ํ•„์š”ํ•  ๋•Œ๋งŒ ๋กœ๋“œ
    async getImage(index) {
        // ์ด๋ฏธ ๋กœ๋“œ๋œ ์ด๋ฏธ์ง€๋Š” ์บ์‹œ์—์„œ ๋ฐ˜ํ™˜
        if (this.loadedImages.has(index)) {
            return this.loadedImages.get(index);
        }
        
        // ์ƒˆ๋กœ์šด ์ด๋ฏธ์ง€ ๋กœ๋“œ
        const url = this.imageUrls[index];
        const image = await this.loadImage(url);
        
        // ์บ์‹œ์— ์ €์žฅ (๋ฉ”๋ชจ๋ฆฌ ์ œํ•œ ๊ณ ๋ ค)
        if (this.loadedImages.size > 50) {
            // ๊ฐ€์žฅ ์˜ค๋ž˜๋œ ์ด๋ฏธ์ง€ ์ œ๊ฑฐ (LRU ์บ์‹œ)
            const firstKey = this.loadedImages.keys().next().value;
            this.loadedImages.delete(firstKey);
        }
        
        this.loadedImages.set(index, image);
        return image;
    }
    
    loadImage(url) {
        return new Promise((resolve, reject) => {
            const img = new Image();
            img.onload = () => resolve(img);
            img.onerror = reject;
            img.src = url;
        });
    }
}

// ์‚ฌ์šฉ
const gallery = new ImageGallery([
    'image1.jpg',
    'image2.jpg',
    // ... 1000๊ฐœ์˜ ์ด๋ฏธ์ง€
]);

// ์ฒ˜์Œ์—๋Š” ๋ฉ”๋ชจ๋ฆฌ๋ฅผ ๊ฑฐ์˜ ์‚ฌ์šฉํ•˜์ง€ ์•Š์Œ
// ์‚ฌ์šฉ์ž๊ฐ€ ์ด๋ฏธ์ง€๋ฅผ ๋ณผ ๋•Œ๋งŒ ๋กœ๋“œ๋จ
const image = await gallery.getImage(5);

3. WeakMap๊ณผ WeakSet ํ™œ์šฉ

WeakMap๊ณผ WeakSet์€ ํ‚ค๊ฐ€ ๊ฐ€๋น„์ง€ ์ปฌ๋ ‰์…˜๋˜๋ฉด ์ž๋™์œผ๋กœ ํ•ญ๋ชฉ๋„ ์ œ๊ฑฐ๋˜๋Š” ํŠน๋ณ„ํ•œ ์ž๋ฃŒ๊ตฌ์กฐ์•ผ! ๐Ÿ—บ๏ธ

// ์ผ๋ฐ˜ Map์˜ ๋ฌธ์ œ์ 
const userCache = new Map();

function cacheUser(user) {
    userCache.set(user.id, user);
}

let user = { id: 1, name: "๊น€๊ฐœ๋ฐœ", data: new Array(10000) };
cacheUser(user);

user = null;  // user ๋ณ€์ˆ˜๋Š” null์ด ๋˜์—ˆ์ง€๋งŒ
// userCache์— ์—ฌ์ „ํžˆ ์ฐธ์กฐ๊ฐ€ ๋‚จ์•„์žˆ์–ด์„œ ๋ฉ”๋ชจ๋ฆฌ ๋ˆ„์ˆ˜! ๐Ÿ’ฅ

// WeakMap ์‚ฌ์šฉ
const userCacheWeak = new WeakMap();

function cacheUserWeak(user) {
    // user ๊ฐ์ฒด๋ฅผ ํ‚ค๋กœ ์‚ฌ์šฉ
    userCacheWeak.set(user, {
        lastAccessed: Date.now(),
        metadata: { /* ... */ }
    });
}

let user2 = { id: 2, name: "๋ฐ•๊ฐœ๋ฐœ", data: new Array(10000) };
cacheUserWeak(user2);

user2 = null;  // user2๊ฐ€ GC๋˜๋ฉด WeakMap์˜ ํ•ญ๋ชฉ๋„ ์ž๋™ ์ œ๊ฑฐ! โœ…

// ์‹ค์ „ ์˜ˆ์ œ: DOM ์š”์†Œ์— ๋ฐ์ดํ„ฐ ์—ฐ๊ฒฐ
const elementData = new WeakMap();

function attachData(element, data) {
    elementData.set(element, data);
}

function getData(element) {
    return elementData.get(element);
}

// DOM ์š”์†Œ ์ƒ์„ฑ
const button = document.createElement('button');
attachData(button, {
    clickCount: 0,
    lastClicked: null,
    userData: { /* ... */ }
});

// ๋ฒ„ํŠผ์ด DOM์—์„œ ์ œ๊ฑฐ๋˜๊ณ  ์ฐธ์กฐ๊ฐ€ ์—†์–ด์ง€๋ฉด
// elementData์˜ ํ•ญ๋ชฉ๋„ ์ž๋™์œผ๋กœ ์ œ๊ฑฐ๋จ!
document.body.removeChild(button);

// WeakSet ์˜ˆ์ œ: ์ฒ˜๋ฆฌ๋œ ๊ฐ์ฒด ์ถ”์ 
const processedItems = new WeakSet();

function processItem(item) {
    if (processedItems.has(item)) {
        console.log('์ด๋ฏธ ์ฒ˜๋ฆฌ๋œ ํ•ญ๋ชฉ');
        return;
    }
    
    // ์ฒ˜๋ฆฌ ๋กœ์ง
    console.log('ํ•ญ๋ชฉ ์ฒ˜๋ฆฌ ์ค‘...');
    
    // ์ฒ˜๋ฆฌ ์™„๋ฃŒ ํ‘œ์‹œ
    processedItems.add(item);
}

const item1 = { id: 1, data: '...' };
processItem(item1);  // ์ฒ˜๋ฆฌ๋จ
processItem(item1);  // ์ด๋ฏธ ์ฒ˜๋ฆฌ๋œ ํ•ญ๋ชฉ

// item1์ด GC๋˜๋ฉด WeakSet์—์„œ๋„ ์ž๋™ ์ œ๊ฑฐ

4. ๋ฉ”๋ชจ๋ฆฌ ํšจ์œจ์ ์ธ ๋ฐ์ดํ„ฐ ๊ตฌ์กฐ

์ ์ ˆํ•œ ๋ฐ์ดํ„ฐ ๊ตฌ์กฐ๋ฅผ ์„ ํƒํ•˜๋Š” ๊ฒƒ๋งŒ์œผ๋กœ๋„ ๋ฉ”๋ชจ๋ฆฌ๋ฅผ ํฌ๊ฒŒ ์ ˆ์•ฝํ•  ์ˆ˜ ์žˆ์–ด! ๐Ÿ“Š

// โŒ ๋น„ํšจ์œจ์ : ๊ฐ์ฒด ๋ฐฐ์—ด
const users = [
    { id: 1, name: "๊น€๊ฐœ๋ฐœ", age: 28, email: "kim@example.com" },
    { id: 2, name: "์ด๊ฐœ๋ฐœ", age: 30, email: "lee@example.com" },
    // ... 10,000๋ช…์˜ ์‚ฌ์šฉ์ž
];

// ๊ฐ ๊ฐ์ฒด๋งˆ๋‹ค ์†์„ฑ ์ด๋ฆ„์ด ์ค‘๋ณต ์ €์žฅ๋จ
// ๋ฉ”๋ชจ๋ฆฌ ๋‚ญ๋น„๊ฐ€ ์‹ฌํ•จ!

// โœ… ํšจ์œจ์ : ์ปฌ๋Ÿผ ๊ธฐ๋ฐ˜ ์ €์žฅ
class UserStore {
    constructor() {
        this.ids = [];
        this.names = [];
        this.ages = [];
        this.emails = [];
    }
    
    addUser(id, name, age, email) {
        this.ids.push(id);
        this.names.push(name);
        this.ages.push(age);
        this.emails.push(email);
    }
    
    getUser(index) {
        return {
            id: this.ids[index],
            name: this.names[index],
            age: this.ages[index],
            email: this.emails[index]
        };
    }
    
    // ํŠน์ • ํ•„๋“œ๋งŒ ํ•„์š”ํ•  ๋•Œ ํšจ์œจ์ 
    getAllNames() {
        return this.names;
    }
}

// TypedArray ์‚ฌ์šฉ (์ˆซ์ž ๋ฐ์ดํ„ฐ์— ์ตœ์ )
// โŒ ์ผ๋ฐ˜ ๋ฐฐ์—ด
const positions = [1.5, 2.3, 3.7, 4.2, 5.1];  // ๊ฐ ์ˆซ์ž๊ฐ€ 64๋น„ํŠธ

// โœ… TypedArray
const positionsTyped = new Float32Array([1.5, 2.3, 3.7, 4.2, 5.1]);  // ๊ฐ ์ˆซ์ž๊ฐ€ 32๋น„ํŠธ
// ๋ฉ”๋ชจ๋ฆฌ ์‚ฌ์šฉ๋Ÿ‰ 50% ์ ˆ๊ฐ!

// 3D ๊ทธ๋ž˜ํ”ฝ์Šค ์˜ˆ์ œ
class Mesh {
    constructor(vertexCount) {
        // Float32Array ์‚ฌ์šฉ์œผ๋กœ ๋ฉ”๋ชจ๋ฆฌ ์ ˆ์•ฝ
        this.vertices = new Float32Array(vertexCount * 3);  // x, y, z
        this.normals = new Float32Array(vertexCount * 3);
        this.uvs = new Float32Array(vertexCount * 2);  // u, v
        
        // ์ธ๋ฑ์Šค๋Š” Uint16Array ์‚ฌ์šฉ
        this.indices = new Uint16Array(vertexCount);
    }
    
    setVertex(index, x, y, z) {
        const i = index * 3;
        this.vertices[i] = x;
        this.vertices[i + 1] = y;
        this.vertices[i + 2] = z;
    }
}

// ๋น„ํŠธ ํ”Œ๋ž˜๊ทธ ์‚ฌ์šฉ
// โŒ ์—ฌ๋Ÿฌ ๋ถˆ๋ฆฐ ์†์„ฑ
const user = {
    isActive: true,
    isAdmin: false,
    isPremium: true,
    isVerified: true,
    canPost: true,
    canComment: false
};

// โœ… ๋น„ํŠธ ํ”Œ๋ž˜๊ทธ
const UserFlags = {
    ACTIVE: 1 << 0,      // 0001
    ADMIN: 1 << 1,       // 0010
    PREMIUM: 1 << 2,     // 0100
    VERIFIED: 1 << 3,    // 1000
    CAN_POST: 1 << 4,    // 10000
    CAN_COMMENT: 1 << 5  // 100000
};

class User {
    constructor() {
        this.flags = 0;  // ๋‹จ ํ•˜๋‚˜์˜ ์ˆซ์ž๋กœ ๋ชจ๋“  ํ”Œ๋ž˜๊ทธ ๊ด€๋ฆฌ
    }
    
    setFlag(flag) {
        this.flags |= flag;
    }
    
    clearFlag(flag) {
        this.flags &= ~flag;
    }
    
    hasFlag(flag) {
        return (this.flags & flag) !== 0;
    }
}

const user2 = new User();
user2.setFlag(UserFlags.ACTIVE);
user2.setFlag(UserFlags.PREMIUM);
user2.setFlag(UserFlags.VERIFIED);

console.log(user2.hasFlag(UserFlags.ACTIVE));  // true
console.log(user2.hasFlag(UserFlags.ADMIN));   // false

5. ๋ฌธ์ž์—ด ์ตœ์ ํ™”

๋ฌธ์ž์—ด์€ ๋ถˆ๋ณ€(immutable)์ด๊ธฐ ๋•Œ๋ฌธ์— ์—ฐ๊ฒฐ ์ž‘์—…์ด ๋งŽ์œผ๋ฉด ๋ฉ”๋ชจ๋ฆฌ๋ฅผ ๋งŽ์ด ์‚ฌ์šฉํ•ด! ๐Ÿ’ฌ

// โŒ ๋น„ํšจ์œจ์ : ๋ฌธ์ž์—ด ์—ฐ๊ฒฐ
function buildHTML(items) {
    let html = '';
    for (let i = 0; i < items.length; i++) {
        html += '<div>';
        html += '<h3>' + items[i].title + '</h3>';
        html += '<p>' + items[i].content + '</p>';
        html += '</div>';
        // ๋งค๋ฒˆ ์ƒˆ๋กœ์šด ๋ฌธ์ž์—ด ๊ฐ์ฒด๊ฐ€ ์ƒ์„ฑ๋จ! ๐Ÿ’ฅ
    }
    return html;
}

// โœ… ํšจ์œจ์ : ๋ฐฐ์—ด ์‚ฌ์šฉ
function buildHTMLOptimized(items) {
    const parts = [];
    for (let i = 0; i < items.length; i++) {
        parts.push('<div>');
        parts.push('<h3>', items[i].title, '</h3>');
        parts.push('<p>', items[i].content, '</p>');
        parts.push('</div>');
    }
    return parts.join('');  // ํ•œ ๋ฒˆ๋งŒ ์—ฐ๊ฒฐ
}

// โœ… ๋” ์ข‹์€ ๋ฐฉ๋ฒ•: ํ…œํ”Œ๋ฆฟ ๋ฆฌํ„ฐ๋Ÿด
function buildHTMLModern(items) {
    return items.map(item => `
        <div>
            <h3>${item.title}</h3>
            <p>${item.content}</p>
        </div>
    `).join('');
}

// ๋ฌธ์ž์—ด ์ธํ„ฐ๋‹ ํ™œ์šฉ
// JavaScript ์—”์ง„์€ ๋™์ผํ•œ ๋ฌธ์ž์—ด ๋ฆฌํ„ฐ๋Ÿด์„ ์žฌ์‚ฌ์šฉํ•จ
const str1 = "hello";
const str2 = "hello";
console.log(str1 === str2);  // true (๊ฐ™์€ ๋ฉ”๋ชจ๋ฆฌ ์ฃผ์†Œ)

// ํ•˜์ง€๋งŒ ๋™์ ์œผ๋กœ ์ƒ์„ฑ๋œ ๋ฌธ์ž์—ด์€ ๋‹ค๋ฆ„
const str3 = "hel" + "lo";
console.log(str1 === str3);  // true (์—”์ง„์ด ์ตœ์ ํ™”)

const str4 = "hel";
const str5 = str4 + "lo";
console.log(str1 === str5);  // false (๋‹ค๋ฅธ ๋ฉ”๋ชจ๋ฆฌ ์ฃผ์†Œ)

// ์ž์ฃผ ์‚ฌ์šฉ๋˜๋Š” ๋ฌธ์ž์—ด์€ ์ƒ์ˆ˜๋กœ ์ •์˜
const STRINGS = {
    ERROR_MESSAGE: "์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ–ˆ์Šต๋‹ˆ๋‹ค",
    SUCCESS_MESSAGE: "์„ฑ๊ณต์ ์œผ๋กœ ์ฒ˜๋ฆฌ๋˜์—ˆ์Šต๋‹ˆ๋‹ค",
    LOADING_MESSAGE: "๋กœ๋”ฉ ์ค‘..."
};

// ์ด๋ ‡๊ฒŒ ํ•˜๋ฉด ๊ฐ™์€ ๋ฌธ์ž์—ด์„ ์—ฌ๋Ÿฌ ๋ฒˆ ์ƒ์„ฑํ•˜์ง€ ์•Š์Œ

๐Ÿ” ๋ฉ”๋ชจ๋ฆฌ ํ”„๋กœํŒŒ์ผ๋ง๊ณผ ๋””๋ฒ„๊น…

Chrome DevTools ๋ฉ”๋ชจ๋ฆฌ ํ”„๋กœํŒŒ์ผ๋Ÿฌ

Chrome DevTools๋Š” ๋ฉ”๋ชจ๋ฆฌ ๋ฌธ์ œ๋ฅผ ์ฐพ๋Š” ์ตœ๊ณ ์˜ ๋„๊ตฌ์•ผ! ์žฌ๋Šฅ๋„ท์—์„œ ์›น ์ตœ์ ํ™” ์ „๋ฌธ๊ฐ€๋“ค๋„ ์ด ๋„๊ตฌ๋ฅผ ๊ฐ€์žฅ ๋งŽ์ด ์ถ”์ฒœํ•ด. ๐Ÿ› ๏ธ

1. Heap Snapshot (ํž™ ์Šค๋ƒ…์ƒท)

ํŠน์ • ์‹œ์ ์˜ ๋ฉ”๋ชจ๋ฆฌ ์ƒํƒœ๋ฅผ ์บก์ฒ˜ํ•ด์„œ ๋ถ„์„ํ•  ์ˆ˜ ์žˆ์–ด.

์‚ฌ์šฉ ๋ฐฉ๋ฒ•:
1. DevTools ์—ด๊ธฐ (F12)
2. Memory ํƒญ ์„ ํƒ
3. "Heap snapshot" ์„ ํƒ
4. "Take snapshot" ํด๋ฆญ

๋ถ„์„ ํŒ:
โ€ข Summary ๋ทฐ: ์ƒ์„ฑ์ž๋ณ„๋กœ ๊ฐ์ฒด ๊ทธ๋ฃนํ™”
โ€ข Comparison ๋ทฐ: ๋‘ ์Šค๋ƒ…์ƒท ๋น„๊ต๋กœ ๋ฉ”๋ชจ๋ฆฌ ์ฆ๊ฐ€ ํ™•์ธ
โ€ข Containment ๋ทฐ: ๊ฐ์ฒด์˜ ์ฐธ์กฐ ๊ตฌ์กฐ ํ™•์ธ
โ€ข Statistics ๋ทฐ: ๋ฉ”๋ชจ๋ฆฌ ์‚ฌ์šฉ๋Ÿ‰์„ ์ฐจํŠธ๋กœ ์‹œ๊ฐํ™”

// ๋ฉ”๋ชจ๋ฆฌ ๋ˆ„์ˆ˜ ํ…Œ์ŠคํŠธ ์ฝ”๋“œ
let leakyArray = [];

function createLeak() {
    // 1. ์ฒซ ๋ฒˆ์งธ ์Šค๋ƒ…์ƒท ์ฐ๊ธฐ
    
    for (let i = 0; i < 10000; i++) {
        leakyArray.push({
            data: new Array(1000).fill('leak'),
            timestamp: Date.now()
        });
    }
    
    // 2. ๋‘ ๋ฒˆ์งธ ์Šค๋ƒ…์ƒท ์ฐ๊ธฐ
    // 3. Comparison ๋ทฐ์—์„œ ์ฆ๊ฐ€ํ•œ ๊ฐ์ฒด ํ™•์ธ
}

// ์—ฌ๋Ÿฌ ๋ฒˆ ์‹คํ–‰
createLeak();
createLeak();
createLeak();
2. Allocation Timeline (ํ• ๋‹น ํƒ€์ž„๋ผ์ธ)

์‹œ๊ฐ„์— ๋”ฐ๋ฅธ ๋ฉ”๋ชจ๋ฆฌ ํ• ๋‹น์„ ์‹ค์‹œ๊ฐ„์œผ๋กœ ๊ธฐ๋กํ•ด.

์‚ฌ์šฉ ๋ฐฉ๋ฒ•:
1. Memory ํƒญ์—์„œ "Allocation instrumentation on timeline" ์„ ํƒ
2. "Start" ํด๋ฆญ
3. ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ์‚ฌ์šฉ
4. "Stop" ํด๋ฆญ

๋ถ„์„:
โ€ข ํŒŒ๋ž€์ƒ‰ ๋ง‰๋Œ€: ๋ฉ”๋ชจ๋ฆฌ ํ• ๋‹น
โ€ข ํšŒ์ƒ‰ ๋ง‰๋Œ€: ํ•ด์ œ๋œ ๋ฉ”๋ชจ๋ฆฌ
โ€ข ํŒŒ๋ž€์ƒ‰์ด ๊ณ„์† ์ฆ๊ฐ€ํ•˜๋ฉด ๋ฉ”๋ชจ๋ฆฌ ๋ˆ„์ˆ˜ ์˜์‹ฌ!

// ํƒ€์ž„๋ผ์ธ ํ…Œ์ŠคํŠธ ์ฝ”๋“œ
function simulateUserActivity() {
    setInterval(() => {
        // ์‚ฌ์šฉ์ž ์•ก์…˜ ์‹œ๋ฎฌ๋ ˆ์ด์…˜
        const data = generateData();
        processData(data);
        // data๊ฐ€ ์ œ๋Œ€๋กœ ํ•ด์ œ๋˜๋Š”์ง€ ํƒ€์ž„๋ผ์ธ์—์„œ ํ™•์ธ
    }, 100);
}

function generateData() {
    return new Array(1000).fill(Math.random());
}

function processData(data) {
    // ๋ฐ์ดํ„ฐ ์ฒ˜๋ฆฌ
    return data.reduce((sum, val) => sum + val, 0);
}
3. Allocation Sampling (ํ• ๋‹น ์ƒ˜ํ”Œ๋ง)

๋ฉ”๋ชจ๋ฆฌ ํ• ๋‹น์„ ์ƒ˜ํ”Œ๋งํ•ด์„œ ์–ด๋–ค ํ•จ์ˆ˜๊ฐ€ ๋ฉ”๋ชจ๋ฆฌ๋ฅผ ๋งŽ์ด ์‚ฌ์šฉํ•˜๋Š”์ง€ ํ™•์ธํ•ด.

์žฅ์ :
โ€ข ์˜ค๋ฒ„ํ—ค๋“œ๊ฐ€ ์ ์–ด์„œ ํ”„๋กœ๋•์…˜์—์„œ๋„ ์‚ฌ์šฉ ๊ฐ€๋Šฅ
โ€ข ํ•จ์ˆ˜๋ณ„ ๋ฉ”๋ชจ๋ฆฌ ํ• ๋‹น๋Ÿ‰ ํ™•์ธ

// ๋ฉ”๋ชจ๋ฆฌ ์ง‘์•ฝ์ ์ธ ํ•จ์ˆ˜ ์ฐพ๊ธฐ
function heavyFunction() {
    const largeArray = new Array(1000000);
    // ์ด ํ•จ์ˆ˜๊ฐ€ ์ƒ˜ํ”Œ๋ง์—์„œ ๋†’๊ฒŒ ๋‚˜ํƒ€๋‚  ๊ฒƒ
    return largeArray.map(x => x * 2);
}

function lightFunction() {
    const smallArray = [1, 2, 3];
    return smallArray.map(x => x * 2);
}

// ์ƒ˜ํ”Œ๋ง ์‹คํ–‰ ์ค‘ ์—ฌ๋Ÿฌ ๋ฒˆ ํ˜ธ์ถœ
for (let i = 0; i < 100; i++) {
    heavyFunction();
    lightFunction();
}

Performance Monitor

์‹ค์‹œ๊ฐ„์œผ๋กœ ๋ฉ”๋ชจ๋ฆฌ ์‚ฌ์šฉ๋Ÿ‰์„ ๋ชจ๋‹ˆํ„ฐ๋งํ•  ์ˆ˜ ์žˆ์–ด!

์‚ฌ์šฉ ๋ฐฉ๋ฒ•:
1. DevTools์—์„œ Cmd+Shift+P (Mac) ๋˜๋Š” Ctrl+Shift+P (Windows)
2. "Show Performance Monitor" ์ž…๋ ฅ
3. ์‹ค์‹œ๊ฐ„ ๋ฉ”ํŠธ๋ฆญ ํ™•์ธ

์ฃผ์š” ๋ฉ”ํŠธ๋ฆญ:
โ€ข JS heap size: JavaScript ํž™ ๋ฉ”๋ชจ๋ฆฌ ์‚ฌ์šฉ๋Ÿ‰
โ€ข DOM Nodes: DOM ๋…ธ๋“œ ๊ฐœ์ˆ˜
โ€ข JS event listeners: ์ด๋ฒคํŠธ ๋ฆฌ์Šค๋„ˆ ๊ฐœ์ˆ˜
โ€ข Documents: ๋ฌธ์„œ ๊ฐœ์ˆ˜
โ€ข Frames: ํ”„๋ ˆ์ž„ ๊ฐœ์ˆ˜

์ด ๊ฐ’๋“ค์ด ๊ณ„์† ์ฆ๊ฐ€ํ•˜๋ฉด ๋ฉ”๋ชจ๋ฆฌ ๋ˆ„์ˆ˜๊ฐ€ ์žˆ๋‹ค๋Š” ์‹ ํ˜ธ์•ผ! ๐Ÿšจ

์ฝ”๋“œ๋กœ ๋ฉ”๋ชจ๋ฆฌ ๋ชจ๋‹ˆํ„ฐ๋ง

// Performance API ์‚ฌ์šฉ
if (performance.memory) {
    console.log('JS Heap Size:', {
        used: (performance.memory.usedJSHeapSize / 1048576).toFixed(2) + ' MB',
        total: (performance.memory.totalJSHeapSize / 1048576).toFixed(2) + ' MB',
        limit: (performance.memory.jsHeapSizeLimit / 1048576).toFixed(2) + ' MB'
    });
}

// ๋ฉ”๋ชจ๋ฆฌ ๋ชจ๋‹ˆํ„ฐ๋ง ํด๋ž˜์Šค
class MemoryMonitor {
    constructor(interval = 1000) {
        this.interval = interval;
        this.measurements = [];
        this.isMonitoring = false;
    }
    
    start() {
        if (!performance.memory) {
            console.warn('Memory API not available');
            return;
        }
        
        this.isMonitoring = true;
        this.monitor();
    }
    
    stop() {
        this.isMonitoring = false;
        return this.getReport();
    }
    
    monitor() {
        if (!this.isMonitoring) return;
        
        const measurement = {
            timestamp: Date.now(),
            used: performance.memory.usedJSHeapSize,
            total: performance.memory.totalJSHeapSize
        };
        
        this.measurements.push(measurement);
        
        // ๋ฉ”๋ชจ๋ฆฌ ์ฆ๊ฐ€ ๊ฐ์ง€
        if (this.measurements.length > 10) {
            const recent = this.measurements.slice(-10);
            const trend = this.calculateTrend(recent);
            
            if (trend > 0.1) {  // 10% ์ด์ƒ ์ฆ๊ฐ€
                console.warn('โš ๏ธ Memory leak detected! Trend:', trend);
            }
        }
        
        setTimeout(() => this.monitor(), this.interval);
    }
    
    calculateTrend(measurements) {
        if (measurements.length < 2) return 0;
        
        const first = measurements[0].used;
        const last = measurements[measurements.length - 1].used;
        
        return (last - first) / first;
    }
    
    getReport() {
        if (this.measurements.length === 0) {
            return { message: 'No measurements' };
        }
        
        const used = this.measurements.map(m => m.used);
        const max = Math.max(...used);
        const min = Math.min(...used);
        const avg = used.reduce((a, b) => a + b) / used.length;
        
        return {
            measurements: this.measurements.length,
            max: (max / 1048576).toFixed(2) + ' MB',
            min: (min / 1048576).toFixed(2) + ' MB',
            avg: (avg / 1048576).toFixed(2) + ' MB',
            trend: this.calculateTrend(this.measurements)
        };
    }
}

// ์‚ฌ์šฉ ์˜ˆ์ œ
const monitor = new MemoryMonitor(1000);
monitor.start();

// ํ…Œ์ŠคํŠธ ์ฝ”๋“œ ์‹คํ–‰
function runTest() {
    const data = [];
    for (let i = 0; i < 1000; i++) {
        data.push(new Array(1000).fill(i));
    }
}

setInterval(runTest, 2000);

// 10์ดˆ ํ›„ ๋ฆฌํฌํŠธ
setTimeout(() => {
    const report = monitor.stop();
    console.log('Memory Report:', report);
}, 10000);

โšก ํ”„๋ ˆ์ž„์›Œํฌ๋ณ„ ๋ฉ”๋ชจ๋ฆฌ ์ตœ์ ํ™”

React ๋ฉ”๋ชจ๋ฆฌ ์ตœ์ ํ™”

1. useEffect ํด๋ฆฐ์—…

import { useEffect, useState } from 'react';

function UserProfile({ userId }) {
    const [user, setUser] = useState(null);
    
    useEffect(() => {
        let isMounted = true;
        const controller = new AbortController();
        
        // ๋ฐ์ดํ„ฐ ํŽ˜์นญ
        fetch(`/api/users/${userId}`, {
            signal: controller.signal
        })
        .then(res => res.json())
        .then(data => {
            if (isMounted) {
                setUser(data);
            }
        })
        .catch(err => {
            if (err.name !== 'AbortError') {
                console.error(err);
            }
        });
        
        // ํด๋ฆฐ์—… ํ•จ์ˆ˜ - ๋งค์šฐ ์ค‘์š”! โœ…
        return () => {
            isMounted = false;
            controller.abort();
        };
    }, [userId]);
    
    return <div>{user?.name}</div>;
}

// โŒ ์ž˜๋ชป๋œ ์˜ˆ: ํด๋ฆฐ์—… ์—†์Œ
function BadComponent() {
    useEffect(() => {
        const interval = setInterval(() => {
            console.log('Running...');
        }, 1000);
        
        // ํด๋ฆฐ์—…์ด ์—†์–ด์„œ ์ปดํฌ๋„ŒํŠธ๊ฐ€ ์–ธ๋งˆ์šดํŠธ๋˜์–ด๋„
        // ํƒ€์ด๋จธ๊ฐ€ ๊ณ„์† ์‹คํ–‰๋จ! ๐Ÿ’ฅ
    }, []);
}
2. React.memo์™€ useMemo

import React, { memo, useMemo } from 'react';

// ๋ถˆํ•„์š”ํ•œ ๋ฆฌ๋ Œ๋”๋ง ๋ฐฉ์ง€
const ExpensiveComponent = memo(({ data, onUpdate }) => {
    // ๋ณต์žกํ•œ ๊ณ„์‚ฐ
    const processedData = useMemo(() => {
        console.log('Processing data...');
        return data.map(item => ({
            ...item,
            computed: heavyComputation(item)
        }));
    }, [data]);  // data๊ฐ€ ๋ณ€๊ฒฝ๋  ๋•Œ๋งŒ ์žฌ๊ณ„์‚ฐ
    
    return (
        <div>
            {processedData.map(item => (
                <div key={item.id}>{item.computed}</div>
            ))}
        </div>
    );
}, (prevProps, nextProps) => {
    // ์ปค์Šคํ…€ ๋น„๊ต ํ•จ์ˆ˜
    return prevProps.data === nextProps.data &&
           prevProps.onUpdate === nextProps.onUpdate;
});

function heavyComputation(item) {
    // ๋ฌด๊ฑฐ์šด ๊ณ„์‚ฐ
    let result = 0;
    for (let i = 0; i < 1000000; i++) {
        result += item.value * i;
    }
    return result;
}
3. ๊ฐ€์ƒ ์Šคํฌ๋กค๋ง

import { FixedSizeList } from 'react-window';

function VirtualizedList({ items }) {
    // 10,000๊ฐœ์˜ ์•„์ดํ…œ์ด ์žˆ์–ด๋„
    // ํ™”๋ฉด์— ๋ณด์ด๋Š” ๊ฒƒ๋งŒ ๋ Œ๋”๋ง! โœ…
    
    const Row = ({ index, style }) => (
        <div style={style}>
            {items[index].name}
        </div>
    );
    
    return (
        <FixedSizeList
            height={600}
            itemCount={items.length}
            itemSize={50}
            width="100%"
        >
            {Row}
        </FixedSizeList>
    );
}

// โŒ ์ผ๋ฐ˜ ๋ Œ๋”๋ง: ๋ชจ๋“  ์•„์ดํ…œ์„ DOM์— ์ƒ์„ฑ
function NonVirtualizedList({ items }) {
    return (
        <div>
            {items.map(item => (
                <div key={item.id}>{item.name}</div>
            ))}
        </div>
    );
    // 10,000๊ฐœ ์•„์ดํ…œ = 10,000๊ฐœ DOM ๋…ธ๋“œ = ๋ฉ”๋ชจ๋ฆฌ ํญ๋ฐœ! ๐Ÿ’ฅ
}

Vue.js ๋ฉ”๋ชจ๋ฆฌ ์ตœ์ ํ™”

1. beforeUnmount ํ›… ํ™œ์šฉ

export default {
    data() {
        return {
            timer: null,
            eventHandler: null
        }
    },
    
    mounted() {
        // ํƒ€์ด๋จธ ์„ค์ •
        this.timer = setInterval(() => {
            this.updateData();
        }, 1000);
        
        // ์ด๋ฒคํŠธ ๋ฆฌ์Šค๋„ˆ ๋“ฑ๋ก
        this.eventHandler = this.handleResize.bind(this);
        window.addEventListener('resize', this.eventHandler);
    },
    
    beforeUnmount() {
        // ํด๋ฆฐ์—… - ๋งค์šฐ ์ค‘์š”! โœ…
        if (this.timer) {
            clearInterval(this.timer);
        }
        
        if (this.eventHandler) {
            window.removeEventListener('resize', this.eventHandler);
        }
    },
    
    methods: {
        updateData() {
            // ๋ฐ์ดํ„ฐ ์—…๋ฐ์ดํŠธ
        },
        
        handleResize() {
            // ๋ฆฌ์‚ฌ์ด์ฆˆ ์ฒ˜๋ฆฌ
        }
    }
}
2. v-once์™€ v-memo ๋””๋ ‰ํ‹ฐ๋ธŒ

<template>
    <!-- ํ•œ ๋ฒˆ๋งŒ ๋ Œ๋”๋งํ•˜๊ณ  ์—…๋ฐ์ดํŠธํ•˜์ง€ ์•Š์Œ -->
    <div v-once>
        <h1>{{ staticTitle }}</h1>
        <p>{{ staticContent }}</p>
    </div>
    
    <!-- ์˜์กด์„ฑ์ด ๋ณ€๊ฒฝ๋  ๋•Œ๋งŒ ์—…๋ฐ์ดํŠธ -->
    <div v-memo="[user.id, user.name]">
        <UserCard :user="user" />
    </div>
    
    <!-- ํฐ ๋ฆฌ์ŠคํŠธ ์ตœ์ ํ™” -->
    <div
        v-for="item in items"
        :key="item.id"
        v-memo="[item.id, item.selected]"
    >
        <ItemCard :item="item" />
    </div>
</template>

Angular ๋ฉ”๋ชจ๋ฆฌ ์ตœ์ ํ™”

1. OnDestroy ์ธํ„ฐํŽ˜์ด์Šค ๊ตฌํ˜„

import { Component, OnInit, OnDestroy } from '@angular/core';
import { Subscription } from 'rxjs';

@Component({
    selector: 'app-user-list',
    templateUrl: './user-list.component.html'
})
export class UserListComponent implements OnInit, OnDestroy {
    private subscriptions = new Subscription();
    users: User[] = [];
    
    constructor(private userService: UserService) {}
    
    ngOnInit() {
        // ๊ตฌ๋… ์ถ”๊ฐ€
        this.subscriptions.add(
            this.userService.getUsers().subscribe(users => {
                this.users = users;
            })
        );
        
        this.subscriptions.add(
            this.userService.userUpdates$.subscribe(update => {
                this.handleUpdate(update);
            })
        );
    }
    
    ngOnDestroy() {
        // ๋ชจ๋“  ๊ตฌ๋… ํ•ด์ œ - ๋งค์šฐ ์ค‘์š”! โœ…
        this.subscriptions.unsubscribe();
    }
    
    handleUpdate(update: any) {
        // ์—…๋ฐ์ดํŠธ ์ฒ˜๋ฆฌ
    }
}
2. TrackBy ํ•จ์ˆ˜ ์‚ฌ์šฉ

// Component
export class ItemListComponent {
    items: Item[] = [];
    
    // trackBy ํ•จ์ˆ˜๋กœ ๋ถˆํ•„์š”ํ•œ DOM ์žฌ์ƒ์„ฑ ๋ฐฉ์ง€
    trackByItemId(index: number, item: Item): number {
        return item.id;
    }
}

// Template
<div *ngFor="let item of items; trackBy: trackByItemId">
    {{ item.name }}
</div>

// trackBy ์—†์œผ๋ฉด ๋ฐฐ์—ด์ด ๋ณ€๊ฒฝ๋  ๋•Œ๋งˆ๋‹ค
// ๋ชจ๋“  DOM ์š”์†Œ๋ฅผ ์žฌ์ƒ์„ฑํ•จ! ๐Ÿ’ฅ

๐ŸŽฏ ์‹ค์ „ ํ”„๋กœ์ ํŠธ ์ตœ์ ํ™” ์‚ฌ๋ก€

์‚ฌ๋ก€ 1: ๋Œ€์šฉ๋Ÿ‰ ๋ฐ์ดํ„ฐ ํ…Œ์ด๋ธ”

๋ฌธ์ œ ์ƒํ™ฉ:
10๋งŒ ๊ฐœ์˜ ํ–‰์„ ๊ฐ€์ง„ ๋ฐ์ดํ„ฐ ํ…Œ์ด๋ธ”์„ ๋ Œ๋”๋งํ•ด์•ผ ํ•˜๋Š”๋ฐ, ํŽ˜์ด์ง€๊ฐ€ ๋А๋ ค์ง€๊ณ  ๋ธŒ๋ผ์šฐ์ €๊ฐ€ ๋ฉˆ์ถค.

ํ•ด๊ฒฐ ๋ฐฉ๋ฒ•:

class VirtualTable {
    constructor(container, data, rowHeight = 50) {
        this.container = container;
        this.data = data;
        this.rowHeight = rowHeight;
        this.visibleRows = Math.ceil(container.clientHeight / rowHeight);
        this.startIndex = 0;
        
        this.init();
    }
    
    init() {
        // ์Šคํฌ๋กค ์ปจํ…Œ์ด๋„ˆ ์ƒ์„ฑ
        this.scrollContainer = document.createElement('div');
        this.scrollContainer.style.height = 
            `${this.data.length * this.rowHeight}px`;
        this.scrollContainer.style.position = 'relative';
        
        // ๋ณด์ด๋Š” ์˜์—ญ ์ปจํ…Œ์ด๋„ˆ
        this.viewport = document.createElement('div');
        this.viewport.style.position = 'absolute';
        this.viewport.style.top = '0';
        this.viewport.style.width = '100%';
        
        this.scrollContainer.appendChild(this.viewport);
        this.container.appendChild(this.scrollContainer);
        
        // ์Šคํฌ๋กค ์ด๋ฒคํŠธ
        this.container.addEventListener('scroll', 
            this.handleScroll.bind(this));
        
        // ์ดˆ๊ธฐ ๋ Œ๋”๋ง
        this.render();
    }
    
    handleScroll() {
        const scrollTop = this.container.scrollTop;
        const newStartIndex = Math.floor(scrollTop / this.rowHeight);
        
        if (newStartIndex !== this.startIndex) {
            this.startIndex = newStartIndex;
            this.render();
        }
    }
    
    render() {
        // ๋ณด์ด๋Š” ํ–‰๋งŒ ๋ Œ๋”๋ง (+ ๋ฒ„ํผ)
        const buffer = 5;
        const start = Math.max(0, this.startIndex - buffer);
        const end = Math.min(
            this.data.length,
            this.startIndex + this.visibleRows + buffer
        );
        
        // ๊ธฐ์กด ๋‚ด์šฉ ์ œ๊ฑฐ
        this.viewport.innerHTML = '';
        
        // ์ƒˆ๋กœ์šด ํ–‰ ์ƒ์„ฑ
        for (let i = start; i < end; i++) {
            const row = this.createRow(this.data[i], i);
            this.viewport.appendChild(row);
        }
        
        // ์œ„์น˜ ์กฐ์ •
        this.viewport.style.transform = 
            `translateY(${start * this.rowHeight}px)`;
    }
    
    createRow(data, index) {
        const row = document.createElement('div');
        row.style.height = `${this.rowHeight}px`;
        row.style.display = 'flex';
        row.style.alignItems = 'center';
        row.style.borderBottom = '1px solid #eee';
        row.innerHTML = `
            <div style="flex: 1">${data.id}</div>
            <div style="flex: 2">${data.name}</div>
            <div style="flex: 2">${data.email}</div>
        `;
        return row;
    }
}

// ์‚ฌ์šฉ
const container = document.getElementById('table-container');
const data = generateLargeDataset(100000);
const table = new VirtualTable(container, data);

// ๊ฒฐ๊ณผ: 10๋งŒ ๊ฐœ ๋ฐ์ดํ„ฐ๋„ ๋ถ€๋“œ๋Ÿฝ๊ฒŒ ์Šคํฌ๋กค! โœ…

์‚ฌ๋ก€ 2: ์‹ค์‹œ๊ฐ„ ์ฑ„ํŒ… ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜

๋ฌธ์ œ ์ƒํ™ฉ:
์ฑ„ํŒ… ๋ฉ”์‹œ์ง€๊ฐ€ ๊ณ„์† ์Œ“์ด๋ฉด์„œ ๋ฉ”๋ชจ๋ฆฌ ์‚ฌ์šฉ๋Ÿ‰์ด ์ฆ๊ฐ€ํ•˜๊ณ  ์„ฑ๋Šฅ์ด ์ €ํ•˜๋จ.

ํ•ด๊ฒฐ ๋ฐฉ๋ฒ•:

class ChatManager {
    constructor(maxMessages = 1000) {
        this.maxMessages = maxMessages;
        this.messages = [];
        this.messageMap = new Map();
        this.listeners = new Set();
    }
    
    addMessage(message) {
        // ๋ฉ”์‹œ์ง€ ์ถ”๊ฐ€
        this.messages.push(message);
        this.messageMap.set(message.id, message);
        
        // ์˜ค๋ž˜๋œ ๋ฉ”์‹œ์ง€ ์ œ๊ฑฐ (๋ฉ”๋ชจ๋ฆฌ ๊ด€๋ฆฌ)
        if (this.messages.length > this.maxMessages) {
            const removed = this.messages.shift();
            this.messageMap.delete(removed.id);
            
            // DOM์—์„œ๋„ ์ œ๊ฑฐ
            this.removeMessageFromDOM(removed.id);
        }
        
        // ๋ฆฌ์Šค๋„ˆ๋“ค์—๊ฒŒ ์•Œ๋ฆผ
        this.notifyListeners(message);
    }
    
    removeMessageFromDOM(messageId) {
        const element = document.getElementById(`msg-${messageId}`);
        if (element) {
            element.remove();
        }
    }
    
    subscribe(listener) {
        this.listeners.add(listener);
        
        // ๊ตฌ๋… ํ•ด์ œ ํ•จ์ˆ˜ ๋ฐ˜ํ™˜
        return () => {
            this.listeners.delete(listener);
        };
    }
    
    notifyListeners(message) {
        this.listeners.forEach(listener => {
            try {
                listener(message);
            } catch (error) {
                console.error('Listener error:', error);
            }
        });
    }
    
    getRecentMessages(count = 50) {
        return this.messages.slice(-count);
    }
    
    clear() {
        this.messages = [];
        this.messageMap.clear();
        this.listeners.clear();
    }
}

// ๋ฉ”์‹œ์ง€ ๋ Œ๋”๋Ÿฌ (๊ฐ€์ƒ ์Šคํฌ๋กค๋ง ์ ์šฉ)
class ChatRenderer {
    constructor(container, chatManager) {
        this.container = container;
        this.chatManager = chatManager;
        this.unsubscribe = null;
        
        this.init();
    }
    
    init() {
        // ์ƒˆ ๋ฉ”์‹œ์ง€ ๊ตฌ๋…
        this.unsubscribe = this.chatManager.subscribe(
            this.handleNewMessage.bind(this)
        );
        
        // ์ดˆ๊ธฐ ๋ฉ”์‹œ์ง€ ๋ Œ๋”๋ง
        const messages = this.chatManager.getRecentMessages();
        messages.forEach(msg => this.renderMessage(msg));
    }
    
    handleNewMessage(message) {
        this.renderMessage(message);
        this.scrollToBottom();
    }
    
    renderMessage(message) {
        const element = document.createElement('div');
        element.id = `msg-${message.id}`;
        element.className = 'chat-message';
        element.innerHTML = `
            <div class="message-header">
                <span class="author">${message.author}</span>
                <span class="time">${this.formatTime(message.timestamp)}</span>
            </div>
            <div class="message-content">${this.escapeHtml(message.content)}</div>
        `;
        
        this.container.appendChild(element);
    }
    
    scrollToBottom() {
        this.container.scrollTop = this.container.scrollHeight;
    }
    
    formatTime(timestamp) {
        return new Date(timestamp).toLocaleTimeString();
    }
    
    escapeHtml(text) {
        const div = document.createElement('div');
        div.textContent = text;
        return div.innerHTML;
    }
    
    destroy() {
        // ํด๋ฆฐ์—…
        if (this.unsubscribe) {
            this.unsubscribe();
        }
        this.container.innerHTML = '';
    }
}

// ์‚ฌ์šฉ
const chatManager = new ChatManager(1000);
const container = document.getElementById('chat-container');
const renderer = new ChatRenderer(container, chatManager);

// WebSocket ์—ฐ๊ฒฐ
const ws = new WebSocket('ws://example.com/chat');

ws.onmessage = (event) => {
    const message = JSON.parse(event.data);
    chatManager.addMessage(message);
};

// ํŽ˜์ด์ง€ ์–ธ๋กœ๋“œ ์‹œ ์ •๋ฆฌ
window.addEventListener('beforeunload', () => {
    renderer.destroy();
    chatManager.clear();
    ws.close();
});

์‚ฌ๋ก€ 3: ์ด๋ฏธ์ง€ ๊ฐค๋Ÿฌ๋ฆฌ ์ตœ์ ํ™”

๋ฌธ์ œ ์ƒํ™ฉ:
์ˆ˜๋ฐฑ ๊ฐœ์˜ ๊ณ ํ•ด์ƒ๋„ ์ด๋ฏธ์ง€๋ฅผ ๋กœ๋“œํ•˜๋ฉด ๋ฉ”๋ชจ๋ฆฌ๊ฐ€ ๋ถ€์กฑํ•ด์ง.

ํ•ด๊ฒฐ ๋ฐฉ๋ฒ•:

class ImageGalleryOptimized {
    constructor(container, images, options = {}) {
        this.container = container;
        this.images = images;
        this.options = {
            thumbnailSize: 200,
            maxCachedImages: 20,
            lazyLoadThreshold: 500,
            ...options
        };
        
        this.imageCache = new Map();
        this.observer = null;
        
        this.init();
    }
    
    init() {
        // Intersection Observer ์„ค์ •
        this.observer = new IntersectionObserver(
            this.handleIntersection.bind(this),
            {
                rootMargin: `${this.options.lazyLoadThreshold}px`
            }
        );
        
        // ์ธ๋„ค์ผ ๋ Œ๋”๋ง
        this.renderThumbnails();
    }
    
    renderThumbnails() {
        this.images.forEach((image, index) => {
            const thumbnail = this.createThumbnail(image, index);
            this.container.appendChild(thumbnail);
            
            // ์ง€์—ฐ ๋กœ๋”ฉ์„ ์œ„ํ•ด ๊ด€์ฐฐ
            this.observer.observe(thumbnail);
        });
    }
    
    createThumbnail(image, index) {
        const div = document.createElement('div');
        div.className = 'thumbnail';
        div.dataset.index = index;
        div.dataset.src = image.url;
        
        // ํ”Œ๋ ˆ์ด์Šคํ™€๋”
        div.innerHTML = `
            <div class="placeholder" 
                 style="width: ${this.options.thumbnailSize}px; 
                        height: ${this.options.thumbnailSize}px;">
                <div class="loading-spinner"></div>
            </div>
        `;
        
        div.addEventListener('click', () => {
            this.showFullImage(image);
        });
        
        return div;
    }
    
    handleIntersection(entries) {
        entries.forEach(entry => {
            if (entry.isIntersecting) {
                const thumbnail = entry.target;
                const src = thumbnail.dataset.src;
                
                // ์ด๋ฏธ์ง€ ๋กœ๋“œ
                this.loadThumbnail(thumbnail, src);
                
                // ๊ด€์ฐฐ ์ค‘์ง€
                this.observer.unobserve(thumbnail);
            }
        });
    }
    
    async loadThumbnail(thumbnail, src) {
        try {
            // ์ธ๋„ค์ผ URL ์ƒ์„ฑ (์„œ๋ฒ„์—์„œ ๋ฆฌ์‚ฌ์ด์ง•๋œ ์ด๋ฏธ์ง€)
            const thumbnailUrl = this.getThumbnailUrl(src);
            
            const img = new Image();
            img.onload = () => {
                thumbnail.innerHTML = '';
                thumbnail.appendChild(img);
            };
            img.src = thumbnailUrl;
            
        } catch (error) {
            console.error('Failed to load thumbnail:', error);
            thumbnail.innerHTML = '<div class="error">๋กœ๋“œ ์‹คํŒจ</div>';
        }
    }
    
    getThumbnailUrl(originalUrl) {
        // ์ธ๋„ค์ผ URL ์ƒ์„ฑ (์˜ˆ: Cloudinary, imgix ๋“ฑ)
        return originalUrl.replace('/original/', '/thumbnail/');
    }
    
    async showFullImage(image) {
        // ์บ์‹œ ํ™•์ธ
        if (this.imageCache.has(image.url)) {
            this.displayImage(this.imageCache.get(image.url));
            return;
        }
        
        // ์บ์‹œ ํฌ๊ธฐ ๊ด€๋ฆฌ
        if (this.imageCache.size >= this.options.maxCachedImages) {
            // LRU: ๊ฐ€์žฅ ์˜ค๋ž˜๋œ ํ•ญ๋ชฉ ์ œ๊ฑฐ
            const firstKey = this.imageCache.keys().next().value;
            this.imageCache.delete(firstKey);
        }
        
        // ์ด๋ฏธ์ง€ ๋กœ๋“œ
        const img = await this.loadFullImage(image.url);
        this.imageCache.set(image.url, img);
        this.displayImage(img);
    }
    
    loadFullImage(url) {
        return new Promise((resolve, reject) => {
            const img = new Image();
            img.onload = () => resolve(img);
            img.onerror = reject;
            img.src = url;
        });
    }
    
    displayImage(img) {
        // ๋ชจ๋‹ฌ ๋˜๋Š” ๋ผ์ดํŠธ๋ฐ•์Šค์— ํ‘œ์‹œ
        const modal = document.createElement('div');
        modal.className = 'image-modal';
        modal.innerHTML = `
            <div class="modal-content">
                <span class="close">ร—</span>
            </div>
        `;
        
        modal.querySelector('.modal-content').appendChild(img.cloneNode());
        document.body.appendChild(modal);
        
        // ๋‹ซ๊ธฐ ์ด๋ฒคํŠธ
        modal.querySelector('.close').addEventListener('click', () => {
            modal.remove();
        });
    }
    
    destroy() {
        // ํด๋ฆฐ์—…
        if (this.observer) {
            this.observer.disconnect();
        }
        this.imageCache.clear();
        this.container.innerHTML = '';
    }
}

// ์‚ฌ์šฉ
const images = [
    { url: '/images/photo1.jpg', title: 'Photo 1' },
    { url: '/images/photo2.jpg', title: 'Photo 2' },
    // ... ์ˆ˜๋ฐฑ ๊ฐœ์˜ ์ด๋ฏธ์ง€
];

const container = document.getElementById('gallery');
const gallery = new ImageGalleryOptimized(container, images, {
    thumbnailSize: 200,
    maxCachedImages: 20,
    lazyLoadThreshold: 500
});

// ๊ฒฐ๊ณผ:
// โœ… ์ดˆ๊ธฐ ๋กœ๋”ฉ ์‹œ๊ฐ„ 90% ๊ฐ์†Œ
// โœ… ๋ฉ”๋ชจ๋ฆฌ ์‚ฌ์šฉ๋Ÿ‰ 70% ๊ฐ์†Œ
// โœ… ๋ถ€๋“œ๋Ÿฌ์šด ์Šคํฌ๋กค๋ง

๐Ÿ“‹ ๋ฉ”๋ชจ๋ฆฌ ์ตœ์ ํ™” ์ฒดํฌ๋ฆฌ์ŠคํŠธ

๐ŸŽฏ ๊ฐœ๋ฐœ ๋‹จ๊ณ„ โœ… ๋ณ€์ˆ˜ ์„ ์–ธ ์‹œ ํ•ญ์ƒ const/let ์‚ฌ์šฉ
โœ… ์ด๋ฒคํŠธ ๋ฆฌ์Šค๋„ˆ๋Š” ๋ฐ˜๋“œ์‹œ ์ œ๊ฑฐ
โœ… ํƒ€์ด๋จธ(setTimeout, setInterval)๋Š” ๋ฐ˜๋“œ์‹œ ์ •๋ฆฌ
โœ… DOM ์ฐธ์กฐ๋Š” ํ•„์š”ํ•œ ๋งŒํผ๋งŒ ์œ ์ง€
โœ… ํด๋กœ์ € ์‚ฌ์šฉ ์‹œ ๋ถˆํ•„์š”ํ•œ ๋ณ€์ˆ˜ ์บก์ฒ˜ ์ฃผ์˜
โœ… ํฐ ๋ฐ์ดํ„ฐ๋Š” WeakMap/WeakSet ๊ณ ๋ ค
โœ… ์ˆœํ™˜ ์ฐธ์กฐ ๋ฐฉ์ง€
โœ… ์ „์—ญ ๋ณ€์ˆ˜ ์ตœ์†Œํ™”

๐Ÿ” ํ…Œ์ŠคํŠธ ๋‹จ๊ณ„

โœ… Chrome DevTools๋กœ ๋ฉ”๋ชจ๋ฆฌ ํ”„๋กœํŒŒ์ผ๋ง
โœ… Heap Snapshot์œผ๋กœ ๋ฉ”๋ชจ๋ฆฌ ๋ˆ„์ˆ˜ ํ™•์ธ
โœ… Performance Monitor๋กœ ์‹ค์‹œ๊ฐ„ ๋ชจ๋‹ˆํ„ฐ๋ง
โœ… ์žฅ์‹œ๊ฐ„ ์‹คํ–‰ ํ…Œ์ŠคํŠธ (๋ฉ”๋ชจ๋ฆฌ ์ฆ๊ฐ€ ์ถ”์„ธ ํ™•์ธ)
โœ… ๋‹ค์–‘ํ•œ ๋ธŒ๋ผ์šฐ์ €์—์„œ ํ…Œ์ŠคํŠธ
โœ… ๋ชจ๋ฐ”์ผ ๊ธฐ๊ธฐ์—์„œ ํ…Œ์ŠคํŠธ

๐Ÿš€ ๋ฐฐํฌ ๋‹จ๊ณ„

โœ… ํ”„๋กœ๋•์…˜ ๋นŒ๋“œ ์ตœ์ ํ™”
โœ… ์ฝ”๋“œ ์Šคํ”Œ๋ฆฌํŒ… ์ ์šฉ
โœ… ์ด๋ฏธ์ง€ ์ตœ์ ํ™” (WebP, ์••์ถ•)
โœ… ๋ฒˆ๋“ค ํฌ๊ธฐ ๋ถ„์„
โœ… CDN ํ™œ์šฉ
โœ… ์บ์‹ฑ ์ „๋žต ์ˆ˜๋ฆฝ

๐Ÿ“Š ๋ชจ๋‹ˆํ„ฐ๋ง ๋‹จ๊ณ„

โœ… ์‹ค์‚ฌ์šฉ์ž ๋ฉ”ํŠธ๋ฆญ ์ˆ˜์ง‘
โœ… ์—๋Ÿฌ ๋กœ๊น… ์„ค์ •
โœ… ์„ฑ๋Šฅ ์ง€ํ‘œ ์ถ”์ 
โœ… ์ •๊ธฐ์ ์ธ ์„ฑ๋Šฅ ๊ฐ์‚ฌ

๐ŸŽ“ ๋งˆ๋ฌด๋ฆฌํ•˜๋ฉฐ

์ž, ์—ฌ๊ธฐ๊นŒ์ง€ ์ €์ˆ˜์ค€ ์›น ์ตœ์ ํ™”์™€ ๋ฉ”๋ชจ๋ฆฌ ๊ด€๋ฆฌ, ๊ทธ๋ฆฌ๊ณ  ๊ฐ€๋น„์ง€ ์ปฌ๋ ‰์…˜์— ๋Œ€ํ•ด ๊นŠ์ด ์žˆ๊ฒŒ ์•Œ์•„๋ดค์–ด! ์ •๋ง ๊ธด ์—ฌ์ •์ด์—ˆ์ง€? ๐Ÿ˜…

ํ•ต์‹ฌ์„ ์ •๋ฆฌํ•˜์ž๋ฉด:

1. ๋ฉ”๋ชจ๋ฆฌ ๊ด€๋ฆฌ๋Š” ์ž๋™์ด์ง€๋งŒ ์™„๋ฒฝํ•˜์ง€ ์•Š๋‹ค
๊ฐ€๋น„์ง€ ์ปฌ๋ ‰ํ„ฐ๊ฐ€ ์žˆ์–ด๋„ ๊ฐœ๋ฐœ์ž๊ฐ€ ๋ฉ”๋ชจ๋ฆฌ๋ฅผ ์˜์‹ํ•˜๋ฉฐ ์ฝ”๋”ฉํ•ด์•ผ ํ•ด. ํŠนํžˆ ์ด๋ฒคํŠธ ๋ฆฌ์Šค๋„ˆ, ํƒ€์ด๋จธ, DOM ์ฐธ์กฐ ๋“ฑ์€ ๋ฐ˜๋“œ์‹œ ์ •๋ฆฌํ•ด์•ผ ํ•˜์ง€!

2. ์ ์ ˆํ•œ ๋„๊ตฌ ์‚ฌ์šฉ์ด ์ค‘์š”ํ•˜๋‹ค
Chrome DevTools์˜ ๋ฉ”๋ชจ๋ฆฌ ํ”„๋กœํŒŒ์ผ๋Ÿฌ๋Š” ์ •๋ง ๊ฐ•๋ ฅํ•œ ๋„๊ตฌ์•ผ. ๋ฌธ์ œ๊ฐ€ ์ƒ๊ธฐ๊ธฐ ์ „์— ๋ฏธ๋ฆฌ๋ฏธ๋ฆฌ ํ™•์ธํ•˜๋Š” ์Šต๊ด€์„ ๋“ค์ด์ž!

3. ํ”„๋ ˆ์ž„์›Œํฌ๋ณ„ ์ตœ์ ํ™” ํŒจํ„ด์„ ์ตํžˆ์ž
React, Vue, Angular ๋“ฑ ๊ฐ ํ”„๋ ˆ์ž„์›Œํฌ๋งˆ๋‹ค ๊ถŒ์žฅํ•˜๋Š” ๋ฉ”๋ชจ๋ฆฌ ๊ด€๋ฆฌ ํŒจํ„ด์ด ์žˆ์–ด. ์ด๊ฑธ ์ž˜ ๋”ฐ๋ฅด๋ฉด ๋Œ€๋ถ€๋ถ„์˜ ๋ฌธ์ œ๋ฅผ ์˜ˆ๋ฐฉํ•  ์ˆ˜ ์žˆ์–ด!

4. ์„ฑ๋Šฅ์€ ์ง€์†์ ์ธ ๊ด€์‹ฌ์ด ํ•„์š”ํ•˜๋‹ค
ํ•œ ๋ฒˆ ์ตœ์ ํ™”ํ•˜๊ณ  ๋์ด ์•„๋‹ˆ์•ผ. ์ƒˆ๋กœ์šด ๊ธฐ๋Šฅ์ด ์ถ”๊ฐ€๋  ๋•Œ๋งˆ๋‹ค ์„ฑ๋Šฅ ์˜ํ–ฅ์„ ์ฒดํฌํ•˜๊ณ , ์ •๊ธฐ์ ์œผ๋กœ ํ”„๋กœํŒŒ์ผ๋ง์„ ํ•ด์•ผ ํ•ด!

๋ฉ”๋ชจ๋ฆฌ ์ตœ์ ํ™”๋Š” ์ฒ˜์Œ์—” ์–ด๋ ต๊ฒŒ ๋А๊ปด์งˆ ์ˆ˜ ์žˆ์ง€๋งŒ, ํ•œ ๋ฒˆ ์ต์ˆ™ํ•ด์ง€๋ฉด ์ž์—ฐ์Šค๋Ÿฝ๊ฒŒ ์ตœ์ ํ™”๋œ ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•˜๊ฒŒ ๋  ๊ฑฐ์•ผ. ๊ทธ๋ฆฌ๊ณ  ๊ทธ ๊ฒฐ๊ณผ๋กœ ์‚ฌ์šฉ์ž๋“ค์€ ํ›จ์”ฌ ๋” ๋น ๋ฅด๊ณ  ๋ถ€๋“œ๋Ÿฌ์šด ์›น ๊ฒฝํ—˜์„ ํ•˜๊ฒŒ ๋˜์ง€! ๐Ÿš€

ํ˜น์‹œ ๋” ๊นŠ์ด ์žˆ๋Š” ์›น ์ตœ์ ํ™” ๊ธฐ๋ฒ•์ด๋‚˜ ์‹ค์ „ ํ”„๋กœ์ ํŠธ ๊ฒฝํ—˜์ด ํ•„์š”ํ•˜๋‹ค๋ฉด, ์žฌ๋Šฅ๋„ท์—์„œ ์ „๋ฌธ๊ฐ€๋“ค์˜ ๋„์›€์„ ๋ฐ›์•„๋ณด๋Š” ๊ฒƒ๋„ ์ข‹์€ ๋ฐฉ๋ฒ•์ด์•ผ. ์‹ค๋ฌด ๊ฒฝํ—˜์ด ํ’๋ถ€ํ•œ ๊ฐœ๋ฐœ์ž๋“ค์ด ๋งŽ์ด ํ™œ๋™ํ•˜๊ณ  ์žˆ๊ฑฐ๋“ !

์ž, ์ด์ œ ์—ฌ๋Ÿฌ๋ถ„์˜ ํ”„๋กœ์ ํŠธ๋กœ ๋Œ์•„๊ฐ€์„œ ์˜ค๋Š˜ ๋ฐฐ์šด ๋‚ด์šฉ์„ ์ ์šฉํ•ด๋ณด์ž! ๋ฉ”๋ชจ๋ฆฌ ๋ˆ„์ˆ˜๋ฅผ ์ฐพ์•„๋‚ด๊ณ , ์„ฑ๋Šฅ์„ ๊ฐœ์„ ํ•˜๊ณ , ์‚ฌ์šฉ์ž๋“ค์—๊ฒŒ ์ตœ๊ณ ์˜ ๊ฒฝํ—˜์„ ์„ ์‚ฌํ•˜์ž! ๐Ÿ’ช

Happy Coding! ๐ŸŽ‰

๐ŸŒŸ ์ด ๊ธ€์ด ๋„์›€์ด ๋˜์—ˆ๋‹ค๋ฉด ๊ณต์œ ํ•ด์ฃผ์„ธ์š”!
๋” ๋‚˜์€ ์›น์„ ๋งŒ๋“œ๋Š” ๊ฐœ๋ฐœ์ž๋“ค์ด ๋งŽ์•„์งˆ์ˆ˜๋ก, ์ธํ„ฐ๋„ท์€ ๋” ๋น ๋ฅด๊ณ  ์พŒ์ ํ•ด์งˆ ๊ฑฐ์˜ˆ์š”! ๐Ÿ˜Š
๋Œ“๊ธ€ ์ž‘์„ฑ

์ด ๊ธ€์— ๋Œ€ํ•œ ์—ฌ๋Ÿฌ๋ถ„์˜ ์ƒ๊ฐ์„ ๋“ค๋ ค์ฃผ์„ธ์š”

๋Œ“๊ธ€ 0
๋Œ“๊ธ€์„ ๋ถˆ๋Ÿฌ์˜ค๋Š” ์ค‘์ž…๋‹ˆ๋‹ค.

์•„์ง ๋Œ“๊ธ€์ด ์—†์Šต๋‹ˆ๋‹ค.