๐Ÿ“– ํด๋กœ์ €(Closure)

๊ธฐ๋ก์ผ๊ธฐ๐Ÿ“ซยท2021๋…„ 1์›” 16์ผ
1

Javascript ๊ฐœ๋…์ •๋ฆฌ

๋ชฉ๋ก ๋ณด๊ธฐ
12/15

์ด๋ฒˆ ํฌ์ŠคํŒ…์€ ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ ํด๋กœ์ €์— ๋Œ€ํ•ด์„œ ์ •๋ฆฌํ•ด๋ณด์ž.

ํด๋กœ์ €๋ž€?

ํด๋กœ์ €๋Š” ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ ๋ฟ๋งŒ ์•„๋‹ˆ๋ผ ํ•จ์ˆ˜๋ฅผ ์ผ๊ธ‰๊ฐ์ฒด๋กœ ์ทจ๊ธ‰ํ•˜๋Š” ํ•จ์ˆ˜ํ˜• ํ”„๋กœ๊ทธ๋ž˜๋ฐ ์–ธ์–ด์—์„œ ๋งŽ์ด ์“ฐ์ด๋Š” ํŠน์„ฑ์ด๋‹ค.

๋”ฐ๋ผ์„œ EcmaScript ์‚ฌ์–‘์—๋Š” ๋“ฑ์žฅํ•˜์ง€ ์•Š์œผ๋ฉฐ MDN์—์„œ๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์ด ์„ค๋ช…์ด ๋˜์–ด์žˆ๋‹ค.

A Closure is the combination of a function and the lexical environment within which that function was declared

ํด๋กœ์ €๋Š” ํ•จ์ˆ˜๊ฐ€ ์„ ์–ธ๋œ ๋ ‰์‹œ์ปฌ ํ™˜๊ฒฝ์— ์—ฐ๊ด€๋˜์—ˆ๋‹ค๊ณ  ์ ํ˜€์žˆ๋Š”๋ฐ, ์ผ๋‹จ ๋ ‰์‹œ์ปฌ ํ™˜๊ฒฝ์ด ๋ฌด์—‡์ธ์ง€ ์‚ดํŽด๋ณด์ž!


๋ ‰์‹œ์ปฌ ์Šค์ฝ”ํ”„

๋ ‰์‹œ์ปฌ ์Šค์ฝ”ํ”„๋ž€ ํ•จ์ˆ˜์˜ ์ƒ์œ„ ์Šค์ฝ”ํ”„๋ฅผ ํ•จ์ˆ˜๋ฅผ ์–ด๋””์„œ ํ˜ธ์ถœํ•˜๋Š”์ง€๋ณด๋‹ค ์–ด๋””์„œ ์„ ์–ธ์„ ํ–ˆ๋Š”์ง€์— ๋”ฐ๋ผ ๊ฒฐ์ •ํ•˜๋Š” ๊ฒƒ์ด๋‹ค.

๋‹ค์Œ ์ฝ”๋“œ๋ฅผ ๋ณด๊ณ  ์‹คํ–‰๊ฒฐ๊ณผ๋ฅผ ์˜ˆ์ธกํ•ด๋ณด์ž. ๐Ÿ‘€


const x = 1;
function outerFunc() {
  const x = 10;
  innerFunc()
}

function innerFunc() {
  console.log(x)
}

outerFunc()


๋งŒ์•ฝ ์กฐ๊ธˆ ํ—ท๊ฐˆ๋ฆฐ๋‹ค๋ฉด, ์•„์ง ๋ ‰์‹œ์ปฌ ์Šค์ฝ”ํ”„์— ๋Œ€ํ•ด ์ œ๋Œ€๋กœ ์ดํ•ดํ•˜์ง€ ๋ชปํ•œ๊ฒƒ์ด๋‹ค! ์กฐ๊ธˆ์ด๋ผ๋„ ํ—ท๊ฐˆ๋ฆฐ๋‹ค๋ฉด ์‹คํ–‰ ์ปจํ…์ŠคํŠธ ํฌ์ŠคํŒ…์„ ์ฐธ์กฐํ•˜๋Š” ๊ฒƒ์„ ์ถ”์ฒœํ•œ๋‹ค.

