[TECH-QA] μžλ°”μŠ€ν¬λ¦½νŠΈ ν΄λ‘œμ €

ν•¨μˆ˜κ°€ 선언될 λ•Œ μžλ™μœΌλ‘œ μƒμ„±λ˜λŠ” λ ‰μ‹œμ»¬ ν™˜κ²½μ— λŒ€ν•œ μ„€λͺ…이닀.
쑰금 더 ν’€μ–΄μ„œ μ„€λͺ…해보면 μžλ°”μŠ€ν¬λ¦½νŠΈμ—μ„œ ν•¨μˆ˜λŠ” λ‹¨μˆœνžˆ μ½”λ“œ 덩어리가 μ•„λ‹ˆλΌ, κ·Έ ν•¨μˆ˜κ°€ λ§Œλ“€μ–΄μ§ˆ λ•Œ μ£Όλ³€ ν™˜κ²½(μŠ€μ½”ν”„)이 λ§Œλ“€μ–΄μ§€κ³  이 μ£Όλ³€ ν™˜κ²½μ„ "λ ‰μ‹œμ»¬ μŠ€μ½”ν”„(lexical scope)"라고 λΆ€λ₯Έλ‹€. ν•¨μˆ˜κ°€ μ–΄λ””μ„œ μ„ μ–Έλ˜μ—ˆλŠ”μ§€μ— 따라 μ ‘κ·Όν•  수 μžˆλŠ” λ³€μˆ˜λ“€μ΄ κ²°μ •λ˜λŠ”λ°
ν΄λ‘œμ €λŠ” λ°”λ‘œ 이 λ ‰μ‹œμ»¬ μŠ€μ½”ν”„λ₯Ό ν™œμš©ν•΄μ„œ, μ™ΈλΆ€ ν•¨μˆ˜κ°€ 싀행을 마치고 사라진 뒀에도 λ‚΄λΆ€ ν•¨μˆ˜κ°€ κ·Έ μ™ΈλΆ€ ν•¨μˆ˜μ˜ λ³€μˆ˜μ— μ ‘κ·Όν•  수 있게 λ§Œλ“œλŠ” 것이닀. 예λ₯Ό λ“€μ–΄μ„œ, ν΄λ‘œμ €λŠ” μ•„λž˜μ™€ 같이 μž‘λ™ν•œλ‹€.

πŸ“ μ™ΈλΆ€ν•¨μˆ˜ 내뢀에 ν•¨μˆ˜κ°€ μ‘΄μž¬ν•˜λŠ” 예제

function greetMaker(name) {

  let greeting = "μ•ˆλ…•, " + name + "!"; // μ™ΈλΆ€ ν•¨μˆ˜μ˜ λ³€μˆ˜
  
  function sayHello() {
    console.log(greeting); // λ‚΄λΆ€ ν•¨μˆ˜μ—μ„œ μ™ΈλΆ€ λ³€μˆ˜ μ‚¬μš©
  }

  return sayHello; // λ‚΄λΆ€ ν•¨μˆ˜ λ°˜ν™˜
}

const greetToMin = greetMaker("민수"); // "μ•ˆλ…•, 민수!"κ°€ μ €μž₯됨
greetToMin(); // "μ•ˆλ…•, 민수!" 좜λ ₯
μœ„μ½”λ“œ μ—μ„œ greetMakerκ°€ 싀행을 끝내면 일반적으둜 greeting λ³€μˆ˜λŠ” μ‚¬λΌμ§ˆ 것 κ°™μ§€λ§Œ, ν΄λ‘œμ € 덕뢄에 sayHelloκ°€ κ·Έ λ³€μˆ˜λ₯Ό 계속 κΈ°μ–΅ν•˜κ³  μžˆλ‹€. κ·Έλž˜μ„œ greetToMin()을 ν˜ΈμΆœν•˜λ©΄ "μ•ˆλ…•, 민수!"κ°€ 좜λ ₯λ˜λŠ” 것이닀.
ν΄λ‘œμ €μ˜ λ©‹μ§„ 점은 이런 νŠΉμ„±μ„ ν™œμš©ν•΄μ„œ 데이터λ₯Ό λ³΄ν˜Έν•˜κ±°λ‚˜, νŠΉμ • μƒνƒœλ₯Ό μœ μ§€ν•˜λŠ” 데 μ‚¬μš©ν•  수 μžˆλ‹€λŠ” 점이 μžˆλ‹€..
function createCounter() {
  let count = 0;
  return function() {
    count += 1;
    return count;
  };
}

