Scope Closure Currying
2025. 3. 30. 14:26
728x90
반응형

Scope

  • Javascript에서의 Scope는 변수와 함수가 접근할 수 있는 범위를 지칭합니다.
  • Javascript의 Scope는 크게 전역 스코프(Global Scope), 지역 스코프(Local Scope) [ 함수 스코프(Function Scope), 블록 스코프(Block Scope) ] 로 나뉩니다.
    • 전역 스코프(Global Scope)
      • 자바스크립트 코드의 최상위 레벨에 해당하는 스코프로 함수 어디에서든 접근이 가능하다.
    • 함수 스코프(Function Scope)
      • 특정 함수 내부에서만 유효한 스코프로 함수 내에서 선언된 변수는 해당 함수 내부에서만 접근 가능하며, 함수 외부에서는 접근할 수 없습니다.
      • 자바스크립트의 var 키워드는 함수 스코프를 따릅니다.
    • 블록 스코프(Block Scope)
      • `{ }`로 감싸진 코드 블록 내에서만 유효한 스코프로 ES6에서 도입된 let const는 블록 스코프를 따릅니다. 즉, let이나 const로 선언된 변수는 선언된 코드 블록 내에서만 유효하며, 코드 블록 외부에서는 접근할 수 없습니다.

시컬 스코프 (Lexical Scope)

  • Javascript는 렉시컬 스코프 (Lexical Scope) 방식을 따른다.
  • 함수가 정의된 위치에 따라 변수에 대한 접근 범위가 결정됩니다. 
function outer() {
  let outerVar = "I am from outer function";

  function inner() { 
  	// inner() 함수는 outer() 함수 내에 정의되어 있기 때문에, outerVar에 접근할 수 있습니다.
    // 하지만 outer() 함수는 inner() 함수의 변수에는 접근할 수 없습니다.
    console.log(outerVar); // "I am from outer function"
  }

  inner();
}

outer();
  • 반대로 함수가 호출되는 위치에 따라 변수에 대한 접근 범위가 결정되는 방식을 다이나믹 스코프 (Dynamic Scope)라고 한다.
    • Bash(셸 스크립트) 은 Dynamic Scope 방식을 따른다.
  • 변수를 찾을 때까지 현재 스코프부터 시작해서 외부 스코프를 차례대로 탐색을 하고, 이 과정을 스코프 체인 (Scope Chain) 이라고 한다.

Closure

  • 함수가 선언될 때의 렉시컬 스코프 (Lexical Scope) 를 기억하여, 함수가 실행될 때도 이 스코프에 접근하여 다른 함수의 범위에 있는 변수를 기억하는 특성입니다. 
  • 내부 함수가 외부 함수의 변수를 참조하고 있을 때, 외부 함수가 실행을 끝내도 내부 함수는 그 변수를 계속 사용할 수 있습니다.
  • 클로저는 캡슐화 (encapsulation), 데이터 은닉 (data hiding) 등의 목적으로 이용되며, React의 유명한 hook인 useState와 같은 많은 곳에서 사용된다.

Closure 사용 시 주의사항

  • 클로저는 외부 함수의 변수를 "기억"합니다. 이로 인해 불필요한 변수가 메모리에 계속 남을 수 있어 메모리 누수가 발생할 수 있습니다. 
  • 많은 클로저를 생성할 때, 더 이상 사용되지 않는 변수들이 메모리에서 해제되지 않고 남아 있을 수 있습니다.
  • 따라서, 클로저가 더 이상 필요하지 않으면, 클로저를 할당한 변수에 null을 할당해줌으로써  명시적으로 참조를 해제하는 것이 좋습니다.

Currying

  • 여러 개의 인수를 받는 함수 하나의 인수만 받는 함수들로 변환하는 기법으로,  함수의 반환 값으로 다른 함수를 반환하고 그 반환된 함수가 외부 함수의 변수에 접근하는 클로저의 예시입니다.
function curry(fn) {
  const arity = fn.length; // 함수가 받는 인자의 개수
  return function curried(...args) {
    if (args.length >= arity) {
      return fn(...args);
    } else {
      return function(...next) {
        return curried(...args, ...next);
      };
    }
  };
}

// ES6에서는 화살표 함수와 Rest 파라미터를 이용하여 커링을 더 간단하게 구현할 수 있습니다.
const curry = (fn) => (...args) => 
  args.length >= fn.length 
  ? fn(...args) 
  : (...next) => curry(fn)(...args, ...next);
  • 커링을 사용하면 함수의 인수를 하나씩 부분적으로 전달할 수 있고, 이를 통해 더 유연한 함수 작성이 가능합니다. 특히 함수형 프로그래밍에서 유용한 기법입니다.
    • 재사용성 : 한 번 정의된 함수가 특정 인자를 고정시켜놓고 나중에 다른 값만을 받도록 할 수 있기 때문에, 함수를 재사용하기 좋습니다.
    • 더 간결한 코드 : 커링을 활용하면 특정 부분만 고정시키고, 나중에 필요한 부분을 쉽게 채울 수 있습니다. 특히 파라미터가 많을 때 유용합니다.
    • 부분 적용 : 커링을 사용하면 함수를 부분적으로 적용할 수 있기 때문에, 함수의 일부 인자만 미리 지정한 후, 나중에 나머지 인자들을 지정할 수 있습니다.
    • 함수형 프로그래밍 : 커링은 함수형 프로그래밍에서 매우 중요한 개념입니다. 순수 함수와 고차 함수와 잘 어울려 코드의 표현력을 높여줍니다.
// 이벤트 핸들러에서 커링 활용
function handleEvent(eventType) {
  return function(event) {
    console.log(`Handling ${eventType} event:`, event);
  };
}

const handleClick = handleEvent("click");
const handleMouseMove = handleEvent("mousemove");

document.querySelector("#myButton").addEventListener("click", handleClick);
document.querySelector("#myButton").addEventListener("mousemove", handleMouseMove);
728x90
반응형

'javascript' 카테고리의 다른 글

알아두면 유용한 Regex 리스트와 match, matchAll  (0) 2025.04.16
Regex (정규표현식)  (1) 2025.04.10
Single Thread와 Javascript  (0) 2025.03.31