innerFunc๊ฐ€ ์„ ์–ธ๋œ ๊ณณ์€ outerFunc ๋‚ด๋ถ€๊ฐ€ ์•„๋‹ˆ๋ผ ์™ธ๋ถ€์ด๊ธฐ ๋•Œ๋ฌธ์—, outerFunc์—์„œ ๋งŒ๋“  ๋ณ€์ˆ˜ x๋Š” outerFunc์—์„œ๋Š” ๋ณด์ด์ง€ ์•Š๋Š”๋‹ค. ๋”ฐ๋ผ์„œ ์ „์—ญ ์Šค์ฝ”ํ”„์— ์žˆ๋Š” x๋ฅผ ์ฐธ์กฐํ•˜๊ฒŒ ๋˜๋ฉฐ, ์ถœ๋ ฅ ๊ฒฐ๊ณผ๋Š” 1์ด ๋‚˜์˜จ๋‹ค.


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

๊ทธ๋Ÿฌ๋ฉด ํด๋กœ์ €์™€ ๋ ‰์‹œ์ปฌ ํ™˜๊ฒฝ์€ ์–ด๋–ค ๊ด€๊ณ„๊ฐ€ ์žˆ์„๊นŒ?

const x = 1

function outer() {
  const x = 10;
  const inner = function() {console.log(x)}
  return inner
}

const innerFunc = outer();
innerFunc()

์œ„ ์ฝ”๋“œ๋ฅผ ์‚ดํŽด๋ณด๋ฉด innerfunc์—๋Š” outerํ•จ์ˆ˜ ๋‚ด์—์„œ ์ƒ์„ฑ๋œ inner ํ•จ์ˆ˜๊ฐ€ return๋œ๋‹ค. ๊ทธ๋ฆฌ๊ณ  outer ํ•จ์ˆ˜ ์ž์ฒด๋Š” stack์—์„œ ์ œ๊ฑฐ๋˜์ง€๋งŒ, inner ํ•จ์ˆ˜๋Š” ๊ณ„์†ํ•ด์„œ outer ํ•จ์ˆ˜์˜ ๋ณ€์ˆ˜์ธ x๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค.

์•ž์—์„œ ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ๋Š” ๋ ‰์‹œ์ปฌ ์Šค์ฝ”ํ”„๋ฅผ ๋”ฐ๋ฅธ๋‹ค๊ณ  ์ด์•ผ๊ธฐํ–ˆ๋‹ค. ๋”ฐ๋ผ์„œ ํ•จ์ˆ˜๋Š” ์ฒ˜์Œ ์ƒ์„ฑ๋ ๋•Œ ์„ ์–ธ๋œ ๊ณณ์˜ ์ƒ์œ„ ์ปจํ…์ŠคํŠธ๋ฅผ ๊ธฐ์–ตํ•˜๊ฒŒ ๋œ๋‹ค.

์œ„์˜ ์˜ˆ์‹œ๋ฅผ ๋ณด๋ฉด inner ํ•จ์ˆ˜๋Š” outer ํ•จ์ˆ˜ ์•ˆ์— ์„ ์–ธ์ด ๋˜์—ˆ๊ธฐ ๋•Œ๋ฌธ์—, ์ƒ์„ฑ๋ ๋•Œ์˜ ์ƒ์œ„ ์Šค์ฝ”ํ”„์ธ outer ํ•จ์ˆ˜๋ฅผ ๊ธฐ์–ตํ•œ๋‹ค.

๋”ฐ๋ผ์„œ outer๊ฐ€ ์‹คํ–‰์ด ์™„๋ฃŒ๋˜์–ด ์Šคํƒ์—์„œ ์ œ๊ฑฐ๋œ ํ›„์—๋„ ๋ณ€์ˆ˜ x์˜ ๊ฐ’์„ ์–ธ์ œ๋“  ์ฐธ์กฐํ•˜์—ฌ ๊ฐ€์ ธ์˜ฌ ์ˆ˜ ์žˆ๋‹ค.

์ด๋Ÿฌํ•œ ๊ฐœ๋…์„ ํด๋กœ์ €๋ผ๊ณ  ํ•œ๋‹ค.

ํด๋กœ์ €์˜ ํ™œ์šฉ

