๐Ÿ”’ 02. ํด๋กœ์ € โ€” ํ•จ์ˆ˜๊ฐ€ ์ฃฝ์–ด๋„ ๊ธฐ์–ตํ•˜๋Š” ๋ณ€์ˆ˜์˜ ๋น„๋ฐ€

2026๋…„ 3์›” 8์ผ ์ˆ˜์ •๋จ

๐Ÿ“‹ ๊ฐœ์š”

ํ•จ์ˆ˜๊ฐ€ ํƒœ์–ด๋‚œ ์Šค์ฝ”ํ”„๋ฅผ ๊ธฐ์–ตํ•˜๋Š” ํด๋กœ์ €์˜ ๋ณธ์งˆ, ์‹ค๋ฌด ํŒจํ„ด(๋ฐ์ดํ„ฐ ์€๋‹‰, ํŒฉํ† ๋ฆฌ ํ•จ์ˆ˜, ๋ฉ”๋ชจ์ด์ œ์ด์…˜)์„ ์™„์ „ ์ •๋ณตํ•ฉ๋‹ˆ๋‹ค.

๐ŸŽฏ ์ด ์„น์…˜์„ ์ฝ๊ณ  ๋‚˜๋ฉด:

  • ํด๋กœ์ €๊ฐ€ ๋‹จ์ˆœํ•œ "ํ•จ์ˆ˜ ์•ˆ์˜ ํ•จ์ˆ˜"๊ฐ€ ์•„๋‹ˆ๋ผ ์Šค์ฝ”ํ”„ ์บก์ฒ˜ ๋ฉ”์ปค๋‹ˆ์ฆ˜์ž„์„ ์„ค๋ช…ํ•  ์ˆ˜ ์žˆ๋‹ค.
  • ๋ฐ์ดํ„ฐ ์€๋‹‰, ํŒฉํ† ๋ฆฌ ํ•จ์ˆ˜, ๋ฉ”๋ชจ์ด์ œ์ด์…˜ ๋“ฑ ์‹ค๋ฌด ํด๋กœ์ € ํŒจํ„ด์„ ์ž‘์„ฑํ•  ์ˆ˜ ์žˆ๋‹ค.
  • ํด๋กœ์ €๋กœ ์ธํ•œ ๋ฉ”๋ชจ๋ฆฌ ๋ˆ„์ˆ˜ ํŒจํ„ด์„ ์ธ์‹ํ•˜๊ณ  ์˜ˆ๋ฐฉํ•  ์ˆ˜ ์žˆ๋‹ค.

๐Ÿ“‹ ๋ชฉ์ฐจ


๐Ÿ“Œ ์ด ๋ฌธ์„œ๋ฅผ ์ฝ๊ธฐ ์ „์—

โฑ๏ธ ์˜ˆ์ƒ ์ฝ๊ธฐ ์‹œ๊ฐ„: 22๋ถ„(์ „์ฒด) / ํ•ต์‹ฌ ํŒŒํŠธ๋งŒ: 13๋ถ„

๐Ÿ—บ๏ธ ์ด ๋ฌธ์„œ์˜ ํ๋ฆ„
[ํด๋กœ์ €์˜ ์ •์˜] โ†’ [๋ ‰์‹œ์ปฌ ํ™˜๊ฒฝ ์ดํ•ด] โ†’ [4๊ฐ€์ง€ ์‹ค๋ฌด ํŒจํ„ด] โ†’ [๋ฉ”๋ชจ๋ฆฌ ๋ˆ„์ˆ˜ ์ฃผ์˜์‚ฌํ•ญ]

๐ŸŽฏ ์ด ๋ฌธ์„œ๋ฅผ ๋‹ค ์ฝ์œผ๋ฉด ํ•  ์ˆ˜ ์žˆ๋Š” ๊ฒƒ

  • "ํด๋กœ์ €๊ฐ€ ๋ญ”๊ฐ€์š”?" ๋ฉด์ ‘ ์งˆ๋ฌธ์— ์ฝ”๋“œ์™€ ํ•จ๊ป˜ ๋‹ตํ•  ์ˆ˜ ์žˆ๋‹ค.
  • ๋ชจ๋“ˆ ํŒจํ„ด์œผ๋กœ ๋ฐ์ดํ„ฐ๋ฅผ ์บก์Аํ™”ํ•  ์ˆ˜ ์žˆ๋‹ค.
  • React์˜ useState, useCallback์ด ํด๋กœ์ €๋ฅผ ๊ธฐ๋ฐ˜์œผ๋กœ ๋™์ž‘ํ•จ์„ ์„ค๋ช…ํ•  ์ˆ˜ ์žˆ๋‹ค.

๐Ÿ—บ๏ธ ์ด ๋ฌธ์„œ์˜ ๋ฐฐ๊ฒฝ ์„ธ๊ณ„๊ด€: '์˜์ˆ˜๋„ค ์ปค๋ฎค๋‹ˆํ‹ฐ'

๐Ÿฃ ์˜์ฒ : "์˜ํ˜ธ ๋‹˜, ์˜ค๋Š˜ ๊ฒŒ์‹œ๊ธ€ ์กฐํšŒ์ˆ˜ ์นด์šดํ„ฐ๋ฅผ ๋งŒ๋“ค๋‹ค๊ฐ€ ์ด์ƒํ•œ ๊ฑธ ๋ฐœ๊ฒฌํ–ˆ์–ด์š”. ์ „์—ญ ๋ณ€์ˆ˜๋กœ viewCount๋ฅผ ์„ ์–ธํ–ˆ๋”๋‹ˆ... ๋‹ค๋ฅธ ํŒŒ์ผ์—์„œ ๋ˆ„๊ฐ€ ๋ง‰ viewCount = 0์œผ๋กœ ์ดˆ๊ธฐํ™”ํ•ด๋ฒ„๋ ธ์–ด์š”. ์ „์—ญ ๋ณ€์ˆ˜๋Š” ์“ฐ์ง€ ๋ง๋ผ๋Š” ๊ฑด ์•Œ๊ฒ ๋Š”๋ฐ, ๊ทธ๋Ÿผ ์นด์šดํ„ฐ ๊ฐ’์„ ์–ด๋””์— ์–ด๋–ป๊ฒŒ ๋ณด๊ด€ํ•ด์•ผ ํ•ด์š”? ํด๋ž˜์Šค ์จ์•ผ ํ•˜๋‚˜์š”?"

