Ver3.0 ๐ ์ ์์ค ์น ์ต์ ํ์ ๋ฉ๋ชจ๋ฆฌ ๊ด๋ฆฌ: ๊ฐ๋น์ง ์ปฌ๋ ์ ์๋ฒฝ ์ดํดํ๊ธฐ

๐ ์ ์์ค ์น ์ต์ ํ์ ๋ฉ๋ชจ๋ฆฌ ๊ด๋ฆฌ: ๊ฐ๋น์ง ์ปฌ๋ ์ ์๋ฒฝ ์ดํดํ๊ธฐ
์น ์ฑ๋ฅ์ ๊ทน๋ํํ๋ ๋ฉ๋ชจ๋ฆฌ ๊ด๋ฆฌ์ ๋ชจ๋ ๊ฒ
๐ก ์ ๋ฉ๋ชจ๋ฆฌ ๊ด๋ฆฌ๊ฐ ์ค์ํ ๊น?
์๋
! ์ค๋์ ์น ๊ฐ๋ฐ์์ ์ ๋ง ์ค์ํ์ง๋ง ๋ง์ ๊ฐ๋ฐ์๋ค์ด ๊ฐ๊ณผํ๋ ์ฃผ์ ์ ๋ํด ์ด์ผ๊ธฐํด๋ณผ๊ฒ. ๋ฐ๋ก ์ ์์ค ์น ์ต์ ํ์ ๋ฉ๋ชจ๋ฆฌ ๊ด๋ฆฌ์ผ. ๐ฏ
ํน์ ์ด๋ฐ ๊ฒฝํ ์์ด? ์ฒ์์ ๋น ๋ฅด๊ฒ ์๋ํ๋ ์น ์ ํ๋ฆฌ์ผ์ด์
์ด ์๊ฐ์ด ์ง๋๋ฉด์ ์ ์ ๋๋ ค์ง๊ณ , ๊ฒฐ๊ตญ ๋ธ๋ผ์ฐ์ ๊ฐ ๋จนํต์ด ๋๋ ์ํฉ ๋ง์ด์ผ. ์ด๊ฒ ๋ฐ๋ก ๋ฉ๋ชจ๋ฆฌ ๋์(Memory Leak)์ ์ ํ์ ์ธ ์ฆ์์ด์ผ. ๐ฐ
ํ๋ ์น ์ ํ๋ฆฌ์ผ์ด์
์ ์ ์ ๋ ๋ณต์กํด์ง๊ณ ์์ด. Single Page Application(SPA), ์ค์๊ฐ ๋ฐ์ดํฐ ์ฒ๋ฆฌ, ๋ณต์กํ ์ฌ์ฉ์ ์ธํฐ๋์
... ์ด ๋ชจ๋ ๊ฒ๋ค์ด ๋ฉ๋ชจ๋ฆฌ๋ฅผ ์๋นํ์ง. ๊ทธ๋์ ๋ฉ๋ชจ๋ฆฌ๋ฅผ ํจ์จ์ ์ผ๋ก ๊ด๋ฆฌํ๋ ๊ฒ์ด ์น ์ฑ๋ฅ ์ต์ ํ์ ํต์ฌ์ด ๋์์ด.
์ฌ๋ฅ๋ท ๊ฐ์ ํ๋ซํผ์์๋ ์น ์ต์ ํ ์ ๋ฌธ๊ฐ๋ค์ด ๋ง์ด ํ๋ํ๊ณ ์๋๋ฐ, ์ด๋ค์ด ๊ฐ์ฅ ๊ฐ์กฐํ๋ ๊ฒ ์ค ํ๋๊ฐ ๋ฐ๋ก ๋ฉ๋ชจ๋ฆฌ ๊ด๋ฆฌ์ผ. ์๋ํ๋ฉด ์ฌ์ฉ์ ๊ฒฝํ(UX)์ ์ง์ ์ ์ธ ์ํฅ์ ๋ฏธ์น๊ธฐ ๋๋ฌธ์ด์ง! ๐จ
๐ง ๋ฉ๋ชจ๋ฆฌ ๊ด๋ฆฌ์ ๊ธฐ๋ณธ ๊ฐ๋
๋ฉ๋ชจ๋ฆฌ ์๋ช ์ฃผ๊ธฐ(Memory Lifecycle)
๋ชจ๋ ํ๋ก๊ทธ๋๋ฐ ์ธ์ด์์ ๋ฉ๋ชจ๋ฆฌ ๊ด๋ฆฌ๋ ๊ธฐ๋ณธ์ ์ผ๋ก ์ธ ๊ฐ์ง ๋จ๊ณ๋ฅผ ๊ฑฐ์ณ. ์ด๊ฑด ๋ง์น ์ฐ๋ฆฌ๊ฐ ๋ฌผ๊ฑด์ ์ฌ์ฉํ๋ ๊ณผ์ ๊ณผ ๋น์ทํด! ๐
1. ํ ๋น(Allocation)
ํ์ํ ๋ฉ๋ชจ๋ฆฌ๋ฅผ ์ด์์ฒด์ ๋ก๋ถํฐ ๋ฐ์์ค๋ ๋จ๊ณ์ผ. JavaScript์์๋ ๋ณ์๋ฅผ ์ ์ธํ๊ฑฐ๋ ๊ฐ์ฒด๋ฅผ ์์ฑํ ๋ ์๋์ผ๋ก ์ผ์ด๋์ง.
2. ์ฌ์ฉ(Use)
ํ ๋น๋ ๋ฉ๋ชจ๋ฆฌ์ ๋ฐ์ดํฐ๋ฅผ ์ฝ๊ณ ์ฐ๋ ๋จ๊ณ์ผ. ์ฐ๋ฆฌ๊ฐ ์ฝ๋๋ฅผ ์์ฑํ๋ฉด์ ๊ฐ์ฅ ๋ง์ด ํ๋ ์์
์ด์ง.
3. ํด์ (Release)
๋ ์ด์ ํ์ ์๋ ๋ฉ๋ชจ๋ฆฌ๋ฅผ ์์คํ
์ ๋ฐํํ๋ ๋จ๊ณ์ผ. ์ฌ๊ธฐ์ ๊ฐ๋น์ง ์ปฌ๋ ์
์ด ์ค์ํ ์ญํ ์ ํด! ๐ฏ
// 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)
โข ์ฐธ์กฐ ํ์
๋ฐ์ดํฐ๋ฅผ ์ ์ฅ
โข ๋์ ์ผ๋ก ํฌ๊ธฐ๊ฐ ๋ณํ ์ ์์
โข ์คํ๋ณด๋ค ๋๋ฆฌ์ง๋ง ํจ์ฌ ํฐ ๋ฐ์ดํฐ๋ฅผ ์ ์ฅ ๊ฐ๋ฅ
โข ๊ฐ๋น์ง ์ปฌ๋ ํฐ๊ฐ ๊ด๋ฆฌ
โข ์: ๊ฐ์ฒด, ๋ฐฐ์ด, ํจ์
๐๏ธ ๊ฐ๋น์ง ์ปฌ๋ ์ (Garbage Collection) ์์ ์ ๋ณต
๊ฐ๋น์ง ์ปฌ๋ ์ ์ด๋?
๊ฐ๋น์ง ์ปฌ๋ ์
(GC)์ ๋ ์ด์ ์ฌ์ฉ๋์ง ์๋ ๋ฉ๋ชจ๋ฆฌ๋ฅผ ์๋์ผ๋ก ์ฐพ์์ ํด์ ํ๋ ๋ฉ์ปค๋์ฆ์ด์ผ. ๋ง์น ์ง์์ ์ฐ๋ ๊ธฐ๋ฅผ ์๋์ผ๋ก ์น์์ฃผ๋ ๋ก๋ด ์ฒญ์๊ธฐ ๊ฐ์ ๊ฑฐ์ง! ๐ค
C๋ C++ ๊ฐ์ ์ธ์ด์์๋ ๊ฐ๋ฐ์๊ฐ ์ง์ malloc()๊ณผ free()๋ฅผ ์ฌ์ฉํด์ ๋ฉ๋ชจ๋ฆฌ๋ฅผ ๊ด๋ฆฌํด์ผ ํด. ํ์ง๋ง JavaScript, Python, Java ๊ฐ์ ํ๋ ์ธ์ด๋ค์ ๊ฐ๋น์ง ์ปฌ๋ ํฐ๊ฐ ์ด ์ผ์ ์๋์ผ๋ก ์ฒ๋ฆฌํด์ค.
๊ทธ๋ ๋ค๊ณ ํด์ ๊ฐ๋ฐ์๊ฐ ๋ฉ๋ชจ๋ฆฌ ๊ด๋ฆฌ์ ์ ๊ฒฝ ์ฐ์ง ์์๋ ๋๋ค๋ ๋ป์ ์๋์ผ! ๊ฐ๋น์ง ์ปฌ๋ ํฐ๋ ์๋ฒฝํ์ง ์๊ฑฐ๋ . ๐
์ฃผ์ ๊ฐ๋น์ง ์ปฌ๋ ์ ์๊ณ ๋ฆฌ์ฆ
1. ์ฐธ์กฐ ์นด์ดํ (Reference Counting)
์๋ ์๋ฆฌ:
โข ๊ฐ์ฒด๊ฐ ์์ฑ๋๋ฉด ์ฐธ์กฐ ์นด์ดํธ = 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)
์๋ ์๋ฆฌ:
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)
๋ฉ๋ชจ๋ฆฌ ๊ตฌ์กฐ:
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. 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();
์๊ฐ์ ๋ฐ๋ฅธ ๋ฉ๋ชจ๋ฆฌ ํ ๋น์ ์ค์๊ฐ์ผ๋ก ๊ธฐ๋กํด.
์ฌ์ฉ ๋ฐฉ๋ฒ:
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);
}
๋ฉ๋ชจ๋ฆฌ ํ ๋น์ ์ํ๋งํด์ ์ด๋ค ํจ์๊ฐ ๋ฉ๋ชจ๋ฆฌ๋ฅผ ๋ง์ด ์ฌ์ฉํ๋์ง ํ์ธํด.
์ฅ์ :
โข ์ค๋ฒํค๋๊ฐ ์ ์ด์ ํ๋ก๋์ ์์๋ ์ฌ์ฉ ๊ฐ๋ฅ
โข ํจ์๋ณ ๋ฉ๋ชจ๋ฆฌ ํ ๋น๋ ํ์ธ
// ๋ฉ๋ชจ๋ฆฌ ์ง์ฝ์ ์ธ ํจ์ ์ฐพ๊ธฐ
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 ๋ฉ๋ชจ๋ฆฌ ์ต์ ํ
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์ useMemoimport 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 ๋ฉ๋ชจ๋ฆฌ ์ต์ ํ
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 ๋ฉ๋ชจ๋ฆฌ ์ต์ ํ
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% ๊ฐ์
// โ
๋ถ๋๋ฌ์ด ์คํฌ๋กค๋ง
๐ ๋ฉ๋ชจ๋ฆฌ ์ต์ ํ ์ฒดํฌ๋ฆฌ์คํธ
โ ์ด๋ฒคํธ ๋ฆฌ์ค๋๋ ๋ฐ๋์ ์ ๊ฑฐ
โ ํ์ด๋จธ(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
์ง์์ธ์ ์ฒ - ์ง์ ์ฌ์ฐ๊ถ ๋ณดํธ ๊ณ ์ง
์ง์ ์ฌ์ฐ๊ถ ๋ณดํธ ๊ณ ์ง
- ์ ์๊ถ ๋ฐ ์์ ๊ถ: ๋ณธ ์ปจํ ์ธ ๋ ์ฌ๋ฅ๋ท์ ๋ ์ AI ๊ธฐ์ ๋ก ์์ฑ๋์์ผ๋ฉฐ, ๋ํ๋ฏผ๊ตญ ์ ์๊ถ๋ฒ ๋ฐ ๊ตญ์ ์ ์๊ถ ํ์ฝ์ ์ํด ๋ณดํธ๋ฉ๋๋ค.
- AI ์์ฑ ์ปจํ ์ธ ์ ๋ฒ์ ์ง์: ๋ณธ AI ์์ฑ ์ปจํ ์ธ ๋ ์ฌ๋ฅ๋ท์ ์ง์ ์ฐฝ์๋ฌผ๋ก ์ธ์ ๋๋ฉฐ, ๊ด๋ จ ๋ฒ๊ท์ ๋ฐ๋ผ ์ ์๊ถ ๋ณดํธ๋ฅผ ๋ฐ์ต๋๋ค.
- ์ฌ์ฉ ์ ํ: ์ฌ๋ฅ๋ท์ ๋ช ์์ ์๋ฉด ๋์ ์์ด ๋ณธ ์ปจํ ์ธ ๋ฅผ ๋ณต์ , ์์ , ๋ฐฐํฌ, ๋๋ ์์ ์ ์ผ๋ก ํ์ฉํ๋ ํ์๋ ์๊ฒฉํ ๊ธ์ง๋ฉ๋๋ค.
- ๋ฐ์ดํฐ ์์ง ๊ธ์ง: ๋ณธ ์ปจํ ์ธ ์ ๋ํ ๋ฌด๋จ ์คํฌ๋ํ, ํฌ๋กค๋ง, ๋ฐ ์๋ํ๋ ๋ฐ์ดํฐ ์์ง์ ๋ฒ์ ์ ์ฌ์ ๋์์ด ๋ฉ๋๋ค.
- AI ํ์ต ์ ํ: ์ฌ๋ฅ๋ท์ AI ์์ฑ ์ปจํ ์ธ ๋ฅผ ํ AI ๋ชจ๋ธ ํ์ต์ ๋ฌด๋จ ์ฌ์ฉํ๋ ํ์๋ ๊ธ์ง๋๋ฉฐ, ์ด๋ ์ง์ ์ฌ์ฐ๊ถ ์นจํด๋ก ๊ฐ์ฃผ๋ฉ๋๋ค.
์์ง ๋๊ธ์ด ์์ต๋๋ค.