๊ทธ๋ ‡๋‹ค๋ฉด ํด๋กœ์ €๋Š” ์–ด๋””์— ํ™œ์šฉ๋  ์ˆ˜ ์žˆ์„๊นŒ? ํด๋กœ์ €๋Š” ์ƒํƒœ๋ฅผ ์•ˆ์ „ํ•˜๊ฒŒ ๋ณ€๊ฒฝํ•˜๊ณ  ์œ ์ง€์‹œํ‚ค๊ธฐ ์œ„ํ•ด ์‚ฌ์šฉ๋œ๋‹ค.

๋‹ค์Œ ์ฝ”๋“œ๋ฅผ ๋ณด์ž.

let num = 0;
const add = function() {
  return ++num;
}

console.log(add())
console.log(add())
console.log(add())

num์„ 1์”ฉ ์ฆ๊ฐ€์‹œํ‚ค๋Š” ์ฝ”๋“œ์ด๋‹ค. ์ด ์ฝ”๋“œ๋Š” ๊ทธ๋Ÿญ์ €๋Ÿญ ์ž˜ ๋™์ž‘ ํ•˜์ง€๋งŒ ์‚ฌ์‹ค ์—ฌ๋Ÿฌ๊ฐ€์ง€ ๋ฌธ์ œ๋ฅผ ๋ฐœ์ƒ์‹œํ‚ฌ ์ˆ˜ ์žˆ๋‹ค.

์ „์—ญ์— num ๋ณ€์ˆ˜๊ฐ€ ์„ ์–ธ๋˜์–ด์žˆ์–ด์„œ ๋‹ค๋ฅธ ํ•จ์ˆ˜์—์„œ ํ•ด๋‹น ๊ฐ’์„ ๋ณ€๊ฒฝ์‹œํ‚ค๋Š” ๊ฒฝ์šฐ state์ด ๋ฐ”๋€Œ์–ด ๋ฌธ์ œ๋ฅผ ์ผ์œผํ‚ฌ ์ˆ˜ ์žˆ๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค.

์ด๋Ÿฐ ๋ฌธ์ œ์ ์„ ํ•ด๊ฒฐํ•˜๊ธฐ ์œ„ํ•ด ํด๋กœ์ €๋ฅผ ํ™œ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค.

const add = (function() {
  let num = 0;
//ํด๋กœ์ € ํ™œ์šฉ
  return function() {
    // num ์„ 1 ๋งŒํผ ์ฆ๊ฐ€์‹œํ‚จ๋‹ค
    return ++num;
  }
}())

console.log(add())
console.log(add()) 
console.log(add())

์ด๋ ‡๊ฒŒ add๋ผ๋Š” ํ•จ์ˆ˜ ์•ˆ์— num ๋ณ€์ˆ˜๋ฅผ ์ž…๋ ฅํ•ด์ฃผ๊ณ , ํ•ด๋‹น ํ•จ์ˆ˜๊ฐ€ ์ƒ์œ„ ํ•จ์ˆ˜ add ์•ˆ์—์„œ ์„ ์–ธ์ด ๋˜๊ฒŒ ํ•จ์œผ๋กœ์จ ํ•ด๋‹น ํ•จ์ˆ˜๊ฐ€ num ๊ฐ’์˜ ์ƒํƒœ๋ฅผ ์œ ์ง€ํ•˜๊ฒŒ ํ•˜์—ฌ ํ™œ์šฉ์„ ํ•  ์ˆ˜ ์žˆ๋‹ค.

๊ทธ๋Ÿผ ๋”ํ•˜๊ธฐ์— ์ด์–ด์„œ ๋นผ๊ธฐ ๊ธฐ๋Šฅ๋„ ๊ตฌํ˜„ํ•ด๋ณด์ž.

const counter = (function () {
  let num = 0;

  return {
    add() {
      return ++num;
    },
    subtract() {
      return num > 0 ? --num : 0;
    },
  };
})();

console.log(counter.subtract());
console.log(counter.add());
console.log(counter.add());
console.log(counter.add());
console.log(counter.add());
console.log(counter.subtract());

counter์—๋Š” ์ฆ‰์‹œ ์‹คํ–‰ํ•จ์ˆ˜๊ฐ€ ์ƒ์„ฑํ•œ add์™€ subtract๋ฅผ ๋ฉ”์„œ๋“œ๋ฅผ ๊ฐ€์ง„ ๊ฐ์ฒด๊ฐ€ return๋œ๋‹ค. ์—ฌ๊ธฐ์„œ ์ค‘์š”ํ•œ ์ ์€ num ๋ณ€์ˆ˜๋Š” add์™€ subtract๋งŒ์ด ์ ‘๊ทผํ•  ์ˆ˜ ์žˆ๋‹ค๋Š” ๊ฒƒ์ด๋‹ค.