๐Ÿฆ ์˜ํ˜ธ: "ํด๋ž˜์Šค๋ณด๋‹ค ๋” ๊ฐ€๋ณ๊ณ  ์šฐ์•„ํ•œ ๋ฐฉ๋ฒ•์ด ์žˆ์–ด. ํด๋กœ์ € ๋ฅผ ์“ฐ๋ฉด ๋ผ. ํ•จ์ˆ˜ ์•ˆ์— ๋ณ€์ˆ˜๋ฅผ ์„ ์–ธํ•˜๊ณ , ๊ทธ ๋ณ€์ˆ˜๋ฅผ ์กฐ์ž‘ํ•˜๋Š” ํ•จ์ˆ˜๋งŒ ๋‚ด๋ณด๋‚ด๋ฉด โ€” ์™ธ๋ถ€์—์„œ ์ง์ ‘ ์ ‘๊ทผํ•˜๋Š” ๊ฑด ๋ถˆ๊ฐ€๋Šฅํ•˜๋ฉด์„œ ์ƒํƒœ๋Š” ๊ณ„์† ์œ ์ง€๋ผ. ์˜ค๋Š˜ ํด๋กœ์ € ํ•˜๋‚˜ ์ œ๋Œ€๋กœ ๋ฐฐ์›Œ๋‘๋ฉด, React useState๊ฐ€ ์™œ ์ €๋ ‡๊ฒŒ ์ƒ๊ฒผ๋Š”์ง€๋„ ์ž๋™์œผ๋กœ ์ดํ•ด๋ผ."


๐Ÿค” 1. ์™œ ์•Œ์•„์•ผ ํ•˜๋Š”๊ฐ€?

์˜ํ˜ธ ๋ฆฌ๋“œ ๋‹˜์ด ์˜์ฒ ์ด์—๊ฒŒ ์ฒ˜์Œ ํด๋กœ์ €๋ฅผ ์„ค๋ช…ํ–ˆ์„ ๋•Œ ๋”ฑ ํ•œ๋งˆ๋””๋กœ ์š”์•ฝํ–ˆ๋‹ค: "ํด๋กœ์ €๊ฐ€ ๋ญ”์ง€ ๋ชจ๋ฅด๋ฉด React useState๊ฐ€ ์™œ ์ €๋ ‡๊ฒŒ ์ƒ๊ฒผ๋Š”์ง€ ํ‰์ƒ ์ดํ•ด ๋ชป ํ•ด." ํด๋กœ์ €๋Š” JS์˜ ๊ฐ€์žฅ ๊ฐ•๋ ฅํ•˜๋ฉด์„œ ๊ฐ€์žฅ ์˜คํ•ด๋ฐ›๋Š” ๊ฐœ๋… ์ด๋‹ค. ๋ฉด์ ‘์—์„œ ๋‹จ๊ณจ๋กœ ๋“ฑ์žฅํ•˜๋Š” ์ด์œ ๊ฐ€ ์žˆ๋‹ค โ€” ํด๋กœ์ €๋ฅผ ์ดํ•ดํ•˜๋ฉด ๋‹ค์Œ ๋ชจ๋“  ๊ฒƒ์ด ์„ค๋ช…๋œ๋‹ค:

  • React useState์˜ ์ƒํƒœ๊ฐ€ ๋ฆฌ๋ Œ๋”๋ง ํ›„์—๋„ ์œ ์ง€๋˜๋Š” ์ด์œ 
  • setTimeout ์ฝœ๋ฐฑ์ด ์™ธ๋ถ€ ๋ณ€์ˆ˜๋ฅผ "๊ธฐ์–ต"ํ•˜๋Š” ์ด์œ 
  • ๋ชจ๋“ˆ ํŒจํ„ด์œผ๋กœ ๋ฐ์ดํ„ฐ๋ฅผ ์บก์Аํ™”ํ•˜๋Š” ๋ฐฉ๋ฒ•
  • useCallback, useMemo๊ฐ€ ์˜์กด์„ฑ ๋ฐฐ์—ด์„ ํ•„์š”๋กœ ํ•˜๋Š” ์ด์œ 

MDN ๊ณต์‹ ์ •์˜: "A closure is the combination of a function bundled together (enclosed) with references to its surrounding state (the lexical environment)." โ€” ํด๋กœ์ €๋Š” ํ•จ์ˆ˜์™€ ๊ทธ ํ•จ์ˆ˜๊ฐ€ ์„ ์–ธ๋์„ ๋•Œ์˜ ๋ ‰์‹œ์ปฌ ํ™˜๊ฒฝ์˜ ์กฐํ•ฉ์ด๋‹ค.


๐Ÿ”ฌ 2. ํด๋กœ์ €์˜ ๋ณธ์งˆ

๋ ‰์‹œ์ปฌ ํ™˜๊ฒฝ๊ณผ ํด๋กœ์ €

์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ์˜ ๋ชจ๋“  ํ•จ์ˆ˜๋Š” ํƒœ์–ด๋‚  ๋•Œ ์ž์‹ ์ด ์„œ ์žˆ๋Š” ๋•…(๋ ‰์‹œ์ปฌ ํ™˜๊ฒฝ)์— ๋Œ€ํ•œ ๊ธฐ์–ต์„ ํ’ˆ๊ณ  ๋‚˜์˜ต๋‹ˆ๋‹ค. ์˜์ฒ ์ด๊ฐ€ ๊ณ ๋ฏผํ•˜๋˜ ์นด์šดํ„ฐ ๋ณ€์ˆ˜ ์˜ค์—ผ ๋ฌธ์ œ๋ฅผ ์ด ๊ธฐ์–ต์„ ํ™œ์šฉํ•ด ์–ด๋–ป๊ฒŒ ํ•ด๊ฒฐํ–ˆ๋Š”์ง€ ๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค.