const counter = createCounter();
console.log(counter()); // 1
console.log(counter()); // 2
console.log(counter()); // 3
μ—¬κΈ°μ„œ countλŠ” createCounter ν•¨μˆ˜ λ°–μ—μ„œλŠ” μ ‘κ·Όν•  수 μ—†μ§€λ§Œ, λ°˜ν™˜λœ ν•¨μˆ˜κ°€ ν΄λ‘œμ €λ‘œ countλ₯Ό κΈ°μ–΅ν•˜κ³  μžˆμ–΄μ„œ 값을 계속 μ¦κ°€μ‹œν‚¬ 수 있죠. 이런 μ‹μœΌλ‘œ ν΄λ‘œμ €λŠ” 개인적인 곡간을 λ§Œλ“€μ–΄μ£ΌλŠ” 역할을 ν•œλ‹€. 즉, ν΄λ‘œμ €λŠ” ν•¨μˆ˜μ™€ κ·Έ ν•¨μˆ˜κ°€ μ ‘κ·Όν•  수 μžˆλŠ” ν™˜κ²½μ΄ ν•¨κ»˜ λ¬Άμ—¬μ„œ, μœ μ—°ν•˜λ©΄μ„œλ„ κ°•λ ₯ν•œ μ½”λ“œλ₯Ό μž‘μ„±ν•  수 있게 ν•΄μ£ΌλŠ” μžλ°”μŠ€ν¬λ¦½νŠΈμ˜ 핡심 κΈ°λŠ₯ 쀑 ν•˜λ‚˜μ΄λ‹€.

μ‹€ν–‰ μ»¨ν…μŠ€νŠΈμ™€ λ ‰μ‹œμ»¬ μŠ€μ½”ν”„

λ¨Όμ €, μ‹€ν–‰ μ»¨ν…μŠ€νŠΈλŠ” μžλ°”μŠ€ν¬λ¦½νŠΈ μ½”λ“œκ°€ 싀행될 λ•Œ λ§Œλ“€μ–΄μ§€λŠ” ν™˜κ²½μ΄λ‹€. 이 ν™˜κ²½μ—λŠ”
  • λ³€μˆ˜ 객체(variable object)
  • μŠ€μ½”ν”„ 체인(scope chain)
  • 그리고 this κ°’
