πŸ¦„ Cukehater

JavaScript ν΄λ‘œμ €(Closure)

πŸ¦„ Cukehater

Β·

2λ…„ μ „

MDNμ—μ„œλŠ” ν΄λ‘œμ €λ₯Ό "μ£Όλ³€ μƒνƒœ(μ–΄νœ˜μ  ν™˜κ²½)에 λŒ€ν•œ 참쑰와 ν•¨κ»˜ 묢인(ν¬ν•¨λœ) ν•¨μˆ˜μ˜ 쑰합이며, 이λ₯Ό 톡해 μ™ΈλΆ€ ν•¨μˆ˜μ˜ 싀행이 μ’…λ£Œλ˜λ”λΌλ„ λ‚΄λΆ€ ν•¨μˆ˜μ—μ„œ μ™ΈλΆ€ ν•¨μˆ˜μ˜ λ²”μœ„μ— λŒ€ν•œ 접근을 μ œκ³΅ν•œλ‹€." 라고 μ„€λͺ…ν•œλ‹€. ν•˜μ§€λ§Œ μ„€λͺ…λ§ŒμœΌλ‘œλŠ” μ œλŒ€λ‘œ 이해가 λ˜μ§€ μ•Šμ•˜κ³  이λ₯Ό ν™•μ‹€νžˆ μ•ŒκΈ° μœ„ν•΄ 이 글을 ν¬μŠ€νŒ…ν•œλ‹€.


TL;DR

ν΄λ‘œμ €λŠ” ν•¨μˆ˜κ°€ 선언될 λ•Œμ˜ ν™˜κ²½μ„ κΈ°μ–΅ν•˜μ—¬, ν•¨μˆ˜ λ‚΄λΆ€μ—μ„œ μ„ μ–Έλœ λ³€μˆ˜κ°€ ν•¨μˆ˜ μ™ΈλΆ€μ—μ„œ μ°Έμ‘°(μ ‘κ·Ό) κ°€λŠ₯ν•˜λ„λ‘ λ§Œλ“œλŠ” λ©”μ»€λ‹ˆμ¦˜μ΄λ‹€.

ν΄λ‘œμ €μ˜ νŠΉμ§•μ€ λ‹€μŒκ³Ό κ°™λ‹€.

  • 데이터 보쑴 및 은닉화 κ°€λŠ₯
  • λͺ¨λ“ˆν™”에 유리
  • private λ³€μˆ˜ κ΅¬ν˜„ κ°€λŠ₯
  • 잘λͺ» μ‚¬μš© μ‹œ λ©”λͺ¨λ¦¬ λˆ„μˆ˜ λ°œμƒ κ°€λŠ₯
  • κ°€λΉ„μ§€ μ»¬λ ‰μ…˜ μ²˜λ¦¬κ°€ 볡작

ν΄λ‘œμ €μ˜ κΈ°λ³Έ κ°œλ…

MDN의 μ„€λͺ…λŒ€λ‘œ ν΄λ‘œμ €λ₯Ό ν•œ μ€„λ‘œ μ„€λͺ…ν•˜μžλ©΄ ν•¨μˆ˜ λ‚΄λΆ€μ—μ„œ μ„ μ–Έλœ λ³€μˆ˜κ°€ ν•¨μˆ˜ μ™ΈλΆ€μ—μ„œ μ°Έμ‘° κ°€λŠ₯ν•˜λ„λ‘ λ§Œλ“œλŠ” λ©”μ»€λ‹ˆμ¦˜μ΄λ‹€.

예제λ₯Ό 톡해 ν΄λ‘œμ €μ˜ κΈ°λ³Έ κ°œλ…μ„ μ•Œμ•„λ³΄μž.

javascript Copy
function outer() {
  let a = 10;
  console.log(a);
}
outer(); // 10
console.log(a); // ReferenceError

이 μ½”λ“œμ—μ„œ ν•¨μˆ˜ outer() λ‚΄λΆ€μ—μ„œ μ„ μ–Έλœ λ³€μˆ˜ aλŠ” ν•¨μˆ˜ λ‚΄λΆ€μ—μ„œλ§Œ 접근이 κ°€λŠ₯ν•˜λ‹€. outer()κ°€ μ’…λ£Œλœ ν›„ μ „μ—­μ—μ„œ a에 μ ‘κ·Όν•˜λ €κ³  ν•˜λ©΄ λ‹Ήμ—°νžˆλ„ ReferenceErrorκ°€ λ°œμƒν•˜λŠ”λ°, μ΄λŠ” aκ°€ ν•¨μˆ˜μ˜ μ‹€ν–‰ μ»¨ν…μŠ€νŠΈ λ‚΄μ—λ§Œ μœ νš¨ν•˜κΈ° λ•Œλ¬Έμ΄λ‹€.