๋ณ€์ˆ˜๊ฐ’์€ ์–ธ์ œ๋“  ๋ณ€๊ฒฝ๋  ์ˆ˜ ์žˆ๊ธฐ ๋•Œ๋ฌธ์— ํ”„๋กœ๊ทธ๋žจ์˜ ์•ˆ์ •์„ฑ์„ ๋†’์ด๊ธฐ ์œ„ํ•ด์„œ๋Š” ํด๋กœ์ €๋ฅผ ์ ๊ทน์ ์œผ๋กœ ์‚ฌ์šฉํ•ด์•ผ ํ•œ๋‹ค.

๋งˆ์ง€๋ง‰์œผ๋กœ ํ•จ์ˆ˜ํ˜• ํ”„๋กœ๊ทธ๋ž˜๋ฐ์—์„œ ํด๋กœ์ €๋ฅผ ํ™œ์šฉํ•˜๋Š” ์˜ˆ์ œ๋ฅผ ๋ณด๋ฉฐ ๋งˆ์น˜๋„๋ก ํ•˜์ž.

// ์นด์šดํŠธ ์ƒํƒœ๋ฅผ ์œ ์ง€ํ•˜๊ธฐ ์œ„ํ•œ ๋ณ€์ˆ˜๋ฅผ ๊ธฐ์–ตํ•˜๋Š” ํด๋กœ์ €๋ฅผ ๋ฐ˜ํ™˜
const counter = (function () {
  // ์นด์šดํŠธ ์ƒํƒœ๋ฅผ ์œ ์ง€ํ•˜๊ธฐ ์œ„ํ•œ ๋ณ€์ˆ˜
  let count = 0;

  // ํ•จ์ˆ˜๋ฅผ ์ธ์ˆ˜๋กœ ์ „๋‹ฌ๋ฐ›๋Š” higher order function์„ ํด๋กœ์ €๋กœ ๋ฐ˜ํ™˜
  return function (arithmetics) {
    // ํ˜„์žฌ ์นด์šดํŠธ ๊ฐ’์— ํด๋กœ์ ธ๊ฐ€ ์ „๋‹ฌ๋ฐ›์€ ํ•จ์ˆ˜๋ฅผ ์‹คํ–‰์‹œํ‚ค๋Š” ํ•จ์ˆ˜ ๋ฐ˜ํ™˜
    count = arithmetics(count);
    return count;
  };
})();

function add(n) {
  return ++n;
}

function subtract(n) {
  return --n;
}

console.log(counter(add));
console.log(counter(add));
console.log(counter(add));
console.log(counter(subtract));
console.log(counter(subtract));
console.log(counter(add));

์ฐธ๊ณ  ๋ฌธ์„œ

https://poiemaweb.com/js-closure

๋งˆ์น˜๋ฉฐ

์ด๋ฒˆ ํฌ์ŠคํŒ…์—์„œ๋Š” ํด๋กœ์ €์— ๋Œ€ํ•ด ์•Œ์•„๋ณด์•˜๋‹ค.

๋ฆฌ์•กํŠธ ๊ณต์‹ ๋ฌธ์„œ๋ฅผ ์‚ดํŽด๋ณผ๋•Œ ์ฒ˜์Œ ํƒ€์ด๋จธ ์˜ˆ์‹œ์ฝ”๋“œ์—์„œ ํด๋กœ์ €๋ฅผ ํ™œ์šฉํ•ด์„œ ์ž‘์„ฑ๋œ ์ฝ”๋“œ๊ฐ€ ์ดํ•ด๊ฐ€ ์•ˆ๋˜์—ˆ๋Š”๋ฐ, ํด๋กœ์ €๋ฅผ ๊ณต๋ถ€ํ•˜๊ณ  ๋‹ค์‹œ ์ฝ์–ด๋ณด๋‹ˆ ์ดํ•ด๊ฐ€ ๋˜์–ด์„œ ๋ฟŒ๋“ฏํ–ˆ๋‹ค.๐Ÿ™Œ๐Ÿ™Œ

0๊ฐœ์˜ ๋Œ“๊ธ€