// ๐Ÿฃ ์˜์ฒ : "์ „์—ญ ๋ณ€์ˆ˜ ๋Œ€์‹  ์ด๋ ‡๊ฒŒ ์งœ๋ฉด ๋˜๋Š” ๊ฑด๊ฐ€์š”?" โ€” ์˜์ˆ˜๋„ค ์ปค๋ฎค๋‹ˆํ‹ฐ ๊ฒŒ์‹œ๊ธ€ ์กฐํšŒ์ˆ˜ ์นด์šดํ„ฐ
 
function createViewCounter() {
  let count = 0; // โ† ์ด ๋ณ€์ˆ˜๊ฐ€ ํด๋กœ์ €์— "์บก์ฒ˜"๋œ๋‹ค
 
  function increment() {
    count++;
    console.log(`์กฐํšŒ์ˆ˜: ${count}`);
  }
 
  function getCount() {
    return count;
  }
 
  return { increment, getCount };
}
 
const postCounter = createViewCounter();
postCounter.increment(); // ์กฐํšŒ์ˆ˜: 1
postCounter.increment(); // ์กฐํšŒ์ˆ˜: 2
postCounter.increment(); // ์กฐํšŒ์ˆ˜: 3
console.log(postCounter.getCount()); // 3
 
// ์™ธ๋ถ€์—์„œ count์— ์ง์ ‘ ์ ‘๊ทผ ๋ถˆ๊ฐ€!
console.log(postCounter.count); // undefined โ€” ์™„๋ฒฝํ•œ ์€๋‹‰

ํ•ต์‹ฌ: createViewCounter()๊ฐ€ ์ด๋ฏธ ๋ฐ˜ํ™˜(์ข…๋ฃŒ) ๋์Œ์—๋„ count๋Š” ์‚ฌ๋ผ์ง€์ง€ ์•Š๋Š”๋‹ค. increment์™€ getCount๊ฐ€ count๋ฅผ ์ฐธ์กฐํ•˜๋Š” ํ•œ, JS ๊ฐ€๋น„์ง€ ์ปฌ๋ ‰ํ„ฐ๋Š” ๊ทธ ๋ ‰์‹œ์ปฌ ํ™˜๊ฒฝ์„ ์ˆ˜๊ฑฐํ•˜์ง€ ์•Š๋Š”๋‹ค.

ํด๋กœ์ €๊ฐ€ ์ƒ์„ฑ๋˜๋Š” ์ˆœ๊ฐ„

ํ•จ์ˆ˜๊ฐ€ ์ด๋ฏธ ์‹คํ–‰์„ ๋งˆ์น˜๊ณ  ์‚ฌ๋ผ์กŒ๋Š”๋ฐ๋„, ๊ทธ ์•ˆ์˜ ์†Œ์ค‘ํ•œ ์ •๋ณด๊ฐ€ ๊ณ„์† ์œ ์ง€๋˜๋Š” ๋งˆ๋ฒ• ๊ฐ™์€ ์ˆœ๊ฐ„์„ ์ฝ”๋“œ๋กœ ์‹œ๊ฐํ™”ํ•ด ๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค.

function outer() {
  const message = "์•ˆ๋…•!"; // outer์˜ ๋ ‰์‹œ์ปฌ ํ™˜๊ฒฝ
 
  function inner() {
    // inner ํ•จ์ˆ˜๋Š” ์ƒ์„ฑ๋  ๋•Œ outer์˜ ๋ ‰์‹œ์ปฌ ํ™˜๊ฒฝ์„ [[Environment]]์— ์ €์žฅ
    console.log(message); // ํด๋กœ์ €๋ฅผ ํ†ตํ•ด ์ ‘๊ทผ
  }
 
  return inner; // inner ํ•จ์ˆ˜ ์ž์ฒด๋ฅผ ๋ฐ˜ํ™˜
}
 
const closureFunc = outer(); // outer ์‹คํ–‰ ์ข…๋ฃŒ โ†’ ์ผ๋ฐ˜์ ์œผ๋กœ message๋Š” ์‚ฌ๋ผ์ ธ์•ผ ํ•จ
closureFunc(); // "์•ˆ๋…•!" โ€” ํ•˜์ง€๋งŒ closureFunc์ด outer์˜ ํ™˜๊ฒฝ์„ ๋“ค๊ณ  ์žˆ์–ด์„œ ์‚ด์•„์žˆ์Œ
 
// ๋ฉ”๋ชจ๋ฆฌ ๊ด€์ :
// outer()๊ฐ€ ์ข…๋ฃŒ๋์ง€๋งŒ, closureFunc([[Environment]])๊ฐ€ outer์˜ ๋ ‰์‹œ์ปฌ ํ™˜๊ฒฝ์„ ์ฐธ์กฐ ์ค‘
// โ†’ GC(๊ฐ€๋น„์ง€ ์ปฌ๋ ‰ํ„ฐ)๊ฐ€ ์ˆ˜๊ฑฐ ๋ถˆ๊ฐ€ โ†’ message๋Š” ๋ฉ”๋ชจ๋ฆฌ์— ์‚ด์•„์žˆ์Œ

๐Ÿ—๏ธ 3. ์‹ค๋ฌด ํด๋กœ์ € ํŒจํ„ด

๋ฐ์ดํ„ฐ ์€๋‹‰ (Private State)

์™ธ๋ถ€์—์„œ ๋ณ€์ˆ˜๋ฅผ ๋งˆ์Œ๋Œ€๋กœ ์ฃผ๋ฌด๋ฅด์ง€ ๋ชปํ•˜๊ฒŒ ๊ฝ๊ฝ ์ˆจ๊ธฐ๊ณ  ์‹ถ์„ ๋•Œ ํด๋กœ์ €๊ฐ€ ํ™œ์•ฝํ•ฉ๋‹ˆ๋‹ค. ์˜์ฒ ์ด๊ฐ€ ์ฒ˜์Œ ์ง  ์ฝ”๋“œ์™€, ์˜ํ˜ธ ๋ฆฌ๋“œ์˜ ์†๊ธธ์ด ๋‹ฟ์€ ์ฝ”๋“œ๋ฅผ ๋น„๊ตํ•ด ๋ณด์„ธ์š”.