κ·ΈλŸ¬λ‚˜ ν΄λ‘œμ €λ₯Ό ν™œμš©ν•˜λ©΄ μ™ΈλΆ€ ν•¨μˆ˜ 내뢀에 μžˆλŠ” λ³€μˆ˜μ— ν•¨μˆ˜ μ™ΈλΆ€μ—μ„œλ„ μ ‘κ·Όν•  수 μžˆλ‹€.

javascript Copy
function outer() {
  let a = 10;
  return function inner() {
    return a;
  };
}
let fn1 = outer();
console.log(fn1()); // 10

이 μ˜ˆμ œμ—μ„œ ν•¨μˆ˜ outer()λŠ” inner() ν•¨μˆ˜λ₯Ό λ°˜ν™˜ν•œλ‹€. inner()λŠ” μ™ΈλΆ€ ν•¨μˆ˜μΈ outer()의 λ³€μˆ˜ a에 μ ‘κ·Όν•  수 μžˆλ‹€. μ΄λŠ” inner()κ°€ 선언될 λ•Œμ˜ μ–΄νœ˜μ  ν™˜κ²½μ„ κΈ°μ–΅ν•˜κΈ° λ•Œλ¬Έμ΄λ‹€.

λ”°λΌμ„œ outer()λ₯Ό ν• λ‹Ήν•œ ν•¨μˆ˜ fn1()μ—μ„œ outer() ν•¨μˆ˜ λ‚΄λΆ€μ˜ λ³€μˆ˜ a에 접근이 κ°€λŠ₯ν•΄μ§€λ©° 10을 λ°˜ν™˜ν•œλ‹€.

μ΄λŸ¬ν•œ λ™μž‘μ΄ κ°€λŠ₯ν•œ μ΄μœ λŠ” JavaScript의 λ ‰μ‹œμ»¬ μŠ€μ½”ν”„(Lexical Scope) νŠΉμ„± λ•Œλ¬Έμ΄λ‹€.

ν•¨μˆ˜κ°€ 선언될 λ•Œ κ·Έ ν•¨μˆ˜μ˜ μƒμœ„ μŠ€μ½”ν”„κ°€ κ²°μ •λ˜λ©°, 이 μŠ€μ½”ν”„ 체인을 톡해 λ³€μˆ˜λ₯Ό μ°Ύμ•„λ‚˜κ°„λ‹€. inner() ν•¨μˆ˜κ°€ 선언될 λ•Œ outer() ν•¨μˆ˜μ˜ μŠ€μ½”ν”„λ₯Ό κΈ°μ–΅ν•˜κ³  있기 λ•Œλ¬Έμ—, outer() ν•¨μˆ˜κ°€ 싀행을 마치고 콜 μŠ€νƒμ—μ„œ 제거된 후에도 inner() ν•¨μˆ˜λŠ” μ—¬μ „νžˆ outer() ν•¨μˆ˜μ˜ λ³€μˆ˜ a에 μ ‘κ·Όν•  수 μžˆλ‹€.

이λ₯Ό ν™œμš©ν•˜λ©΄ λ‹€μŒκ³Ό 같은 μ˜ˆμ œλ„ κ°€λŠ₯ν•˜λ‹€.

javascript Copy
function counter() {
  let count = 0;
  return {
    increase: function() {
      return ++count;
    },
    decrease: function() {
      return --count;
    },
    getCount: function() {
      return count;
    }
  };
}

const counter1 = counter();

console.log(counter1.increase()); // 1
console.log(counter1.increase()); // 2
console.log(counter1.decrease()); // 1
console.log(counter1.getCount()); // 1

이 μ˜ˆμ œμ—μ„œλŠ” counter() ν•¨μˆ˜κ°€ μ„Έ 개의 λ©”μ„œλ“œλ₯Ό κ°€μ§„ 객체λ₯Ό λ°˜ν™˜ν•œλ‹€.