같은 정보가 포함돼 μžˆλ‹€. ν•¨μˆ˜κ°€ 호좜될 λ•Œλ§ˆλ‹€ μƒˆλ‘œμš΄ μ‹€ν–‰ μ»¨ν…μŠ€νŠΈκ°€ μƒμ„±λ˜λŠ”λ°, 이건 μŠ€νƒ ν˜•νƒœλ‘œ μŒ“μ—¬μ„œ ν˜„μž¬ μ‹€ν–‰ 쀑인 μ½”λ“œμ™€ κ·Έ μ£Όλ³€ ν™˜κ²½μ„ κ΄€λ¦¬ν•œλ‹€. μ—¬κΈ°μ„œ μ€‘μš”ν•œ 건 λ ‰μ‹œμ»¬ μŠ€μ½”ν”„μΈλ° λ ‰μ‹œμ»¬ μŠ€μ½”ν”„λŠ” ν•¨μˆ˜κ°€ μ–΄λ””μ„œ μ„ μ–Έλ˜μ—ˆλŠ”μ§€μ— 따라 κ·Έ ν•¨μˆ˜κ°€ μ ‘κ·Όν•  수 μžˆλŠ” λ³€μˆ˜μ˜ λ²”μœ„κ°€ μ •ν•΄μ§„λ‹€. 즉, ν•¨μˆ˜κ°€ μž‘μ„±λœ μœ„μΉ˜μ— 따라 "μ •μ μœΌλ‘œ" μŠ€μ½”ν”„κ°€ κ²°μ •λœλ‹€λŠ” λœ»μ΄λ‹€. 이 λ ‰μ‹œμ»¬ μŠ€μ½”ν”„λŠ” μ‹€ν–‰ μ»¨ν…μŠ€νŠΈκ°€ λ§Œλ“€μ–΄μ§ˆ λ•Œ ν•¨μˆ˜μ˜ ν™˜κ²½μ„ κΈ°μ–΅ν•˜λŠ” 데 핡심적인 역할을 ν•œλ‹€.

μŠ€μ½”ν”„μ™€ μŠ€μ½”ν”„ 체인

μŠ€μ½”ν”„λŠ” μ–΄λ–€ λ³€μˆ˜μ— μ ‘κ·Όν•  수 μžˆλŠ” μœ νš¨λ²”μœ„λ‘œ μ½”λ“œ μ–΄λ””μ„œλ“  μ°Έμ‘° ν•  수 μžˆλŠ” μ „μ—­μŠ€μ½”ν”„μ™€ ν•¨μˆ˜μžμ‹ κ³Ό ν•˜μœ„ν•¨μˆ˜μ—μ„œλ§Œ μ°Έμ‘°ν•  수 μžˆλŠ” μ§€μ—­μŠ€μ½”ν”„κ°€ μžˆλ‹€. ν•¨μˆ˜ μ•ˆμ—μ„œ μ„ μ–Έλœ λ³€μˆ˜λŠ” ν•΄λ‹Ή ν•¨μˆ˜μ—μ„œλ§Œ μ‚¬μš©ν• μˆ˜ 있으며 μ „μ—­λ³€μˆ˜μ— 영ν–₯을 끼칠 수 μ—†λ‹€. μœ„μ—μ„œ λ§ν•œ μŠ€μ½”ν”„ 체인은 ν•¨μˆ˜κ°€ μ„ μ–Έλœ λ ‰μ‹œμ»¬ μŠ€μ½”ν”„λ₯Ό 기반으둜, λ³€μˆ˜μ— μ ‘κ·Όν•  λ•Œ λ”°λΌκ°€λŠ” μΌμ’…μ˜ "탐색 경둜"이닀. μžλ°”μŠ€ν¬λ¦½νŠΈλŠ” λ‚΄λΆ€ ν•¨μˆ˜μ—μ„œ μ°Έμ‘°ν•  λ³€μˆ˜κ°€ ν˜„μž¬ μŠ€μ½”ν”„μ— μ—†μœΌλ©΄, κ·Έ μƒμœ„ μŠ€μ½”ν”„λ‘œ λ²”μœ„λ₯Ό λ„“ν˜€ κ°€λ©° μ°Ύμ•„ μ˜¬λΌκ°€λŠ”λ° μ‹€ν–‰ μ»¨ν…μŠ€νŠΈ μ•ˆμ— μ €μž₯된 μŠ€μ½”ν”„ 체인이 λ°”λ‘œ 이 과정을 κ°€λŠ₯ν•˜κ²Œ ν•΄μ€λ‹ˆλ‹€.

ν΄λ‘œμ €μ˜ 탄생