// โŒ ์ˆœ์ง„ํ•œ ์ฝ”๋“œ (Naive Approach) โ€” ์ „์—ญ ๋…ธ์ถœ
// ๐Ÿฃ ์˜์ฒ ์˜ ์ดˆ๊ธฐ ์ฝ”๋“œ
let postLikeCount = 0;
 
function like() {
  postLikeCount++;
}
function unlike() {
  postLikeCount--;
}
 
// ๋ฌธ์ œ: ๋ˆ„๊ตฌ๋‚˜ postLikeCount = 9999 ๋กœ ์กฐ์ž‘ ๊ฐ€๋Šฅ
 
// โœ… ํด๋กœ์ €๋กœ ๋ฐ์ดํ„ฐ ์€๋‹‰ (Pro Approach)
// ๐Ÿฆ ์˜ํ˜ธ์˜ ๋ฆฌํŒฉํ† ๋ง
function createLikeManager(initialCount = 0) {
  let _count = initialCount; // ์™ธ๋ถ€ ์ ‘๊ทผ ๋ถˆ๊ฐ€ํ•œ private ๋ณ€์ˆ˜
 
  return {
    like() {
      if (_count >= 9999) return; // ๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง๋„ ๋‚ด๋ถ€์—์„œ ์ฒ˜๋ฆฌ
      _count++;
    },
    unlike() {
      if (_count <= 0) return;
      _count--;
    },
    getCount() {
      return _count;
    },
  };
}
 
const likeManager = createLikeManager(42);
likeManager.like();
likeManager.like();
console.log(likeManager.getCount()); // 44
// likeManager._count ๋Š” undefined โ€” ์™„์ „ ๋ด‰์ธ

ํŒฉํ† ๋ฆฌ ํ•จ์ˆ˜ (Factory Function)

์˜์ˆ˜ PM์ด "๊ฒŒ์‹œ๊ธ€๋งˆ๋‹ค ๋…๋ฆฝ์ ์œผ๋กœ ์นด์šดํŠธ๊ฐ€ ๋Œ์•„๊ฐ€์•ผ ํ•ด์š”"๋ผ๋Š” ๋ฏธ์…˜์„ ์คฌ์„ ๋•Œ, ์˜ํ˜ธ ๋ฆฌ๋“œ ๋‹˜์ด ๊ถŒ์žฅํ•œ ๋ฐฉ์‹์ž…๋‹ˆ๋‹ค. ํ•˜๋‚˜์˜ ์„ค๊ณ„๋„๋กœ ์—ฌ๋Ÿฌ ๊ฐœ์˜ ๋…๋ฆฝ์ ์ธ ๊ฒฐ๊ณผ๋ฌผ์„ ์ฐ์–ด๋‚ผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

// ์˜์ˆ˜๋„ค ์ปค๋ฎค๋‹ˆํ‹ฐ โ€” ๊ฒŒ์‹œ๊ธ€๋ณ„ ๋…๋ฆฝ์ ์ธ ์ƒํƒœ ๊ด€๋ฆฌ
 
function createPostManager(postId) {
  let viewCount = 0;
  let likeCount = 0;
  const comments = [];
 
  return {
    view() {
      viewCount++;
      return viewCount;
    },
    like() {
      likeCount++;
      return likeCount;
    },
    addComment(text) {
      const comment = { id: comments.length + 1, text, postId };
      comments.push(comment);
      return comment;
    },
    getSummary() {
      return { postId, viewCount, likeCount, commentCount: comments.length };
    },
  };
}
 
// ๊ฐ ๊ฒŒ์‹œ๊ธ€๋งˆ๋‹ค ๋…๋ฆฝ์ ์ธ ์ƒํƒœ
const post1 = createPostManager(1);
const post2 = createPostManager(2);
 
post1.view();
post1.view();
post1.like();
post2.view();
 
console.log(post1.getSummary()); // { postId: 1, viewCount: 2, likeCount: 1, commentCount: 0 }
console.log(post2.getSummary()); // { postId: 2, viewCount: 1, likeCount: 0, commentCount: 0 }
// post1๊ณผ post2๋Š” ์„œ๋กœ์˜ ์ƒํƒœ์— ์ „ํ˜€ ์˜ํ–ฅ์„ ์ฃผ์ง€ ์•Š์Œ โœ…

๋ฉ”๋ชจ์ด์ œ์ด์…˜ (Memoization)

์˜์ˆ™ ๋””์ž์ด๋„ˆ๊ฐ€ "๊ฒ€์ƒ‰ํ•  ๋•Œ๋งˆ๋‹ค ๋ฌ˜ํ•˜๊ฒŒ ํ™”๋ฉด์ด ๋Šฆ๊ฒŒ ๋– ์š”"๋ผ๊ณ  ์ œ๋ณดํ•œ ์ƒํ™ฉ์ž…๋‹ˆ๋‹ค. ์˜ํ˜ธ ๋ฆฌ๋“œ ๋‹˜์€ ๊ฐ™์€ ๊ฒฐ๊ณผ๋ฅผ ๊ตณ์ด ๋‘ ๋ฒˆ ๊ณ„์‚ฐํ•˜์ง€ ์•Š๋„๋ก, ํด๋กœ์ €์˜ '๊ธฐ์–ต๋ ฅ'์„ ํ™œ์šฉํ•œ ์บ์‹ฑ ์žฅ์น˜๋ฅผ ๋„์ž…ํ–ˆ์Šต๋‹ˆ๋‹ค.

// ์˜์ˆ˜๋„ค ์ปค๋ฎค๋‹ˆํ‹ฐ โ€” ๊ฒ€์ƒ‰ ํ•„ํ„ฐ ๊ฒฐ๊ณผ ์บ์‹ฑ
 
function memoize(fn) {
  const cache = new Map(); // ํด๋กœ์ €๋กœ ์บก์ฒ˜๋œ ์บ์‹œ
 
  return function (...args) {
    const key = JSON.stringify(args);
 
    if (cache.has(key)) {
      console.log("์บ์‹œ ํžˆํŠธ! ๐ŸŽฏ");
      return cache.get(key);
    }
 
    const result = fn(...args);
    cache.set(key, result);
    return result;
  };
}
 