각 λ©”μ„œλ“œλŠ” ν΄λ‘œμ €λ₯Ό ν˜•μ„±ν•˜μ—¬ count λ³€μˆ˜μ— μ ‘κ·Όν•  수 μžˆλ‹€. 이λ₯Ό 톡해 private λ³€μˆ˜μ²˜λŸΌ μ™ΈλΆ€μ—μ„œ 직접적인 접근은 λΆˆκ°€λŠ₯ν•˜μ§€λ§Œ, μ •μ˜λœ λ©”μ„œλ“œλ₯Ό ν†΅ν•΄μ„œλ§Œ μ‘°μž‘μ΄ κ°€λŠ₯ν•œ μ€λ‹‰ν™”λœ μƒνƒœλ₯Ό λ§Œλ“€ 수 μžˆλ‹€.

이처럼 ν΄λ‘œμ €λŠ” 데이터 ν”„λΌμ΄λ²„μ‹œλ₯Ό κ΅¬ν˜„ν•˜κ³ , μƒνƒœλ₯Ό μ•ˆμ „ν•˜κ²Œ κ΄€λ¦¬ν•˜λ©°, λͺ¨λ“ˆν™”λœ μ½”λ“œλ₯Ό μž‘μ„±ν•˜λŠ” 데 맀우 μœ μš©ν•œ JavaScript의 핡심 κ°œλ…μ΄λ‹€.


ν΄λ‘œμ €μ˜ μž₯단점

  1. 데이터 보쑴
    μ™ΈλΆ€ ν•¨μˆ˜μ˜ 싀행이 λλ‚˜λ”λΌλ„ μ™ΈλΆ€ ν•¨μˆ˜ λ‚΄ λ³€μˆ˜λ₯Ό 계속 μ‚¬μš©ν•  수 μžˆλŠ” νŠΉμ„±μ„ 톡해 νŠΉμ • 데이터λ₯Ό μŠ€μ½”ν”„ μ•ˆμ— 가두어 계속 μ‚¬μš©ν•  수 μžˆλŠ” 폐쇄성을 κ°–λŠ”λ‹€.
  2. λͺ¨λ“ˆν™”에 유리
    ν•¨μˆ˜λ₯Ό 독립적인 ν˜•νƒœλ‘œ λΆ„λ¦¬ν•˜μ—¬ ν•¨μˆ˜μ˜ μž¬μ‚¬μš©μ„±μ„ ν–₯μƒμ‹œν‚¬ 수 μžˆλ‹€. 이λ₯Ό 톡해 μ½”λ“œμ˜ λͺ¨λ“ˆν™”κ°€ μš©μ΄ν•΄μ§„λ‹€.
  3. μ •λ³΄μ˜ 은닉화
    μ„ μ–Έ λ‹Ήμ‹œ μ°Έμ‘°ν•˜λ˜ μ™ΈλΆ€ ν™˜κ²½μ„ κΈ°μ–΅ν•˜μ—¬ 정보 은닉이 κ°€λŠ₯ν•˜λ‹€. 이λ₯Ό 톡해 μ™ΈλΆ€λ‘œλΆ€ν„°μ˜ μ˜€μ—Όμ„ λ°©μ§€ν•  수 μžˆλ‹€.
  4. λ©”λͺ¨λ¦¬ λˆ„μˆ˜
    μ œλŒ€λ‘œ μ‚¬μš©λ˜μ§€ μ•ŠμœΌλ©΄ λ‚΄λΆ€ ν•¨μˆ˜κ°€ 더 이상 μ°Έμ‘°λ˜μ§€ μ•Šμ„ λ•ŒκΉŒμ§€ μ™ΈλΆ€ ν•¨μˆ˜ λ²”μœ„μ˜ λ³€μˆ˜κ°€ λ©”λͺ¨λ¦¬μ—μ„œ μ§€μ›Œμ§€μ§€ μ•ŠκΈ° λ•Œλ¬Έμ— λ©”λͺ¨λ¦¬ λˆ„μˆ˜λ₯Ό μΌμœΌν‚¬ 수 μžˆλ‹€. λ”°λΌμ„œ 루프λ₯Ό λŒλ©΄μ„œ ν΄λ‘œμ €λ₯Ό 계속 μƒμ„±ν•˜λŠ” μ„€κ³„λŠ” μ§€μ–‘ν•΄μ•Ό ν•œλ‹€.
  5. κ°€λΉ„μ§€ μ»¬λ ‰μ…˜μ˜ 어렀움
    ν΄λ‘œμ €μ˜ 지속적인 νŠΉμ„±μœΌλ‘œ 인해 κ°€λΉ„μ§€ 컬렉터가 ν΄λ‘œμ €κ°€ 더 이상 ν•„μš”ν•˜μ§€ μ•Šκ³  λ©”λͺ¨λ¦¬μ—μ„œ μ•ˆμ „ν•˜κ²Œ μ œκ±°ν•  수 μžˆλŠ” μ‹œκΈ°λ₯Ό κ²°μ •ν•˜κΈ° μ–΄λ €μšΈ 수 μžˆλ‹€.

