์๋ฐ์คํฌ๋ฆฝํธ ๋น๋๊ธฐ ์ปจํ ์คํธ ๋ฉ๋ชจ๋ฆฌ ๊ด๋ฆฌ๋ฅผ ๋ง์คํฐํ๊ณ , ์ปจํ ์คํธ ์๋ช ์ฃผ๊ธฐ๋ฅผ ์ต์ ํํ์ฌ ๋น๋๊ธฐ ์ ํ๋ฆฌ์ผ์ด์ ์ ์ฑ๋ฅ๊ณผ ์์ ์ฑ์ ํฅ์์ํค์ธ์.
์๋ฐ์คํฌ๋ฆฝํธ ๋น๋๊ธฐ ์ปจํ ์คํธ ๋ฉ๋ชจ๋ฆฌ ๊ด๋ฆฌ: ์ปจํ ์คํธ ์๋ช ์ฃผ๊ธฐ ์ต์ ํ
๋น๋๊ธฐ ํ๋ก๊ทธ๋๋ฐ์ ํ๋ ์๋ฐ์คํฌ๋ฆฝํธ ๊ฐ๋ฐ์ ํต์ฌ์ผ๋ก, ๋ฐ์์ฑ ๋๊ณ ํจ์จ์ ์ธ ์ ํ๋ฆฌ์ผ์ด์ ์ ๊ตฌ์ถํ ์ ์๊ฒ ํด์ค๋๋ค. ๊ทธ๋ฌ๋ ๋น๋๊ธฐ ์์ ์์ ์ปจํ ์คํธ๋ฅผ ๊ด๋ฆฌํ๋ ๊ฒ์ ๋ณต์กํด์ง ์ ์์ผ๋ฉฐ, ์ ์คํ๊ฒ ๋ค๋ฃจ์ง ์์ผ๋ฉด ๋ฉ๋ชจ๋ฆฌ ๋์๋ ์ฑ๋ฅ ๋ฌธ์ ๋ฅผ ์ผ์ผํฌ ์ ์์ต๋๋ค. ์ด ๊ธ์์๋ ์๋ฐ์คํฌ๋ฆฝํธ์ ๋น๋๊ธฐ ์ปจํ ์คํธ์ ๋ณต์ก์ฑ์ ๊น์ด ํ๊ณ ๋ค์ด, ๊ฒฌ๊ณ ํ๊ณ ํ์ฅ ๊ฐ๋ฅํ ์ ํ๋ฆฌ์ผ์ด์ ์ ์ํ ์๋ช ์ฃผ๊ธฐ ์ต์ ํ์ ์ด์ ์ ๋ง์ถฅ๋๋ค.
์๋ฐ์คํฌ๋ฆฝํธ์ ๋น๋๊ธฐ ์ปจํ ์คํธ ์ดํดํ๊ธฐ
๋๊ธฐ์ ์ธ ์๋ฐ์คํฌ๋ฆฝํธ ์ฝ๋์์ ์ปจํ ์คํธ(๋ณ์, ํจ์ ํธ์ถ, ์คํ ์ํ)๋ ๊ด๋ฆฌํ๊ธฐ๊ฐ ๊ฐ๋จํฉ๋๋ค. ํจ์๊ฐ ์ข ๋ฃ๋๋ฉด ํด๋น ์ปจํ ์คํธ๋ ์ผ๋ฐ์ ์ผ๋ก ํด์ ๋์ด ๊ฐ๋น์ง ์ปฌ๋ ํฐ๊ฐ ๋ฉ๋ชจ๋ฆฌ๋ฅผ ํ์ํ ์ ์๊ฒ ๋ฉ๋๋ค. ๊ทธ๋ฌ๋ ๋น๋๊ธฐ ์์ ์ ๋ณต์ก์ฑ์ ํ ์ธต ๋ํฉ๋๋ค. API์์ ๋ฐ์ดํฐ๋ฅผ ๊ฐ์ ธ์ค๊ฑฐ๋ ์ฌ์ฉ์ ์ด๋ฒคํธ๋ฅผ ์ฒ๋ฆฌํ๋ ๊ฒ๊ณผ ๊ฐ์ ๋น๋๊ธฐ ์์ ์ ์ฆ์ ์๋ฃ๋์ง ์๋ ๊ฒฝ์ฐ๊ฐ ๋ง์ต๋๋ค. ์ด๋ฌํ ์์ ๋ค์ ์ฝ๋ฐฑ, ํ๋ก๋ฏธ์ค(Promise), ๋๋ async/await๋ฅผ ํฌํจํ๋ฉฐ, ์ด๋ ํด๋ก์ ๋ฅผ ์์ฑํ๊ณ ์ฃผ๋ณ ์ค์ฝํ์ ๋ณ์์ ๋ํ ์ฐธ์กฐ๋ฅผ ์ ์งํ ์ ์์ต๋๋ค. ์ด๋ก ์ธํด ์ปจํ ์คํธ์ ์ผ๋ถ๊ฐ ํ์ ์ด์์ผ๋ก ์ค๋ ์ ์ง๋์ด ์๋์น ์๊ฒ ๋ฉ๋ชจ๋ฆฌ ๋์๋ก ์ด์ด์ง ์ ์์ต๋๋ค.
ํด๋ก์ ์ ์ญํ
ํด๋ก์ ๋ ๋น๋๊ธฐ ์๋ฐ์คํฌ๋ฆฝํธ์์ ์ค์ํ ์ญํ ์ ํฉ๋๋ค. ํด๋ก์ ๋ ํจ์์ ๊ทธ ํจ์๊ฐ ์ ์ธ๋ ๋น์์ ๋ ์์ปฌ ํ๊ฒฝ(surrounding state)์ ๋ํ ์ฐธ์กฐ์ ์กฐํฉ์ ๋๋ค. ์ฆ, ํด๋ก์ ๋ ๋ด๋ถ ํจ์์์ ์ธ๋ถ ํจ์์ ์ค์ฝํ์ ์ ๊ทผํ ์ ์๊ฒ ํด์ค๋๋ค. ๋น๋๊ธฐ ์์ ์ด ์ฝ๋ฐฑ์ด๋ ํ๋ก๋ฏธ์ค์ ์์กดํ ๋, ์ข ์ข ํด๋ก์ ๋ฅผ ์ฌ์ฉํ์ฌ ๋ถ๋ชจ ์ค์ฝํ์ ๋ณ์์ ์ ๊ทผํฉ๋๋ค. ๋ง์ฝ ์ด ํด๋ก์ ๋ค์ด ๋ ์ด์ ํ์ํ์ง ์์ ํฐ ๊ฐ์ฒด๋ ๋ฐ์ดํฐ ๊ตฌ์กฐ์ ๋ํ ์ฐธ์กฐ๋ฅผ ์ ์งํ๋ค๋ฉด, ๋ฉ๋ชจ๋ฆฌ ์๋น์ ์๋นํ ์ํฅ์ ๋ฏธ์น ์ ์์ต๋๋ค.
๋ค์ ์์ ๋ฅผ ์ดํด๋ณด๊ฒ ์ต๋๋ค:
function fetchData(url) {
const largeData = new Array(1000000).fill('some data'); // Simulate a large dataset
return new Promise((resolve, reject) => {
setTimeout(() => {
// Simulate fetching data from an API
const result = `Data from ${url}`; // Uses url from the outer scope
resolve(result);
}, 1000);
});
}
async function processData() {
const data = await fetchData('https://example.com/api/data');
console.log(data);
// largeData is still in scope here, even if it's not used directly
}
processData();
์ด ์์ ์์ `processData`๊ฐ ๊ฐ์ ธ์จ ๋ฐ์ดํฐ๋ฅผ ๋ก๊ทธ์ ๊ธฐ๋กํ ํ์๋, `fetchData` ๋ด์ `setTimeout` ์ฝ๋ฐฑ์ ์ํด ์์ฑ๋ ํด๋ก์ ๋๋ฌธ์ `largeData`๋ ์ฌ์ ํ ์ค์ฝํ ๋ด์ ๋จ์ ์์ต๋๋ค. ๋ง์ฝ `fetchData`๊ฐ ์ฌ๋ฌ ๋ฒ ํธ์ถ๋๋ค๋ฉด, ์ฌ๋ฌ `largeData` ์ธ์คํด์ค๊ฐ ๋ฉ๋ชจ๋ฆฌ์ ์ ์ง๋์ด ์ ์ฌ์ ์ผ๋ก ๋ฉ๋ชจ๋ฆฌ ๋์๋ก ์ด์ด์ง ์ ์์ต๋๋ค.
๋น๋๊ธฐ ์๋ฐ์คํฌ๋ฆฝํธ์์ ๋ฉ๋ชจ๋ฆฌ ๋์ ์๋ณํ๊ธฐ
๋น๋๊ธฐ ์๋ฐ์คํฌ๋ฆฝํธ์์ ๋ฉ๋ชจ๋ฆฌ ๋์๋ฅผ ๊ฐ์งํ๋ ๊ฒ์ ์ด๋ ค์ธ ์ ์์ต๋๋ค. ๋ค์์ ๋ช ๊ฐ์ง ์ผ๋ฐ์ ์ธ ๋๊ตฌ์ ๊ธฐ์ ์ ๋๋ค:
- ๋ธ๋ผ์ฐ์ ๊ฐ๋ฐ์ ๋๊ตฌ: ๋๋ถ๋ถ์ ์ต์ ๋ธ๋ผ์ฐ์ ๋ ๋ฉ๋ชจ๋ฆฌ ์ฌ์ฉ๋์ ํ๋กํ์ผ๋งํ๊ธฐ ์ํ ๊ฐ๋ ฅํ ๊ฐ๋ฐ์ ๋๊ตฌ๋ฅผ ์ ๊ณตํฉ๋๋ค. ์๋ฅผ ๋ค์ด, ํฌ๋กฌ ๊ฐ๋ฐ์ ๋๊ตฌ(Chrome DevTools)๋ฅผ ์ฌ์ฉํ๋ฉด ํ ์ค๋ ์ท์ ์ฐ๊ณ , ๋ฉ๋ชจ๋ฆฌ ํ ๋น ํ์๋ผ์ธ์ ๊ธฐ๋กํ๋ฉฐ, ๊ฐ๋น์ง ์ปฌ๋ ์ ๋์ง ์๋ ๊ฐ์ฒด๋ฅผ ์๋ณํ ์ ์์ต๋๋ค. ์ ์ฌ์ ์ธ ๋์๋ฅผ ์กฐ์ฌํ ๋๋ ์ ์ง๋ ํฌ๊ธฐ(retained size)์ ์์ฑ์ ์ ํ์ ์ฃผ์๋ฅผ ๊ธฐ์ธ์ด์ธ์.
- Node.js ๋ฉ๋ชจ๋ฆฌ ํ๋กํ์ผ๋ฌ: Node.js ์ ํ๋ฆฌ์ผ์ด์ ์ ๊ฒฝ์ฐ, `heapdump`๋ `v8-profiler`์ ๊ฐ์ ๋๊ตฌ๋ฅผ ์ฌ์ฉํ์ฌ ํ ์ค๋ ์ท์ ์บก์ฒํ๊ณ ๋ฉ๋ชจ๋ฆฌ ์ฌ์ฉ๋์ ๋ถ์ํ ์ ์์ต๋๋ค. Node.js ์ธ์คํํฐ(`node --inspect`) ๋ํ ํฌ๋กฌ ๊ฐ๋ฐ์ ๋๊ตฌ์ ์ ์ฌํ ๋๋ฒ๊น ์ธํฐํ์ด์ค๋ฅผ ์ ๊ณตํฉ๋๋ค.
- ์ฑ๋ฅ ๋ชจ๋ํฐ๋ง ๋๊ตฌ: New Relic, Datadog, Sentry์ ๊ฐ์ ์ ํ๋ฆฌ์ผ์ด์ ์ฑ๋ฅ ๋ชจ๋ํฐ๋ง(APM) ๋๊ตฌ๋ ์๊ฐ ๊ฒฝ๊ณผ์ ๋ฐ๋ฅธ ๋ฉ๋ชจ๋ฆฌ ์ฌ์ฉ๋ ์ถ์ธ์ ๋ํ ํต์ฐฐ๋ ฅ์ ์ ๊ณตํ ์ ์์ต๋๋ค. ์ด๋ฌํ ๋๊ตฌ๋ ํจํด์ ์๋ณํ๊ณ ์ฝ๋์์ ๋ฉ๋ชจ๋ฆฌ ๋์์ ๊ธฐ์ฌํ ์ ์๋ ์์ญ์ ์ ํํ ์ฐพ์๋ด๋ ๋ฐ ๋์์ด ๋ฉ๋๋ค.
- ์ฝ๋ ๋ฆฌ๋ทฐ: ์ ๊ธฐ์ ์ธ ์ฝ๋ ๋ฆฌ๋ทฐ๋ ์ ์ฌ์ ์ธ ๋ฉ๋ชจ๋ฆฌ ๊ด๋ฆฌ ๋ฌธ์ ๊ฐ ์ฌ๊ฐํด์ง๊ธฐ ์ ์ ์๋ณํ๋ ๋ฐ ๋์์ด ๋ ์ ์์ต๋๋ค. ๋น๋๊ธฐ ์์ ์์ ์ฌ์ฉ๋๋ ํด๋ก์ , ์ด๋ฒคํธ ๋ฆฌ์ค๋, ๋ฐ์ดํฐ ๊ตฌ์กฐ์ ํนํ ์ฃผ์๋ฅผ ๊ธฐ์ธ์ด์ธ์.
๋ฉ๋ชจ๋ฆฌ ๋์์ ์ผ๋ฐ์ ์ธ ์งํ
๋ค์์ ์ฌ๋ฌ๋ถ์ ์๋ฐ์คํฌ๋ฆฝํธ ์ ํ๋ฆฌ์ผ์ด์ ์ด ๋ฉ๋ชจ๋ฆฌ ๋์๋ฅผ ๊ฒช๊ณ ์์ ์ ์๋ค๋ ๋ช ๊ฐ์ง ๋ช ๋ฐฑํ ์งํ์ ๋๋ค:
- ์ ์ง์ ์ธ ๋ฉ๋ชจ๋ฆฌ ์ฌ์ฉ๋ ์ฆ๊ฐ: ์ ํ๋ฆฌ์ผ์ด์ ์ด ํ๋ฐํ๊ฒ ์์ ์ ์ํํ์ง ์์ ๋์๋ ์๊ฐ์ด ์ง๋จ์ ๋ฐ๋ผ ๋ฉ๋ชจ๋ฆฌ ์๋น๊ฐ ๊พธ์คํ ์ฆ๊ฐํฉ๋๋ค.
- ์ฑ๋ฅ ์ ํ: ์ ํ๋ฆฌ์ผ์ด์ ์ด ๋ ์ค๋ ๊ธฐ๊ฐ ์คํ๋ ์๋ก ๋๋ ค์ง๊ณ ๋ฐ์์ฑ์ด ๋จ์ด์ง๋๋ค.
- ์ฆ์ ๊ฐ๋น์ง ์ปฌ๋ ์ ์ฃผ๊ธฐ: ๊ฐ๋น์ง ์ปฌ๋ ํฐ๊ฐ ๋ ์์ฃผ ์คํ๋๋ฉฐ, ์ด๋ ๋ฉ๋ชจ๋ฆฌ ํ์์ ์ด๋ ค์์ ๊ฒช๊ณ ์์์ ๋ํ๋ ๋๋ค.
- ์ ํ๋ฆฌ์ผ์ด์ ์ถฉ๋: ๊ทน๋จ์ ์ธ ๊ฒฝ์ฐ, ๋ฉ๋ชจ๋ฆฌ ๋์๋ ๋ฉ๋ชจ๋ฆฌ ๋ถ์กฑ ์ค๋ฅ๋ก ์ธํด ์ ํ๋ฆฌ์ผ์ด์ ์ถฉ๋๋ก ์ด์ด์ง ์ ์์ต๋๋ค.
๋น๋๊ธฐ ์ปจํ ์คํธ ์๋ช ์ฃผ๊ธฐ ์ต์ ํ
์ด์ ๋น๋๊ธฐ ์ปจํ ์คํธ ๋ฉ๋ชจ๋ฆฌ ๊ด๋ฆฌ์ ์ด๋ ค์์ ์ดํดํ์ผ๋ฏ๋ก, ์ปจํ ์คํธ ์๋ช ์ฃผ๊ธฐ๋ฅผ ์ต์ ํํ๊ธฐ ์ํ ๋ช ๊ฐ์ง ์ ๋ต์ ์ดํด๋ณด๊ฒ ์ต๋๋ค:
1. ํด๋ก์ ์ค์ฝํ ์ต์ํ
ํด๋ก์ ์ ์ค์ฝํ๊ฐ ์์์๋ก ๋ ์ ์ ๋ฉ๋ชจ๋ฆฌ๋ฅผ ์๋นํฉ๋๋ค. ํด๋ก์ ์์ ๋ถํ์ํ ๋ณ์๋ฅผ ์บก์ฒํ๋ ๊ฒ์ ํผํ์ธ์. ๋์ , ๋น๋๊ธฐ ์์ ์ ์๊ฒฉํ๊ฒ ํ์ํ ๋ฐ์ดํฐ๋ง ์ ๋ฌํ์ธ์.
์์ :
๋์ ์:
function processUserData(user) {
const userData = { ...user, extraData: 'some extra info' }; // Create a new object
setTimeout(() => {
console.log(`Processing user: ${userData.name}`); // Access userData
}, 1000);
}
์ด ์์ ์์๋ `setTimeout` ์ฝ๋ฐฑ ๋ด๋ถ์์ `name` ์์ฑ๋ง ์ฌ์ฉ๋จ์๋ ๋ถ๊ตฌํ๊ณ ์ ์ฒด `userData` ๊ฐ์ฒด๊ฐ ํด๋ก์ ์ ์บก์ฒ๋ฉ๋๋ค.
์ข์ ์:
function processUserData(user) {
const userData = { ...user, extraData: 'some extra info' };
const userName = userData.name; // Extract the name
setTimeout(() => {
console.log(`Processing user: ${userName}`); // Access only userName
}, 1000);
}
์ด ์ต์ ํ๋ ๋ฒ์ ์์๋ `userName`๋ง ํด๋ก์ ์ ์บก์ฒ๋์ด ๋ฉ๋ชจ๋ฆฌ ์ฌ์ฉ๋์ ์ค์ ๋๋ค.
2. ์ํ ์ฐธ์กฐ ๋๊ธฐ
์ํ ์ฐธ์กฐ๋ ๋ ๊ฐ ์ด์์ ๊ฐ์ฒด๊ฐ ์๋ก๋ฅผ ์ฐธ์กฐํ์ฌ ๊ฐ๋น์ง ์ปฌ๋ ์ ๋๋ ๊ฒ์ ๋ง์ ๋ ๋ฐ์ํฉ๋๋ค. ์ด๋ ๋น๋๊ธฐ ์๋ฐ์คํฌ๋ฆฝํธ์์, ํนํ ์ด๋ฒคํธ ๋ฆฌ์ค๋๋ ๋ณต์กํ ๋ฐ์ดํฐ ๊ตฌ์กฐ๋ฅผ ๋ค๋ฃฐ ๋ ํํ ๋ฌธ์ ๊ฐ ๋ ์ ์์ต๋๋ค.
์์ :
class MyObject {
constructor() {
this.eventListeners = [];
}
addListener(listener) {
this.eventListeners.push(listener);
}
removeListener(listener) {
this.eventListeners = this.eventListeners.filter(l => l !== listener);
}
doSomethingAsync() {
const listener = () => {
console.log('Something happened!');
this.doSomethingElse(); // Circular reference: listener references this
};
this.addListener(listener);
setTimeout(() => {
this.removeListener(listener);
}, 1000);
}
doSomethingElse() {
console.log('Doing something else.');
}
}
const myObject = new MyObject();
myObject.doSomethingAsync();
์ด ์์ ์์ `doSomethingAsync` ๋ด์ `listener` ํจ์๋ `this`(`MyObject` ์ธ์คํด์ค)์ ๋ํ ์ฐธ์กฐ๋ฅผ ์บก์ฒํฉ๋๋ค. `MyObject` ์ธ์คํด์ค ๋ํ `eventListeners` ๋ฐฐ์ด์ ํตํด `listener`์ ๋ํ ์ฐธ์กฐ๋ฅผ ๊ฐ์ง๊ณ ์์ต๋๋ค. ์ด๋ก ์ธํด `setTimeout` ์ฝ๋ฐฑ์ด ์คํ๋ ํ์๋ `MyObject` ์ธ์คํด์ค์ `listener` ๋ชจ๋ ๊ฐ๋น์ง ์ปฌ๋ ์ ๋๋ ๊ฒ์ ๋ง๋ ์ํ ์ฐธ์กฐ๊ฐ ์์ฑ๋ฉ๋๋ค. ๋น๋ก ๋ฆฌ์ค๋๊ฐ eventListeners ๋ฐฐ์ด์์ ์ ๊ฑฐ๋๋๋ผ๋, ํด๋ก์ ์์ฒด๋ ์ฌ์ ํ `this`์ ๋ํ ์ฐธ์กฐ๋ฅผ ์ ์งํฉ๋๋ค.
ํด๊ฒฐ์ฑ : ๋ ์ด์ ํ์ํ์ง ์์ ๋ ์ฐธ์กฐ๋ฅผ ๋ช ์์ ์ผ๋ก `null` ๋๋ undefined๋ก ์ค์ ํ์ฌ ์ํ ์ฐธ์กฐ๋ฅผ ๋์ต๋๋ค.
class MyObject {
constructor() {
this.eventListeners = [];
}
addListener(listener) {
this.eventListeners.push(listener);
}
removeListener(listener) {
this.eventListeners = this.eventListeners.filter(l => l !== listener);
}
doSomethingAsync() {
let listener = () => {
console.log('Something happened!');
this.doSomethingElse();
listener = null; // Break the circular reference
};
this.addListener(listener);
setTimeout(() => {
this.removeListener(listener);
}, 1000);
}
doSomethingElse() {
console.log('Doing something else.');
}
}
const myObject = new MyObject();
myObject.doSomethingAsync();
์์ ํด๊ฒฐ์ฑ ์ด ์ํ ์ฐธ์กฐ๋ฅผ ๋๋ ๊ฒ์ฒ๋ผ ๋ณด์ผ ์ ์์ง๋ง, `setTimeout` ๋ด์ ๋ฆฌ์ค๋๋ ์ฌ์ ํ ์๋์ `listener` ํจ์๋ฅผ ์ฐธ์กฐํ๊ณ , ์ด๋ ๋ค์ `this`๋ฅผ ์ฐธ์กฐํฉ๋๋ค. ๋ ๊ฒฌ๊ณ ํ ํด๊ฒฐ์ฑ ์ ๋ฆฌ์ค๋ ๋ด์์ `this`๋ฅผ ์ง์ ์บก์ฒํ๋ ๊ฒ์ ํผํ๋ ๊ฒ์ ๋๋ค.
class MyObject {
constructor() {
this.eventListeners = [];
}
addListener(listener) {
this.eventListeners.push(listener);
}
removeListener(listener) {
this.eventListeners = this.eventListeners.filter(l => l !== listener);
}
doSomethingAsync() {
const self = this; // Capture 'this' in a separate variable
const listener = () => {
console.log('Something happened!');
self.doSomethingElse(); // Use the captured 'self'
};
this.addListener(listener);
setTimeout(() => {
this.removeListener(listener);
}, 1000);
}
doSomethingElse() {
console.log('Doing something else.');
}
}
const myObject = new MyObject();
myObject.doSomethingAsync();
์ด๊ฒ์ ์ด๋ฒคํธ ๋ฆฌ์ค๋๊ฐ ์ค๋ซ๋์ ์ฐ๊ฒฐ๋์ด ์๋ ๊ฒฝ์ฐ ๋ฌธ์ ๋ฅผ ์์ ํ ํด๊ฒฐํ์ง ๋ชปํฉ๋๋ค. ๊ฐ์ฅ ์ ๋ขฐํ ์ ์๋ ์ ๊ทผ ๋ฐฉ์์ `MyObject` ์ธ์คํด์ค๋ฅผ ์ง์ ์ฐธ์กฐํ๋ ํด๋ก์ ๋ฅผ ํผํ๊ณ ์ด๋ฒคํธ ๋ฐ์ ๋ฉ์ปค๋์ฆ์ ์ฌ์ฉํ๋ ๊ฒ์ ๋๋ค.
3. ์ด๋ฒคํธ ๋ฆฌ์ค๋ ๊ด๋ฆฌ
์ด๋ฒคํธ ๋ฆฌ์ค๋๋ ์ ๋๋ก ์ ๊ฑฐ๋์ง ์์ผ๋ฉด ๋ฉ๋ชจ๋ฆฌ ๋์์ ํํ ์์ธ์ด ๋ฉ๋๋ค. ์์๋ ๊ฐ์ฒด์ ์ด๋ฒคํธ ๋ฆฌ์ค๋๋ฅผ ์ฒจ๋ถํ๋ฉด, ๋ฆฌ์ค๋๋ ๋ช ์์ ์ผ๋ก ์ ๊ฑฐ๋๊ฑฐ๋ ์์/๊ฐ์ฒด๊ฐ ํ๊ดด๋ ๋๊น์ง ํ์ฑ ์ํ๋ก ์ ์ง๋ฉ๋๋ค. ๋ฆฌ์ค๋๋ฅผ ์ ๊ฑฐํ๋ ๊ฒ์ ์์ผ๋ฉด ์๊ฐ์ด ์ง๋จ์ ๋ฐ๋ผ ๋์ ๋์ด ๋ฉ๋ชจ๋ฆฌ๋ฅผ ์๋นํ๊ณ ์ ์ฌ์ ์ผ๋ก ์ฑ๋ฅ ๋ฌธ์ ๋ฅผ ์ผ์ผํฌ ์ ์์ต๋๋ค.
์์ :
const button = document.getElementById('myButton');
function handleClick() {
console.log('Button clicked!');
}
button.addEventListener('click', handleClick);
// PROBLEM: The event listener is never removed!
ํด๊ฒฐ์ฑ : ๋ ์ด์ ํ์ํ์ง ์์ ๋ ํญ์ ์ด๋ฒคํธ ๋ฆฌ์ค๋๋ฅผ ์ ๊ฑฐํ์ธ์.
const button = document.getElementById('myButton');
function handleClick() {
console.log('Button clicked!');
button.removeEventListener('click', handleClick); // Remove the listener
}
button.addEventListener('click', handleClick);
// Alternatively, remove the listener after a certain condition:
setTimeout(() => {
button.removeEventListener('click', handleClick);
}, 5000);
`WeakMap`์ ์ฌ์ฉํ์ฌ DOM ์์์ ๊ฐ๋น์ง ์ปฌ๋ ์ ์ ๋ฐฉํดํ์ง ์์ผ๋ฉด์ ํด๋น ์์์ ๋ฐ์ดํฐ๋ฅผ ์ฐ๊ฒฐํ๋ ๊ฒ์ ๊ณ ๋ คํด ๋ณด์ธ์.
4. WeakRef์ FinalizationRegistry ์ฌ์ฉํ๊ธฐ (๊ณ ๊ธ)
๋ ๋ณต์กํ ์๋๋ฆฌ์ค์์๋ `WeakRef`์ `FinalizationRegistry`๋ฅผ ์ฌ์ฉํ์ฌ ๊ฐ์ฒด ์๋ช ์ฃผ๊ธฐ๋ฅผ ๋ชจ๋ํฐ๋งํ๊ณ ๊ฐ์ฒด๊ฐ ๊ฐ๋น์ง ์ปฌ๋ ์ ๋ ๋ ์ ๋ฆฌ ์์ ์ ์ํํ ์ ์์ต๋๋ค. `WeakRef`๋ ๊ฐ์ฒด์ ๊ฐ๋น์ง ์ปฌ๋ ์ ์ ๋ฐฉํดํ์ง ์์ผ๋ฉด์ ํด๋น ๊ฐ์ฒด์ ๋ํ ์ฐธ์กฐ๋ฅผ ์ ์งํ ์ ์๊ฒ ํด์ค๋๋ค. `FinalizationRegistry`๋ ๊ฐ์ฒด๊ฐ ๊ฐ๋น์ง ์ปฌ๋ ์ ๋ ๋ ์คํ๋ ์ฝ๋ฐฑ์ ๋ฑ๋กํ ์ ์๊ฒ ํด์ค๋๋ค.
์์ :
const registry = new FinalizationRegistry(heldValue => {
console.log(`Object with value ${heldValue} was garbage collected.`);
});
let obj = { data: 'some data' };
const weakRef = new WeakRef(obj);
registry.register(obj, obj.data); // Register the object with the registry
obj = null; // Remove the strong reference to the object
// At some point in the future, the garbage collector will reclaim the memory used by the object,
// and the callback in the FinalizationRegistry will be executed.
์ฌ์ฉ ์ฌ๋ก:
- ์บ์ ๊ด๋ฆฌ: `WeakRef`๋ฅผ ์ฌ์ฉํ์ฌ ํด๋น ๊ฐ์ฒด๊ฐ ๋ ์ด์ ์ฌ์ฉ๋์ง ์์ ๋ ์๋์ผ๋ก ํญ๋ชฉ์ ์ ๊ฑฐํ๋ ์บ์๋ฅผ ๊ตฌํํ ์ ์์ต๋๋ค.
- ๋ฆฌ์์ค ์ ๋ฆฌ: `FinalizationRegistry`๋ฅผ ์ฌ์ฉํ์ฌ ๊ฐ์ฒด๊ฐ ๊ฐ๋น์ง ์ปฌ๋ ์ ๋ ๋ ๋ฆฌ์์ค(์: ํ์ผ ํธ๋ค, ๋คํธ์ํฌ ์ฐ๊ฒฐ)๋ฅผ ํด์ ํ ์ ์์ต๋๋ค.
์ค์ ๊ณ ๋ ค์ฌํญ:
- ๊ฐ๋น์ง ์ปฌ๋ ์ ์ ๋น๊ฒฐ์ ์ ์ด๋ฏ๋ก `FinalizationRegistry` ์ฝ๋ฐฑ์ด ํน์ ์๊ฐ์ ์คํ๋ ๊ฒ์ด๋ผ๊ณ ์์กดํ ์ ์์ต๋๋ค.
- ์ฝ๋์ ๋ณต์ก์ฑ์ ๋ํ ์ ์์ผ๋ฏ๋ก `WeakRef`์ `FinalizationRegistry`๋ ๋๋ฌผ๊ฒ ์ฌ์ฉํ์ธ์.
5. ์ ์ญ ๋ณ์ ํผํ๊ธฐ
์ ์ญ ๋ณ์๋ ์๋ช ์ด ๊ธธ๊ณ ์ ํ๋ฆฌ์ผ์ด์ ์ด ์ข ๋ฃ๋ ๋๊น์ง ๊ฐ๋น์ง ์ปฌ๋ ์ ๋์ง ์์ต๋๋ค. ์ผ์์ ์ผ๋ก๋ง ํ์ํ ํฐ ๊ฐ์ฒด๋ ๋ฐ์ดํฐ ๊ตฌ์กฐ๋ฅผ ์ ์ฅํ๊ธฐ ์ํด ์ ์ญ ๋ณ์๋ฅผ ์ฌ์ฉํ๋ ๊ฒ์ ํผํ์ธ์. ๋์ , ํจ์๋ ๋ชจ๋ ๋ด์ ์ง์ญ ๋ณ์๋ฅผ ์ฌ์ฉํ๋ฉด ํด๋น ์ค์ฝํ๋ฅผ ๋ฒ์ด๋ ๋ ๊ฐ๋น์ง ์ปฌ๋ ์ ๋ฉ๋๋ค.
์์ :
๋์ ์:
// Global variable
let myLargeArray = new Array(1000000).fill('some data');
function processData() {
// ... use myLargeArray
}
processData();
์ข์ ์:
function processData() {
// Local variable
const myLargeArray = new Array(1000000).fill('some data');
// ... use myLargeArray
}
processData();
๋ ๋ฒ์งธ ์์ ์์ `myLargeArray`๋ `processData` ๋ด์ ์ง์ญ ๋ณ์์ด๋ฏ๋ก `processData` ์คํ์ด ๋๋๋ฉด ๊ฐ๋น์ง ์ปฌ๋ ์ ๋ฉ๋๋ค.
6. ๋ช ์์ ์ผ๋ก ๋ฆฌ์์ค ํด์ ํ๊ธฐ
์ด๋ค ๊ฒฝ์ฐ์๋ ๋น๋๊ธฐ ์์ ์ด ๋ณด์ ํ๊ณ ์๋ ๋ฆฌ์์ค๋ฅผ ๋ช ์์ ์ผ๋ก ํด์ ํด์ผ ํ ์๋ ์์ต๋๋ค. ์๋ฅผ ๋ค์ด, ๋ฐ์ดํฐ๋ฒ ์ด์ค ์ฐ๊ฒฐ์ด๋ ํ์ผ ํธ๋ค์ ์ฌ์ฉํ๋ ๊ฒฝ์ฐ, ์์ ์ด ๋๋๋ฉด ๋ซ์์ผ ํฉ๋๋ค. ์ด๋ ๋ฆฌ์์ค ๋์๋ฅผ ๋ฐฉ์งํ๊ณ ์ ํ๋ฆฌ์ผ์ด์ ์ ์ ๋ฐ์ ์ธ ์์ ์ฑ์ ํฅ์์ํค๋ ๋ฐ ๋์์ด ๋ฉ๋๋ค.
์์ :
const fs = require('fs');
async function readFileAsync(filePath) {
return new Promise((resolve, reject) => {
fs.readFile(filePath, (err, data) => {
if (err) {
reject(err);
return;
}
resolve(data);
});
});
}
async function processFile(filePath) {
let fileHandle = null;
try {
fileHandle = await fs.promises.open(filePath, 'r');
const data = await readFileAsync(filePath); // Or fileHandle.readFile()
console.log(data.toString());
} catch (error) {
console.error('Error reading file:', error);
} finally {
if (fileHandle) {
await fileHandle.close(); // Explicitly close the file handle
console.log('File handle closed.');
}
}
}
processFile('myFile.txt');
`finally` ๋ธ๋ก์ ํ์ผ ์ฒ๋ฆฌ ์ค ์ค๋ฅ๊ฐ ๋ฐ์ํ๋๋ผ๋ ํ์ผ ํธ๋ค์ด ํญ์ ๋ซํ๋๋ก ๋ณด์ฅํฉ๋๋ค.
7. ๋น๋๊ธฐ ์ดํฐ๋ ์ดํฐ์ ์ ๋๋ ์ดํฐ ์ฌ์ฉํ๊ธฐ
๋น๋๊ธฐ ์ดํฐ๋ ์ดํฐ์ ์ ๋๋ ์ดํฐ๋ ๋๋์ ๋ฐ์ดํฐ๋ฅผ ๋น๋๊ธฐ์ ์ผ๋ก ์ฒ๋ฆฌํ๋ ๋ ํจ์จ์ ์ธ ๋ฐฉ๋ฒ์ ์ ๊ณตํฉ๋๋ค. ๋ฐ์ดํฐ๋ฅผ ์ฒญํฌ(chunk) ๋จ์๋ก ์ฒ๋ฆฌํ ์ ์๊ฒ ํ์ฌ ๋ฉ๋ชจ๋ฆฌ ์๋น๋ฅผ ์ค์ด๊ณ ๋ฐ์์ฑ์ ํฅ์์ํต๋๋ค.
์์ :
async function* generateData() {
for (let i = 0; i < 100; i++) {
await new Promise(resolve => setTimeout(resolve, 10)); // Simulate asynchronous operation
yield i;
}
}
async function processData() {
for await (const item of generateData()) {
console.log(item);
}
}
processData();
์ด ์์ ์์ `generateData` ํจ์๋ ๋ฐ์ดํฐ๋ฅผ ๋น๋๊ธฐ์ ์ผ๋ก ์์ฑ(yield)ํ๋ ๋น๋๊ธฐ ์ ๋๋ ์ดํฐ์ ๋๋ค. `processData` ํจ์๋ `for await...of` ๋ฃจํ๋ฅผ ์ฌ์ฉํ์ฌ ์์ฑ๋ ๋ฐ์ดํฐ๋ฅผ ๋ฐ๋ณตํฉ๋๋ค. ์ด๋ฅผ ํตํด ์ ์ฒด ๋ฐ์ดํฐ์ ์ด ํ ๋ฒ์ ๋ฉ๋ชจ๋ฆฌ์ ๋ก๋๋๋ ๊ฒ์ ๋ฐฉ์งํ๊ณ ๋ฐ์ดํฐ๋ฅผ ์ฒญํฌ ๋จ์๋ก ์ฒ๋ฆฌํ ์ ์์ต๋๋ค.
8. ๋น๋๊ธฐ ์์ ์ค๋กํ๋ง ๋ฐ ๋๋ฐ์ด์ฑ
์ฌ์ฉ์ ์ ๋ ฅ ์ฒ๋ฆฌ๋ API์์ ๋ฐ์ดํฐ ๊ฐ์ ธ์ค๊ธฐ์ ๊ฐ์ด ๋น๋ฒํ ๋น๋๊ธฐ ์์ ์ ๋ค๋ฃฐ ๋, ์ค๋กํ๋ง(throttling)๊ณผ ๋๋ฐ์ด์ฑ(debouncing)์ ๋ฉ๋ชจ๋ฆฌ ์๋น๋ฅผ ์ค์ด๊ณ ์ฑ๋ฅ์ ํฅ์์ํค๋ ๋ฐ ๋์์ด ๋ ์ ์์ต๋๋ค. ์ค๋กํ๋ง์ ํจ์๊ฐ ์คํ๋๋ ๋น๋๋ฅผ ์ ํํ๋ ๋ฐ๋ฉด, ๋๋ฐ์ด์ฑ์ ๋ง์ง๋ง ํธ์ถ ์ดํ ์ผ์ ์๊ฐ์ด ์ง๋ ๋๊น์ง ํจ์ ์คํ์ ์ง์ฐ์ํต๋๋ค.
์์ (๋๋ฐ์ด์ฑ):
function debounce(func, delay) {
let timeoutId;
return function(...args) {
clearTimeout(timeoutId);
timeoutId = setTimeout(() => {
func.apply(this, args);
}, delay);
};
}
function handleInputChange(event) {
console.log('Input changed:', event.target.value);
// Perform asynchronous operation here (e.g., search API call)
}
const debouncedHandleInputChange = debounce(handleInputChange, 300); // Debounce for 300ms
const inputElement = document.getElementById('myInput');
inputElement.addEventListener('input', debouncedHandleInputChange);
์ด ์์ ์์ `debounce` ํจ์๋ `handleInputChange` ํจ์๋ฅผ ๊ฐ์๋๋ค. ๋๋ฐ์ด์ฑ๋ ํจ์๋ 300๋ฐ๋ฆฌ์ด ๋์ ์๋ฌด๋ฐ ํ๋์ด ์์ ๋๋ง ์คํ๋ฉ๋๋ค. ์ด๋ ๊ณผ๋ํ API ํธ์ถ์ ๋ฐฉ์งํ๊ณ ๋ฉ๋ชจ๋ฆฌ ์๋น๋ฅผ ์ค์ ๋๋ค.
9. ๋ผ์ด๋ธ๋ฌ๋ฆฌ ๋๋ ํ๋ ์์ํฌ ์ฌ์ฉ ๊ณ ๋ ค
๋ง์ ์๋ฐ์คํฌ๋ฆฝํธ ๋ผ์ด๋ธ๋ฌ๋ฆฌ์ ํ๋ ์์ํฌ๋ ๋น๋๊ธฐ ์์ ์ ๊ด๋ฆฌํ๊ณ ๋ฉ๋ชจ๋ฆฌ ๋์๋ฅผ ๋ฐฉ์งํ๊ธฐ ์ํ ๋ด์ฅ ๋ฉ์ปค๋์ฆ์ ์ ๊ณตํฉ๋๋ค. ์๋ฅผ ๋ค์ด, ๋ฆฌ์กํธ(React)์ useEffect ํ ์ ์ฌ์ฉํ๋ฉด ๋ถ์ ํจ๊ณผ(side effects)๋ฅผ ์ฝ๊ฒ ๊ด๋ฆฌํ๊ณ ์ปดํฌ๋ํธ๊ฐ ๋ง์ดํธ ํด์ ๋ ๋ ์ ๋ฆฌํ ์ ์์ต๋๋ค. ๋ง์ฐฌ๊ฐ์ง๋ก, ์ต๊ทค๋ฌ(Angular)์ RxJS ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ ๋น๋๊ธฐ ๋ฐ์ดํฐ ์คํธ๋ฆผ์ ์ฒ๋ฆฌํ๊ณ ๊ตฌ๋ ์ ๊ด๋ฆฌํ๊ธฐ ์ํ ๊ฐ๋ ฅํ ์ฐ์ฐ์ ์ธํธ๋ฅผ ์ ๊ณตํฉ๋๋ค.
์์ (React useEffect):
import React, { useState, useEffect } from 'react';
function MyComponent() {
const [data, setData] = useState(null);
useEffect(() => {
let isMounted = true; // Track component mount state
async function fetchData() {
const response = await fetch('https://example.com/api/data');
const result = await response.json();
if (isMounted) {
setData(result);
}
}
fetchData();
return () => {
// Cleanup function
isMounted = false; // Prevent state updates on unmounted component
// Cancel any pending asynchronous operations here
};
}, []); // Empty dependency array means this effect runs only once on mount
return (
{data ? Data: {data.value}
: Loading...
}
);
}
export default MyComponent;
`useEffect` ํ ์ ์ปดํฌ๋ํธ๊ฐ ์ฌ์ ํ ๋ง์ดํธ๋ ๊ฒฝ์ฐ์๋ง ์ํ๋ฅผ ์ ๋ฐ์ดํธํ๋๋ก ๋ณด์ฅํฉ๋๋ค. ์ ๋ฆฌ ํจ์๋ `isMounted`๋ฅผ `false`๋ก ์ค์ ํ์ฌ ์ปดํฌ๋ํธ๊ฐ ๋ง์ดํธ ํด์ ๋ ํ ๋ ์ด์์ ์ํ ์ ๋ฐ์ดํธ๋ฅผ ๋ฐฉ์งํฉ๋๋ค. ์ด๋ ๋น๋๊ธฐ ์์ ์ด ์ปดํฌ๋ํธ๊ฐ ํ๊ดด๋ ํ์ ์๋ฃ๋ ๋ ๋ฐ์ํ ์ ์๋ ๋ฉ๋ชจ๋ฆฌ ๋์๋ฅผ ๋ฐฉ์งํฉ๋๋ค.
๊ฒฐ๋ก
ํจ์จ์ ์ธ ๋ฉ๋ชจ๋ฆฌ ๊ด๋ฆฌ๋ ๊ฒฌ๊ณ ํ๊ณ ํ์ฅ ๊ฐ๋ฅํ ์๋ฐ์คํฌ๋ฆฝํธ ์ ํ๋ฆฌ์ผ์ด์ ์ ๊ตฌ์ถํ๋ ๋ฐ ๋งค์ฐ ์ค์ํ๋ฉฐ, ํนํ ๋น๋๊ธฐ ์์ ์ ๋ค๋ฃฐ ๋ ๋์ฑ ๊ทธ๋ ์ต๋๋ค. ๋น๋๊ธฐ ์ปจํ ์คํธ์ ๋ณต์ก์ฑ์ ์ดํดํ๊ณ , ์ ์ฌ์ ์ธ ๋ฉ๋ชจ๋ฆฌ ๋์๋ฅผ ์๋ณํ๋ฉฐ, ์ด ๊ธ์์ ์ค๋ช ํ ์ต์ ํ ๊ธฐ์ ์ ๊ตฌํํจ์ผ๋ก์จ ์ ํ๋ฆฌ์ผ์ด์ ์ ์ฑ๋ฅ๊ณผ ์์ ์ฑ์ ํฌ๊ฒ ํฅ์์ํฌ ์ ์์ต๋๋ค. ํ๋กํ์ผ๋ง ๋๊ตฌ๋ฅผ ์ฌ์ฉํ๊ณ , ์ฒ ์ ํ ์ฝ๋ ๋ฆฌ๋ทฐ๋ฅผ ์ํํ๋ฉฐ, `WeakRef` ๋ฐ `FinalizationRegistry`์ ๊ฐ์ ์ต์ ์๋ฐ์คํฌ๋ฆฝํธ ๊ธฐ๋ฅ์ ํ์ ํ์ฉํ์ฌ ์ ํ๋ฆฌ์ผ์ด์ ์ด ๋ฉ๋ชจ๋ฆฌ ํจ์จ์ ์ด๊ณ ์ฑ๋ฅ์ด ๋ฐ์ด๋๋๋ก ํ์ญ์์ค.