// ๋ฌด๊ฑฐ์šด ํ•„ํ„ฐ๋ง ํ•จ์ˆ˜ (์‹ค์ œ๋กœ๋Š” ๋ณต์žกํ•œ ์—ฐ์‚ฐ)
const heavyFilter = (posts, keyword) => {
  console.log(`DB ์กฐํšŒ ์ค‘... keyword: ${keyword}`);
  return posts.filter((p) => p.title.includes(keyword));
};
 
const cachedFilter = memoize(heavyFilter);
 
const posts = [
  { id: 1, title: "ํด๋กœ์ € ์™„์ „์ •๋ณต" },
  { id: 2, title: "React ํ›… ์ •๋ณต" },
  { id: 3, title: "ํด๋กœ์ € ์‹ฌํ™”" },
];
 
cachedFilter(posts, "ํด๋กœ์ €"); // DB ์กฐํšŒ ์ค‘...
cachedFilter(posts, "ํด๋กœ์ €"); // ์บ์‹œ ํžˆํŠธ! ๐ŸŽฏ โ€” ์žฌ๊ณ„์‚ฐ ์—†์Œ
cachedFilter(posts, "React");  // DB ์กฐํšŒ ์ค‘...

๋ถ€๋ถ„ ์ ์šฉ ํ•จ์ˆ˜ (Partial Application)

๊ณตํ†ต์œผ๋กœ ๋“ค์–ด๊ฐ€๋Š” ์„ค์ •์„ ๋ฏธ๋ฆฌ ๋”ฑ ๊ณ ์ •ํ•ด๋‘๊ณ , ๋‚˜์ค‘์— ์•Œ๋งน์ด๋งŒ ์™ ๋ฐ”๊ฟ”์„œ ์“ธ ์ˆ˜ ์žˆ๋Š” ํŽธ๋ฆฌํ•œ ๋„๊ตฌ๋ฅผ ๋งŒ๋“ค ๋•Œ๋„ ํด๋กœ์ €๊ฐ€ ์“ฐ์ž…๋‹ˆ๋‹ค.

// ์˜์ˆ˜๋„ค ์ปค๋ฎค๋‹ˆํ‹ฐ โ€” API ํ˜ธ์ถœ ํ—ฌํผ
 
function createApiCaller(baseURL) {
  return function (endpoint, options = {}) {
    return fetch(`${baseURL}${endpoint}`, {
      headers: {
        "Content-Type": "application/json",
        ...options.headers,
      },
      ...options,
    });
  };
}
 
// baseURL์„ ํ•œ ๋ฒˆ๋งŒ ์„ค์ •ํ•˜๊ณ  ์—ฌ๋Ÿฌ API ํ˜ธ์ถœ์— ์žฌ์‚ฌ์šฉ
const devApi = createApiCaller("https://dev-api.youngsu.com");
const prodApi = createApiCaller("https://api.youngsu.com");
 
// ์‹ค์ œ ์‚ฌ์šฉ โ€” ๊ฐ„๊ฒฐํ•˜๊ฒŒ!
devApi("/posts");            // https://dev-api.youngsu.com/posts
devApi("/users/42");         // https://dev-api.youngsu.com/users/42
prodApi("/posts");           // https://api.youngsu.com/posts

โš ๏ธ 4. ํด๋กœ์ € ํ•จ์ •๊ณผ ๋ฉ”๋ชจ๋ฆฌ ๋ˆ„์ˆ˜

ํด๋กœ์ €์˜ '๊ธฐ์–ต๋ ฅ'์ด ๋„ˆ๋ฌด ์ข‹์œผ๋ฉด ๋ฌธ์ œ๊ฐ€ ๋ฉ๋‹ˆ๋‹ค. ์ด๋ฏธ ์žŠ์–ด์•ผ ํ•  ๊ฑฐ๋Œ€ํ•œ ๋ฐ์ดํ„ฐ๊นŒ์ง€ ํ‰์ƒ ๋ถ™๋“ค๊ณ  ์žˆ์œผ๋ฉด ๋ฉ”๋ชจ๋ฆฌ๊ฐ€ ๋ถ€์กฑํ•ด์ง€๊ฑฐ๋“ ์š”. ์˜ํ˜ธ ๋ฆฌ๋“œ ๋‹˜์ด ์˜์ฒ ์ด์˜ ์ฝ”๋“œ์—์„œ ๋ฐœ๊ฒฌํ•œ ์œ„ํ—˜ํ•œ ํŒจํ„ด์„ ์‚ดํŽด๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค.

// โŒ ๋ฉ”๋ชจ๋ฆฌ ๋ˆ„์ˆ˜ ํŒจํ„ด
// ๐Ÿฃ ์˜์ฒ ์˜ ์ฝ”๋“œ โ€” ์ด๋ฒคํŠธ ๋ฆฌ์Šค๋„ˆ ์•ˆ์—์„œ ๊ฑฐ๋Œ€ํ•œ ๋ฐ์ดํ„ฐ๋ฅผ ํด๋กœ์ €๋กœ ์บก์ฒ˜
 
function setupUI() {
  const hugeUserList = fetchAllUsers(); // ์ˆ˜๋งŒ ๋ช…์˜ ์œ ์ € ๋ฐ์ดํ„ฐ (์ˆ˜ MB)
 
  document.getElementById("search-btn").addEventListener("click", function () {
    // hugeUserList ์ „์ฒด๋ฅผ ํด๋กœ์ €๋กœ ์ฐธ์กฐ
    const result = hugeUserList.filter(/* ... */);
    renderResult(result);
  });
 
  // setupUI๊ฐ€ ๋๋‚œ ํ›„์—๋„:
  // - click ์ด๋ฒคํŠธ ๋ฆฌ์Šค๋„ˆ๊ฐ€ ์‚ด์•„์žˆ๊ณ 
  // - ๊ทธ ๋ฆฌ์Šค๋„ˆ๊ฐ€ hugeUserList๋ฅผ ์ฐธ์กฐํ•˜๋ฏ€๋กœ
  // - hugeUserList๋Š” GC ์ˆ˜๊ฑฐ ๋ถˆ๊ฐ€ โ†’ ๋ฉ”๋ชจ๋ฆฌ ๋ˆ„์ˆ˜
}
 