ν΄λ‘œμ €λŠ” λ‚΄λΆ€ ν•¨μˆ˜κ°€ μ™ΈλΆ€ ν•¨μˆ˜μ˜ λ³€μˆ˜μ— μ ‘κ·Όν•˜λŠ” μƒν™©μ—μ„œ λ‚˜νƒ€λ‚œλ‹€. μ™ΈλΆ€ ν•¨μˆ˜κ°€ 싀행을 마치고 μ‹€ν–‰ μ»¨ν…μŠ€νŠΈκ°€ μŠ€νƒμ—μ„œ μ œκ±°λ˜λ”λΌλ„, λ‚΄λΆ€ ν•¨μˆ˜λŠ” μ™ΈλΆ€ ν•¨μˆ˜μ˜ λ ‰μ‹œμ»¬ ν™˜κ²½μ„ μ°Έμ‘°ν•  수 μžˆλ‹€. 이게 κ°€λŠ₯ν•œ μ΄μœ λŠ” λ‚΄λΆ€ ν•¨μˆ˜κ°€ μŠ€μ½”ν”„ 체인을 톡해 μ™ΈλΆ€ ν•¨μˆ˜μ˜ λ³€μˆ˜κ°€ μ €μž₯된 ν™˜κ²½μ„ 계속 "κΈ°μ–΅"ν•˜κ³  있기 λ•Œλ¬Έμ΄κ³  μžλ°”μŠ€ν¬λ¦½νŠΈ 엔진은 λ³€μˆ˜λ‚˜ ν•¨μˆ˜κ°€ μ–΄λ–€ μŠ€μ½”ν”„μ—μ„œ μ—¬μ „νžˆ 참쑰되고 μžˆλŠ” 경우, κ·Έ 데이터λ₯Ό λ©”λͺ¨λ¦¬μ—μ„œ μ œκ±°ν•˜μ§€ μ•Šκ³  μœ μ§€ν•˜λ„λ‘ μ„€κ³„λ˜μ–΄ μžˆλ‹€.

κ°€λΉ„μ§€ μ»¬λ ‰μ…˜κ³Ό μ°Έμ‘°

μžλ°”μŠ€ν¬λ¦½νŠΈ μ—”μ§„(예: V8 μ—”μ§„ 같은 경우)μ—λŠ” κ°€λΉ„μ§€ 컬렉터(Garbage Collector, GC)κ°€ μžˆμ–΄μ„œ 더 이상 μ‚¬μš©λ˜μ§€ μ•ŠλŠ” λ©”λͺ¨λ¦¬λ₯Ό 정리해쀀닀. κ°€λΉ„μ§€ μ»¬λ ‰ν„°λŠ” 기본적으둜 "도달 κ°€λŠ₯μ„±(reachability)"μ΄λΌλŠ” κ°œλ…μ„ κΈ°μ€€μœΌλ‘œ λ™μž‘ν•œλ‹€.
  • μ–΄λ–€ 데이터가 μ½”λ“œμ—μ„œ 더 이상 도달(μ°Έμ‘°)될 수 μ—†μœΌλ©΄, κ·Έ λ°μ΄ν„°λŠ” λ©”λͺ¨λ¦¬μ—μ„œ ν•΄μ œ(garbage collected)될 λŒ€μƒμ΄ λœλ‹€.
  • λ°˜λŒ€λ‘œ, μ–΄λ–€ 데이터가 μ—¬μ „νžˆ 참쑰되고 있으면 λ©”λͺ¨λ¦¬μ— μœ μ§€λœλ‹€.
ν΄λ‘œμ €μ˜ 경우, λ‚΄λΆ€ ν•¨μˆ˜κ°€ μ™ΈλΆ€ ν•¨μˆ˜μ˜ λ ‰μ‹œμ»¬ ν™˜κ²½μ„ μŠ€μ½”ν”„ 체인을 톡해 μ°Έμ‘°ν•˜κ³  있으면, κ·Έ λ ‰μ‹œμ»¬ ν™˜κ²½κ³Ό κ·Έ μ•ˆμ— μžˆλŠ” λ³€μˆ˜λ“€μ€ "도달 κ°€λŠ₯"ν•œ μƒνƒœλ‘œ κ°„μ£Όλœλ‹€. κ·Έλž˜μ„œ κ°€λΉ„μ§€ 컬렉터가 이 λ©”λͺ¨λ¦¬λ₯Ό μ •λ¦¬ν•˜μ§€ μ•Šκ³  남겨둔닀.
function outer() {
  let x = 10;
  function inner() {
    console.log(x);
  }
  return inner;
}