Reactμ—μ„œ ν΄λ‘œμ €

React의 λ‚΄μž₯ ν›…(Hooks)κ³Ό ν΄λ‘œμ €λŠ” μ„œλ‘œ λ°€μ ‘ν•œ 연관이 μžˆλ‹€. κ·Έ 쀑 useState ν›…μ˜ λ™μž‘ 원리λ₯Ό ν΄λ‘œμ € κ΄€μ μ—μ„œ μ‚΄νŽ΄λ³΄μž.

jsx Copy
function Counter() {
  const [count, setCount] = useState(0);
  
  return (
    <button onClick={() => setCount(count + 1)}>
      Count: {count}
    </button>
  );
}

useStateλŠ” ν΄λ‘œμ €λ₯Ό ν™œμš©ν•˜μ—¬ μ»΄ν¬λ„ŒνŠΈμ˜ μƒνƒœλ₯Ό κ΄€λ¦¬ν•œλ‹€.
ReactλŠ” λ‚΄λΆ€μ μœΌλ‘œ λ‹€μŒκ³Ό 같은 ν΄λ‘œμ € ꡬ쑰λ₯Ό κ°€μ§„λ‹€.

javascript Copy
// React λ‚΄λΆ€ λ™μž‘ λ‹¨μˆœν™” μ˜ˆμ‹œ
function useState(initialValue) {
  let state = initialValue; // ν΄λ‘œμ €λ₯Ό 톡해 μœ μ§€λ˜λŠ” κ°’
  
  const setState = (newValue) => {
    state = newValue;
    // λ¦¬λ Œλ”λ§ 트리거
  };
  
  return [state, setState];
}

이 μ½”λ“œμ—μ„œ μ£Όλͺ©ν•  점은 state λ³€μˆ˜κ°€ ν΄λ‘œμ €λ₯Ό 톡해 μœ μ§€λœλ‹€λŠ” 것이닀. μ»΄ν¬λ„ŒνŠΈκ°€ λ¦¬λ Œλ”λ§λ˜λ”λΌλ„ state λ³€μˆ˜λŠ” ν΄λ‘œμ € 덕뢄에 이전 값을 μœ μ§€ν•  수 μžˆλ‹€. λ˜ν•œ 각 μ»΄ν¬λ„ŒνŠΈ μΈμŠ€ν„΄μŠ€λŠ” μžμ‹ λ§Œμ˜ ν΄λ‘œμ €λ₯Ό κ°€μ§€λ―€λ‘œ, μ„œλ‘œ λ‹€λ₯Έ μ»΄ν¬λ„ŒνŠΈμ˜ μƒνƒœκ°€ λ…λ¦½μ μœΌλ‘œ μœ μ§€λœλ‹€.

jsx Copy
function App() {
  return (
    <div>
      <Counter /> {/* λ…λ¦½λœ state */}
      <Counter /> {/* λ…λ¦½λœ state */}
    </div>
  );
}

이처럼 ν΄λ‘œμ €λŠ” React의 선언적이고 예츑 κ°€λŠ₯ν•œ μƒνƒœ 관리λ₯Ό κ°€λŠ₯ν•˜κ²Œ ν•˜λ©°, React의 μƒνƒœ 관리 μ‹œμŠ€ν…œμ˜ 핡심 λ©”μ»€λ‹ˆμ¦˜μœΌλ‘œμ„œ, μ»΄ν¬λ„ŒνŠΈμ˜ 생λͺ…μ£ΌκΈ° λ™μ•ˆ μƒνƒœλ₯Ό μ•ˆμ „ν•˜κ²Œ λ³΄μ‘΄ν•˜κ³  μ—…λ°μ΄νŠΈν•˜λŠ” 역할을 ν•œλ‹€.

cukehater

πŸ¦„ Cukehater

개발 κ²½ν—˜κ³Ό 기술적 μΈμ‚¬μ΄νŠΈλ₯Ό κ³΅μœ ν•©λ‹ˆλ‹€ πŸ’»βœ¨