// โœ… ํ•ด๊ฒฐ์ฑ… โ€” ํ•„์š”ํ•œ ๋ฐ์ดํ„ฐ๋งŒ ํด๋กœ์ €์— ๊ฐ€๋‘ฌ๋ผ
function setupUI() {
  const hugeUserList = fetchAllUsers();
  const processedList = preprocessForSearch(hugeUserList); // ๊ฒฝ๋Ÿ‰ํ™”๋œ ๋ฐ์ดํ„ฐ๋งŒ ์ถ”์ถœ
  hugeUserList = null; // ์›๋ณธ ์ฐธ์กฐ ํ•ด์ œ โ†’ GC ๊ฐ€๋Šฅ
 
  document.getElementById("search-btn").addEventListener("click", function () {
    const result = processedList.filter(/* ... */); // ๊ฒฝ๋Ÿ‰ ๋ฐ์ดํ„ฐ๋งŒ ์ฐธ์กฐ
    renderResult(result);
  });
}

ํด๋กœ์ € ํ•จ์ • ์ฒดํฌ๋ฆฌ์ŠคํŠธ:

  • ๊ฑฐ๋Œ€ํ•œ ๋ฐ์ดํ„ฐ๋ฅผ ํด๋กœ์ €๋กœ ์บก์ฒ˜ํ•˜๊ณ  ์žˆ์ง€๋Š” ์•Š์€๊ฐ€?
  • ์ด๋ฒคํŠธ ๋ฆฌ์Šค๋„ˆ๊ฐ€ ํ•ด์ œ(removeEventListener)๋˜์ง€ ์•Š๊ณ  ๊ณ„์† ์‚ด์•„์žˆ์ง€๋Š” ์•Š์€๊ฐ€?
  • setInterval ์ฝœ๋ฐฑ ์•ˆ์—์„œ ํด๋กœ์ €๋กœ ๊ฐ์ฒด๋ฅผ ์ฐธ์กฐํ•˜๊ณ  ์žˆ์ง€๋Š” ์•Š์€๊ฐ€?
  • ์ปดํฌ๋„ŒํŠธ๊ฐ€ ์–ธ๋งˆ์šดํŠธ๋๋Š”๋ฐ ํด๋กœ์ €๊ฐ€ ๊ทธ ์ƒํƒœ๋ฅผ ์•„์ง ์ฐธ์กฐํ•˜๊ณ  ์žˆ์ง€๋Š” ์•Š์€๊ฐ€?

๐Ÿ“ ๋งˆ๋ฌด๋ฆฌ ํ€ด์ฆˆ

Q1. ์•„๋ž˜ ์ฝ”๋“œ์˜ ์ถœ๋ ฅ ๊ฒฐ๊ณผ๋ฅผ ์˜ˆ์ธกํ•˜๊ณ  ์ด์œ ๋ฅผ ์„ค๋ช…ํ•˜๋ผ.

function makeAdder(x) {
  return function (y) {
    return x + y;
  };
}
 
const add5 = makeAdder(5);
const add10 = makeAdder(10);
 
console.log(add5(3));  // ?
console.log(add10(3)); // ?
console.log(add5(10)); // ?

โœ… ์ •๋‹ต: 8, 13, 15

๐Ÿ’ก ์ƒ์„ธ ํ•ด์„ค:

  • makeAdder(5) ํ˜ธ์ถœ ์‹œ, ๋‚ด๋ถ€ ํ•จ์ˆ˜(ํด๋กœ์ €)๊ฐ€ x = 5์ธ ๋ ‰์‹œ์ปฌ ํ™˜๊ฒฝ์„ ์บก์ฒ˜ํ•˜์—ฌ add5์— ๋ฐ˜ํ™˜๋œ๋‹ค.
  • makeAdder(10) ํ˜ธ์ถœ ์‹œ, ๋…๋ฆฝ์ ์ธ ๋‹ค๋ฅธ ๋ ‰์‹œ์ปฌ ํ™˜๊ฒฝ ์—์„œ x = 10์„ ์บก์ฒ˜ํ•œ ํด๋กœ์ €๊ฐ€ add10์— ์ €์žฅ๋œ๋‹ค.
  • add5์™€ add10์€ ๊ฐ์ž์˜ x๋ฅผ ๋ณ„๋„๋กœ ๊ธฐ์–ต ํ•˜๋ฏ€๋กœ ์„œ๋กœ์—๊ฒŒ ์˜ํ–ฅ์„ ์ฃผ์ง€ ์•Š๋Š”๋‹ค.
  • ๐Ÿ“Œ ํ•ต์‹ฌ ๊ธฐ์–ต๋ฒ•: "ํŒฉํ† ๋ฆฌ ํ•จ์ˆ˜๋ฅผ ํ•œ ๋ฒˆ ํ˜ธ์ถœํ•  ๋•Œ๋งˆ๋‹ค ์ƒˆ๋กœ์šด ๋ ‰์‹œ์ปฌ ํ™˜๊ฒฝ ์ด ์ƒ์„ฑ๋˜๊ณ , ๋ฐ˜ํ™˜๋œ ํด๋กœ์ €๋Š” ๊ทธ ํ™˜๊ฒฝ์„ ๋…์ ํ•œ๋‹ค."

Q2. ํด๋กœ์ €์™€ React useState์˜ ๊ด€๊ณ„๋ฅผ ์„ค๋ช…ํ•˜๋ผ.

โœ… ์ •๋‹ต: useState๋Š” ํด๋กœ์ €๋ฅผ ์‚ฌ์šฉํ•ด ์ƒํƒœ(state)๋ฅผ ์ปดํฌ๋„ŒํŠธ ํ•จ์ˆ˜์˜ ์™ธ๋ถ€์—์„œ ๋ณด๊ด€ํ•˜๊ณ , setter ํ•จ์ˆ˜๋ฅผ ํ†ตํ•ด์„œ๋งŒ ์ ‘๊ทผํ•˜๊ฒŒ ํ•œ๋‹ค.