const closure = outer();
closure(); // 10 좜λ ₯
  • μ—¬κΈ°μ„œ outerκ°€ λλ‚œ 후에도 closure λ³€μˆ˜κ°€ inner ν•¨μˆ˜λ₯Ό μ°Έμ‘°ν•˜κ³  μžˆλ‹€.
  • innerλŠ” μŠ€μ½”ν”„ 체인을 톡해 outer의 λ ‰μ‹œμ»¬ ν™˜κ²½(κ·Έ μ•ˆμ— x = 10 포함)을 μ°Έμ‘°ν•˜κ³  μžˆλ‹€.
  • μžλ°”μŠ€ν¬λ¦½νŠΈ 엔진은 closureκ°€ μ‚΄μ•„μžˆλŠ” ν•œ, innerκ°€ μ°Έμ‘°ν•˜λŠ” x와 κ·Έ λ ‰μ‹œμ»¬ ν™˜κ²½μ΄ 도달 κ°€λŠ₯ν•˜λ‹€κ³  νŒλ‹¨ν•΄μ„œ λ©”λͺ¨λ¦¬μ—μ„œ μ œκ±°ν•˜μ§€ μ•ŠλŠ”λ‹€.

λ ‰μ‹œμ»¬ ν™˜κ²½

πŸ“ λ ‰μ‹œμ»¬ ν™˜κ²½μ˜ ꡬ성 μš”μ†Œ

  • ν™˜κ²½ λ ˆμ½”λ“œ(Environment Record): λ³€μˆ˜, ν•¨μˆ˜ μ„ μ–Έ, λ§€κ°œλ³€μˆ˜ 등이 μ €μž₯됨.
  • μ™ΈλΆ€ λ ‰μ‹œμ»¬ ν™˜κ²½ μ°Έμ‘°(Outer Reference): μƒμœ„ μŠ€μ½”ν”„μ˜ λ ‰μ‹œμ»¬ ν™˜κ²½μ„ 가리킴.

πŸ“ μŠ€μ½”ν”„ 체인(Scope Chain)κ³Ό λ ‰μ‹œμ»¬ ν™˜κ²½

  • μŠ€μ½”ν”„ 체인은 λ ‰μ‹œμ»¬ ν™˜κ²½μ΄ 직접 κ°€μ§€κ³  μžˆλŠ” 건 μ•„λ‹ˆκ³  λ ‰μ‹œμ»¬ ν™˜κ²½μ˜ "μ™ΈλΆ€ μ°Έμ‘°"λ₯Ό 톡해 μŠ€μ½”ν”„ 체인이 λ§Œλ“€μ–΄μ§„λ‹€.
  • μŠ€μ½”ν”„ 체인은 μ‹€ν–‰ μ»¨ν…μŠ€νŠΈκ°€ 싀행될 λ•Œ, ν˜„μž¬ λ ‰μ‹œμ»¬ ν™˜κ²½κ³Ό κ·Έ μ™ΈλΆ€ μ°Έμ‘°λ₯Ό μ—°κ²°ν•˜λ©΄μ„œ ν˜•μ„±λ˜λŠ” 동적인 체인이라고 λ³Ό 수 μžˆλ‹€. 즉, λ ‰μ‹œμ»¬ ν™˜κ²½μ€ μŠ€μ½”ν”„ 체인 자체λ₯Ό "κ°€μ§€κ³  μžˆλ‹€"κ³  ν‘œν˜„ν•˜κΈ°λ³΄λ‹€λŠ” "μŠ€μ½”ν”„ 체인을 κ°€λŠ₯ν•˜κ²Œ ν•˜λŠ” ꡬ쑰"이닀.

