** ๐๋ฉ๋ชจ๋ฆฌ ๋์ ์ฌํ**
๐ ์ ์
์ฌ์ฉํ์ง ์๋๋ฐ๋ GC์ ์ํด ํด์ ๋์ง ์๊ณ ๊ณ์ ๋ฉ๋ชจ๋ฆฌ์ ๋จ์์๋ ๋ฐ์ดํฐ
๐ฅ ์ฃผ์ ์์ธ ์ฌํ ์ ๋ฆฌ
์์ธ | ์ค๋ช | ์ค์ ํจํด |
---|---|---|
์ ์ญ ๋ณ์ | ์ ์ญ ์ฐธ์กฐ๋ ์ฑ ์ข ๋ฃ ์ ๊น์ง ํด์ X | window.leak = ... |
ํด๋ก์ ๋ด๋ถ ์ฐธ์กฐ | ์ธ๋ถ ํจ์ ์ค์ฝํ ์ฐธ์กฐ ์ ์ง | ์ด๋ฒคํธ ํธ๋ค๋ฌ ๋ด๋ถ์์ ํด๋ก์ |
DOM ์์ ์ฐธ์กฐ | DOM ์ญ์ ํ๋๋ฐ ์ฐธ์กฐ ๋ณ์ ์ ์ง | let btn = document.getElementById() ํ DOM ์ญ์ ์ ํจ |
ํ์ด๋จธ | setInterval โ clearInterval ๋๋ฝ |
์ฃผ๊ธฐ์ ํจ์ โ ํ์ด์ง ์ด๋ํด๋ ๊ณ์ ๋์ |
์ด๋ฒคํธ ๋ฆฌ์ค๋ | removeEventListener ์ ํจ |
Single Page App์์ ์์ฃผ ๋ฐ์ |
โ ์ค๋ฌด ๋ฐฉ์ง ์์
// BAD
let leak = [];
document.querySelector('#btn').addEventListener('click', function handler() {
leak.push(new Array(100000).fill('*'));
});
// GOOD
const btn = document.querySelector('#btn');
function handler() {
leak.push(new Array(100000).fill('*'));
}
btn.addEventListener('click', handler);
btn.removeEventListener('click', handler);
๐ DevTools๋ก ๋ฉ๋ชจ๋ฆฌ ๋์ ๊ฒ์ถ
- Memory โ Heap Snapshot โ GC ๊ฐ์ ์คํ
- Detached DOM Tree ํ์ธ
- Object โ Retainers Chain โ ๋์๋ ์ฐธ์กฐ ํ์ธ
- ์ค์ ์์ธ (ํด๋ก์ , DOM ์ฐธ์กฐ ๋ฑ) ์ถ์ ๊ฐ๋ฅ
๐ณ ๋ฉ๋ชจ๋ฆฌ ๋์ ๋ฐ์ ํ๋ฆ ๋์ํ
[ DOM ์์ ]
โ
[ ์ด๋ฒคํธ ๋ฆฌ์ค๋, ํด๋ก์ ์์ ์ฐธ์กฐ ]
โ
[ ์์ ์ญ์ โ ์ฐธ์กฐ ๋จ์ ]
โ
[ GC ๋ถ๊ฐ โ ๋ฉ๋ชจ๋ฆฌ ์ฆ๊ฐ ]
2๏ธโฃ ๐ฏ ๋ฆฌํ๋ก์ฐ & ๋ฆฌํ์ธํธ ์ต์ ํ ์ฌํ
๐ ์ ํํ ์ฐจ์ด
์ฉ์ด | ์๋ฏธ | ์ฑ๋ฅ ์ํฅ |
---|---|---|
Reflow (Layout) | DOM ๊ตฌ์กฐ ๋ณ๊ฒฝ โ ๋ ์ด์์ ์ฌ๊ณ์ฐ | ํฌ๋ค (ํธ๋ฆฌ ์ ์ฒด ์ํฅ) |
Repaint | ์์/ํฐํธ ๋ฑ ์๊ฐ์ ์์ฑ ๋ณ๊ฒฝ | ์๋์ ๋ฎ์ |
๐จ Reflow ๋ฐ์ ํธ๋ฆฌ๊ฑฐ ์ฌํ
ํธ๋ฆฌ๊ฑฐ | ์ค๋ช |
---|---|
DOM ์ฝ์ /์ญ์ | .appendChild() , .remove() |
ํด๋์ค ์ถ๊ฐ/์ญ์ | .classList.add() |
offsetWidth, getBoundingClientRect ์ ๊ทผ | ๊ฐ์ Reflow ๋ฐ์! |
์๋์ฐ ๋ฆฌ์ฌ์ด์ฆ, ํฐํธ ๋ณ๊ฒฝ ๋ฑ | ย |
๐ ๏ธ ์ต์ ํ ์ ๋ต
๋ฐฉ๋ฒ | ์ค๋ช |
---|---|
DocumentFragment | ๋ค์ DOM ์กฐ์ โ ํ ๋ฒ์ ์ฝ์ |
batch ์ฒ๋ฆฌ | ์คํ์ผ ๋ณ๊ฒฝ ํ ๋ฒ์ |
requestAnimationFrame | ์ ๋๋ฉ์ด์ ์ต์ ํ |
GPU ๊ฐ์(css transform, opacity) | Reflow ํผํ๊ณ Repaint๋ง ์ ๋ |
โ ์๋ชป๋ ์์ :
for (let i = 0; i < 1000; i++) {
const item = document.createElement('li');
item.textContent = `Item ${i}`;
document.body.appendChild(item); // Reflow 1000๋ฒ ๋ฐ์
}
โ ๊ฐ์ ์์ :
const frag = document.createDocumentFragment();
for (let i = 0; i < 1000; i++) {
const item = document.createElement('li');
item.textContent = `Item ${i}`;
frag.appendChild(item);
}
document.body.appendChild(frag); // Reflow 1๋ฒ
3๏ธโฃ ๐ ๏ธ ํฌ๋กฌ DevTools ๊ณ ๊ธ ๋๋ฒ๊น
๐ ํ์ ํญ & ์ค๋ฌด ํฌ์ธํธ
ํญ | ์ฌ์ฉ๋ฒ |
---|---|
Performance | FPS Drop ์์ธ, Layout Shifts ํ์ธ |
Memory โ Leak ๋ถ์ | Snapshot โ Detached DOM/Closure ์ถ์ |
Sources โ Breakpoints | ์กฐ๊ฑด๋ถ, DOM ๋ณ๊ฒฝ ์, XHR ์์ฒญ ์ ์ค๋จ ๊ฐ๋ฅ |
Network | ์์ฒญ/์๋ต ํค๋, ์บ์ ์ ๋ต ํ์ธ |
Application | ์ฟ ํค, Storage, Service Worker ํ์ธ |
๐ ์ค์ ํธ๋ฌ๋ธ ์ํ ์์
- Performance โ Long Task (๋นจ๊ฐ์) ํ์ธ
- ํด๋น Task โ ํจ์ ํ์ธ โ ์คํฌ๋กค ๋ ์์ธ pinpoint
- Memory โ Heap ๋น๊ต, ๋์ ์์ธ ์ฐพ๊ธฐ
4๏ธโฃ ๐ XSS & CSRF ์ฌํ
๐ฆ XSS ์ข ๋ฅ๋ณ ์์
์ ํ | ์ค๋ช | ์์ |
---|---|---|
Stored XSS | DB์ ์คํฌ๋ฆฝํธ ์ ์ฅ โ ์ฌ๋ฌ ์ฌ์ฉ์ ๋ ธ์ถ | ๋๊ธ์ <script>alert(1)</script> |
Reflected XSS | URL์ ์ฝ์ โ ์ฆ์ ์คํ | ?search=<script>alert(1)</script> |
DOM-based XSS | JS๊ฐ innerHTML๋ก ๋ฐ๋ก ์ฝ์ | element.innerHTML = userInput; |
โ CSP ์ ์ฉ ์ค๋ฌด ์์
<meta http-equiv="Content-Security-Policy" content="default-src 'self'; script-src 'self'; object-src 'none';">
- ์ธ๋ถ ์คํฌ๋ฆฝํธ, ์ธ๋ผ์ธ ์คํฌ๋ฆฝํธ ์ฐจ๋จ
๐ฆ CSRF ๊ตฌ์กฐ ์๊ฐํ
User ๋ก๊ทธ์ธ โ ๊ณต๊ฒฉ ์ฌ์ดํธ ์ ์ โ
<img src="https://bank.com/transfer?amount=1000"> โ
์ฟ ํค ์๋ ํฌํจ โ ์์ฒญ ์กฐ์ ์ฑ๊ณต
โ ์ค๋ฌด ๋ฐฉ์ด: Token + SameSite
Set-Cookie: session=abc; SameSite=Strict; Secure
- ์๋ฒ โ ๋๋ค CSRF Token ๋ฐ๊ธ
- ํผ/ํค๋์ ํฌํจ โ ์๋ฒ ๊ฒ์ฆ
- SameSite + HTTPS ํ์
๐ง ๊ธฐ์ ๋ฉด์ ๋๋น ์ด๊ณ ๊ธ ์์ฝ
์ง๋ฌธ | ํต์ฌ ๋ต๋ณ |
---|---|
๋ฉ๋ชจ๋ฆฌ ๋์ ์์ธ? | ์ ์ญ ๋ณ์, DOM ์ฐธ์กฐ, ํด๋ก์ , ์ด๋ฒคํธ ๋ฏธ์ ๊ฑฐ, ํ์ด๋จธ |
๋ฆฌํ๋ก์ฐ vs ๋ฆฌํ์ธํธ? | ๋ ์ด์์ ์ฌ๊ณ์ฐ โ ์ฑ๋ฅ ์ํฅ ํผ / ์์, ๊ธ๊ผด ๋ฑ |
DevTools Memory์์ ๋์ ์ฐพ๊ธฐ? | Heap Snapshot โ Detached DOM, Retainers Chain ๋ถ์ |
XSS ๋ฐฉ์ด๋ฒ? | ์ ๋ ฅ ๊ฒ์ฆ, ์ด์ค์ผ์ดํ, CSP |
CSRF ๋ฐฉ์ด๋ฒ? | Token ๊ฒ์ฆ, SameSite ์ฟ ํค, CORS, HTTPS |
โ ์ข ํฉ์ฝ๋ ์์
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>๊ณ ๊ธ ์ฑ๋ฅ ์ต์ ํ & ๋ณด์ ์ค์ต ๐</title>
<style>
.output {
width: 100%;
min-height: 50px;
border: 1px solid #333;
margin-top: 10px;
padding: 10px;
background-color: #f9f9f9;
}
</style>
</head>
<body>
<h1>๐ ์ฑ๋ฅ ์ต์ ํ & ๋ณด์ ์ค์ต</h1>
<h3>1๏ธโฃ ๋ฉ๋ชจ๋ฆฌ ๋์ ๋ฐฉ์ง</h3>
<button id="btnMemoryLeak">๐ ์คํ</button>
<div id="output1" class="output"></div>
<h3>2๏ธโฃ ๋ฆฌํ๋ก์ฐ & ๋ฆฌํ์ธํธ ์ต์ ํ</h3>
<button id="btnReflow">โก ์คํ</button>
<div id="output2" class="output"></div>
<h3>3๏ธโฃ ํฌ๋กฌ DevTools ํ์ฉ</h3>
<button id="btnDevTools">๐ ์คํ</button>
<div id="output3" class="output"></div>
<h3>4๏ธโฃ XSS & CSRF ๋ฐฉ์ด</h3>
<button id="btnSecurity">๐ ์คํ</button>
<div id="output4" class="output"></div>
<script>
/*****************************
1๏ธโฃ ๋ฉ๋ชจ๋ฆฌ ๋์ ๋ฐฉ์ง
*****************************/
document.getElementById("btnMemoryLeak").addEventListener("click", function() {
let element = document.createElement("div");
element.textContent = "โ
๋ฉ๋ชจ๋ฆฌ ๋์ ์์ ์คํ!";
document.body.appendChild(element);
// ๐ด ๋ฉ๋ชจ๋ฆฌ ๋์ ๋ฐฉ์ง: ํ์ด๋จธ ์ข
๋ฃ ํ ์์ ์ ๊ฑฐ
setTimeout(() => {
document.body.removeChild(element);
}, 2000);
document.getElementById("output1").textContent = "๋ฉ๋ชจ๋ฆฌ ๊ด๋ฆฌ: DOM ์์ ์๋ ์ ๊ฑฐ";
});
/*****************************
2๏ธโฃ ๋ฆฌํ๋ก์ฐ & ๋ฆฌํ์ธํธ ์ต์ ํ
*****************************/
document.getElementById("btnReflow").addEventListener("click", function() {
const frag = document.createDocumentFragment();
for (let i = 0; i < 1000; i++) {
const div = document.createElement("div");
div.textContent = `Item ${i}`;
frag.appendChild(div);
}
document.body.appendChild(frag); // ๐ Reflow ์ต์ํ
document.getElementById("output2").textContent = "Reflow ์ต์ ํ ์๋ฃ!";
});
/*****************************
3๏ธโฃ ํฌ๋กฌ DevTools ํ์ฉ
*****************************/
document.getElementById("btnDevTools").addEventListener("click", function() {
console.log("Performance ํญ์์ ํ์ธํ์ธ์!");
document.getElementById("output3").textContent = "DevTools ํ์ฉ: ์ฝ์ ํ์ธ!";
});
/*****************************
4๏ธโฃ XSS & CSRF ๋ฐฉ์ด
*****************************/
document.getElementById("btnSecurity").addEventListener("click", function() {
const userInput = "<script>alert('XSS ๊ณต๊ฒฉ!')</script>";
// ๐ XSS ๋ฐฉ์ง: ํน์๋ฌธ์๋ฅผ HTML ์ํฐํฐ๋ก ๋ณํ
const sanitizedInput = userInput.replace(/</g, "<").replace(/>/g, ">");
document.getElementById("output4").innerHTML = `์์ ํ ์ถ๋ ฅ: ${sanitizedInput}`;
});
</script>
</body>
</html>