๐Ÿ’ก ์ƒ์„ธ ํ•ด์„ค:

  • React์˜ useState๋ฅผ ๊ทน๋„๋กœ ๋‹จ์ˆœํ™”ํ•˜๋ฉด ์•„๋ž˜์™€ ๊ฐ™๋‹ค:
    function useState(initialValue) {
      let _state = initialValue; // React ๋‚ด๋ถ€์— ๋ณด๊ด€ (ํด๋กœ์ €๋กœ ์บก์ฒ˜)
     
      function setState(newValue) {
        _state = newValue;
        rerender(); // ๋ฆฌ๋ Œ๋” ํŠธ๋ฆฌ๊ฑฐ
      }
     
      return [_state, setState]; // ํ˜„์žฌ ๊ฐ’๊ณผ setter๋งŒ ๋…ธ์ถœ
    }
  • ์ปดํฌ๋„ŒํŠธ๊ฐ€ ๋ฆฌ๋ Œ๋”๋  ๋•Œ๋งˆ๋‹ค useState๊ฐ€ ๋‹ค์‹œ ํ˜ธ์ถœ๋˜์ง€๋งŒ, React๋Š” ๋‚ด๋ถ€์ ์œผ๋กœ ๋™์ผํ•œ _state ๊ฐ’์„ ์œ ์ง€ํ•œ๋‹ค. ์ด๊ฒƒ์ด ํด๋กœ์ €(๋ ‰์‹œ์ปฌ ํ™˜๊ฒฝ ์บก์ฒ˜) ๋•๋ถ„์— ๊ฐ€๋Šฅํ•˜๋‹ค.
  • ๐Ÿ“Œ ํ•ต์‹ฌ ๊ธฐ์–ต๋ฒ•: "React์˜ ์ƒํƒœ๋Š” ์ปดํฌ๋„ŒํŠธ ํ•จ์ˆ˜ ๋ฐ–์—์„œ ํด๋กœ์ €๋กœ ๋ณด๊ด€ ๋˜๊ณ , setState๋ผ๋Š” ์ฐฝ๊ตฌ๋ฅผ ํ†ตํ•ด์„œ๋งŒ ๋ณ€๊ฒฝ๋œ๋‹ค."

Q3. ์˜์ฒ ์ด์˜ ํ…Œ์ŠคํŠธ ํƒ€์ž„ โ€” ์‹œ๋‹ˆ์–ด ๋ฉด์ ‘ ๋„์ „

์˜์ฒ ์ด๊ฐ€ ์ด์ง์„ ์œ„ํ•ด ์Šคํƒ€ํŠธ์—… ๋ฉด์ ‘์„ ๋ณด๋Ÿฌ ๊ฐ”๋‹ค. ๋ฉด์ ‘๊ด€์ด ๋ฌป๋Š”๋‹ค.

"ํด๋กœ์ €๋กœ ์ธํ•œ ๋ฉ”๋ชจ๋ฆฌ ๋ˆ„์ˆ˜๊ฐ€ ๋ฐœ์ƒํ•˜๋Š” ์ƒํ™ฉ๊ณผ, ๊ทธ ํ•ด๊ฒฐ ๋ฐฉ๋ฒ•์„ ์„ค๋ช…ํ•ด์ฃผ์„ธ์š”."

โœ… ์ •๋‹ต: ํด๋กœ์ €๊ฐ€ ๋ถˆํ•„์š”ํ•˜๊ฒŒ ์˜ค๋ž˜ ์‚ด์•„์žˆ๋Š” ๋Œ€์šฉ๋Ÿ‰ ๊ฐ์ฒด๋ฅผ ์ฐธ์กฐ ํ•  ๋•Œ ๋ฉ”๋ชจ๋ฆฌ ๋ˆ„์ˆ˜๊ฐ€ ๋ฐœ์ƒํ•œ๋‹ค. ํ•ด๊ฒฐ์ฑ…์€ ์ฐธ์กฐ๋ฅผ ๋ช…์‹œ์ ์œผ๋กœ ๋Š๊ฑฐ๋‚˜(null ํ• ๋‹น), ์ด๋ฒคํŠธ ๋ฆฌ์Šค๋„ˆ๋ฅผ ํ•ด์ œํ•˜๊ฑฐ๋‚˜, ํ•„์š”ํ•œ ๋ฐ์ดํ„ฐ๋งŒ ์ถ”์ถœํ•ด ๊ฒฝ๋Ÿ‰ํ™”ํ•˜๋Š” ๊ฒƒ์ด๋‹ค.