πŸ“ λ ‰μ‹œμ»¬ μŠ€μ½”ν”„(Lexical Scope)와 λ ‰μ‹œμ»¬ ν™˜κ²½

  • λ ‰μ‹œμ»¬ ν™˜κ²½μ€ ν•¨μˆ˜κ°€ μ„ μ–Έλœ μœ„μΉ˜μ— 따라 μ •μ μœΌλ‘œ κ²°μ •λ˜λ©° ν•¨μˆ˜κ°€ 선언될 λ•Œ κ·Έ μ£Όλ³€ ν™˜κ²½μ„ μΊ‘μ²˜ν•΄μ„œ λ ‰μ‹œμ»¬ μŠ€μ½”ν”„λ₯Ό λ§Œλ“ λ‹€.

πŸ“ this κ°’κ³Ό μ‹€ν–‰μ»¨ν…μŠ€νŠΈ

  • μ‹€ν–‰ μ»¨ν…μŠ€νŠΈλŠ” λ ‰μ‹œμ»¬ ν™˜κ²½(Lexical Environment), λ³€μˆ˜ 객체(Variable Object), 그리고 this 바인딩을 ν¬ν•¨ν•˜λŠ” λ ‰μ‹œμ»¬ ν™˜κ²½λ³΄λ‹€ 더 큰 ꡬ쑰이닀. λ ‰μ‹œμ»¬ ν™˜κ²½μ€ κ·Έ μ•ˆμ—μ„œ λ³€μˆ˜μ™€ μŠ€μ½”ν”„λ§Œ 닀루고, thisλŠ” ν•¨μˆ˜κ°€ "μ–΄λ–»κ²Œ ν˜ΈμΆœλ˜μ—ˆλŠ”μ§€"에 따라 λ™μ μœΌλ‘œ κ²°μ • ν•œλ‹€.

πŸ“ 예제둜 확인

function outer() {
  let x = 10;
  function inner() {
    console.log(x, this);
  }
  return inner;
}

const closure = outer.call({ name: "test" });
closure(); // 10, { name: "test" } κ°€μ Έμ˜€μ§€λ§Œ, 이건 λ ‰μ‹œμ»¬ ν™˜κ²½μ΄ thisλ₯Ό ν¬ν•¨ν•΄μ„œκ°€ μ•„λ‹ˆλΌ ν™”μ‚΄ν‘œ ν•¨μˆ˜ 자체의 νŠΉμ„± λ•Œλ¬Έμ΄λ‹€.
λ ‰μ‹œμ»¬ ν™˜κ²½
  • outer의 λ ‰μ‹œμ»¬ ν™˜κ²½: { x: 10, inner: ν•¨μˆ˜ }, μ™ΈλΆ€ μ°Έμ‘°λŠ” μ „μ—­.
  • inner의 λ ‰μ‹œμ»¬ ν™˜κ²½: 둜컬 λ³€μˆ˜ μ—†μŒ, μ™ΈλΆ€ μ°Έμ‘°λŠ” outer의 λ ‰μ‹œμ»¬ ν™˜κ²½.
μŠ€μ½”ν”„ 체인
  • inner μ‹€ν–‰ μ‹œ inner β†’ outer β†’ μ „μ—­μœΌλ‘œ μ—°κ²°.
this
  • outer.call둜 { name: "test" }κ°€ 바인딩됨. 이건 λ ‰μ‹œμ»¬ ν™˜κ²½μ΄ μ•„λ‹ˆλΌ μ‹€ν–‰ μ»¨ν…μŠ€νŠΈμ—μ„œ κ²°μ •.