๐Ÿ’ก ์ƒ์„ธ ํ•ด์„ค:

  • ๋ˆ„์ˆ˜ ๋ฐœ์ƒ ์กฐ๊ฑด: โ‘  ํด๋กœ์ €(๋‚ด๋ถ€ ํ•จ์ˆ˜)๊ฐ€ ์‚ด์•„์žˆ๊ณ  โ‘ก ๊ทธ ํด๋กœ์ €๊ฐ€ ํฐ ๋ฐ์ดํ„ฐ๋ฅผ ์ฐธ์กฐํ•˜๋ฉฐ โ‘ข ๊ทธ ๋ฐ์ดํ„ฐ๊ฐ€ ๋” ์ด์ƒ ํ•„์š” ์—†๋Š”๋ฐ๋„ GC๊ฐ€ ์ˆ˜๊ฑฐํ•˜์ง€ ๋ชปํ•˜๋Š” ์ƒํ™ฉ.
  • ์‹ค๋ฌด ์‚ฌ๋ก€: ์ด๋ฒคํŠธ ๋ฆฌ์Šค๋„ˆ, setInterval, ์ „์—ญ ๋ณ€์ˆ˜์— ์ €์žฅ๋œ ํด๋กœ์ € ๋“ฑ.
  • ํ•ด๊ฒฐ ์ „๋žต:
    1. ๋ถˆํ•„์š”ํ•ด์ง„ ์ด๋ฒคํŠธ ๋ฆฌ์Šค๋„ˆ๋Š” removeEventListener๋กœ ์ œ๊ฑฐ
    2. setInterval์€ clearInterval๋กœ ํ•ด์ œ
    3. ํด๋กœ์ €์— ํฌํ•จ๋  ๋ฐ์ดํ„ฐ๋Š” ํ•„์š”ํ•œ ๋ถ€๋ถ„๋งŒ ์ถ”์ถœํ•ด ์บก์ฒ˜
    4. React์—์„œ๋Š” useEffect cleanup ํ•จ์ˆ˜๋กœ ๋ฆฌ์Šค๋„ˆ ์ •๋ฆฌ
  • ๐Ÿ“Œ ํ•ต์‹ฌ ๊ธฐ์–ต๋ฒ•: "ํด๋กœ์ €๊ฐ€ ๊ธฐ์–ตํ•˜๋Š” ๊ฑด ๊ฐ’์ด ์•„๋‹ˆ๋ผ ์ฐธ์กฐ์ž…๋‹ˆ๋‹ค. ์ฐธ์กฐ๊ฐ€ ๋Š์–ด์ง€์ง€ ์•Š๋Š” ํ•œ ๊ฐ€๋น„์ง€ ์ปฌ๋ ‰ํ„ฐ๋„ ์–ด์ฐŒํ•  ์ˆ˜ ์—†์œผ๋‹ˆ, ๋ถˆํ•„์š”ํ•ด์ง„ ์ฐธ์กฐ๋Š” ์ ๊ทน์ ์œผ๋กœ ๋Š์–ด์ฃผ์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค."

๐Ÿฃ ์˜์ฒ ์ด์˜ ํ‡ด๊ทผ ์ผ๊ธฐ

์˜ค๋Š˜ ์ ์‹ฌ์— ๋ญ˜ ๋จน์—ˆ๋Š”์ง€๋„ ๊ธฐ์–ต ์•ˆ ๋‚˜๋Š”๋ฐ ํด๋กœ์ €๋Š” ๋จธ๋ฆฟ์†์— ์„ ๋ช…ํ•˜๊ฒŒ ๋‚จ์•„์žˆ๋‹ค. ์‹ ๊ธฐํ•˜๋‹ค. ์•„๋งˆ ์˜ํ˜ธ ๋‹˜์ด "ํ•จ์ˆ˜๊ฐ€ ํƒœ์–ด๋‚œ ๊ณณ์„ ๊ธฐ์–ตํ•œ๋‹ค"๋Š” ๋ง์„ ๋”ฑ ํ•œ ๋งˆ๋””๋กœ ์ •๋ฆฌํ•ด์ค˜์„œ ๊ทธ๋Ÿฐ ๊ฒƒ ๊ฐ™๋‹ค.

์‚ฌ์‹ค ๋‚˜๋Š” ๊ทธ๋™์•ˆ ํด๋กœ์ €๋ฅผ ์“ฐ๋ฉด์„œ๋„ ํด๋กœ์ €์ธ์ง€ ๋ชฐ๋ž๋‹ค. setTimeout ์•ˆ์—์„œ ์™ธ๋ถ€ ๋ณ€์ˆ˜ ์“ฐ๋Š” ๊ฑฐ, ์ด๋ฒคํŠธ ํ•ธ๋“ค๋Ÿฌ์—์„œ ์นด์šดํ„ฐ ๋ณ€์ˆ˜ ์ ‘๊ทผํ•˜๋Š” ๊ฑฐ โ€” ์ „๋ถ€ ํด๋กœ์ €์˜€๋‹ค. ๊ฐœ๋…์€ ์ด๋ฏธ ์†์— ์ต์–ด์žˆ์—ˆ๋Š”๋ฐ ์ด๋ฆ„๋งŒ ๋ชฐ๋ž๋˜ ๊ฑฐ๋‹ค.

๐Ÿ’ก ์˜ค๋Š˜์˜ ๊ตํ›ˆ: "ํด๋กœ์ €๋Š” 'ํ•จ์ˆ˜ ์•ˆ์˜ ํ•จ์ˆ˜'๊ฐ€ ์•„๋‹ˆ๋ผ, ํ•จ์ˆ˜๊ฐ€ ํƒœ์–ด๋‚œ ํ™˜๊ฒฝ์„ ๋“ค๊ณ  ๋‹ค๋‹ˆ๋Š” ๊ฒƒ์ด๋‹ค. ๊ทธ ํ™˜๊ฒฝ์ด ์‚ด์•„์žˆ๋Š” ํ•œ, ๊ทธ ์•ˆ์˜ ๋ณ€์ˆ˜๋„ ์‚ด์•„์žˆ๋‹ค."

๋‚ด์ผ์€ this ๋ฐ”์ธ๋”ฉ ๊ณต๋ถ€ํ•ด์•ผ ํ•˜๋Š”๋ฐ... ์†”์งํžˆ ์กฐ๊ธˆ ๋ฌด์„ญ๋‹ค. ์˜ํ˜ธ ๋‹˜์ด "this๋Š” ํ™”์‚ดํ‘œ ํ•จ์ˆ˜๋ž‘ ์ผ๋ฐ˜ ํ•จ์ˆ˜๊ฐ€ ์™„์ „ํžˆ ๋‹ฌ๋ผ" ๋ผ๊ณ  ๊ท€๋”ํ•ด์คฌ๋Š”๋ฐ, ๋ถ„๋ช… ๋‚ด ์˜ˆ์ƒ์„ ๊นจ๋ถ€์ˆ˜๋Š” ๋‚ด์šฉ์ด ๋‚˜์˜ฌ ๊ฒƒ ๊ฐ™๋‹ค. ํ—ฌ์Šค์žฅ์—์„œ ์šด๋™ํ•˜๋ฉด์„œ ๊ฐ์˜ค ์ข€ ๋‹ค์ง€๊ณ  ์ž์•ผ๊ฒ ๋‹ค.


๐Ÿ”— ๋” ์•Œ์•„๋ณด๊ธฐ