본문 바로가기
CoreJavaScript-study/presentation

[발표3회차] 클로저

by 어느새벽 2024. 7. 18.

개념정리

클로저는 자바스크립트에서 중요한 개념 중 하나이다. 이 개념을 이해하려면 함수와 변수가 어떻게 동작하는 지 알아야 한다. 

우선, 함수는 특별한 종류의 '작업'을 수행하는 코드 뭉치이다. 예를들어 우리가 만든 함수를 호출하면 그 안에 있는 코드들이 실행된다.

클로저는 이 함수와 그 함수가 만들어진 환경(변수들의 집합)을 함께 기억하는 것이다. 이 말은 함수 안에서 정의된 함수가 외부 함수의 변수들에 접근할 수 있는 것이다.

function outerFunction() {
  let outerVariable = '안녕'; // 바깥 함수에서 선언된 변수

  function innerFunction() {
    console.log(outerVariable); // 바깥 함수의 변수를 사용
  }

  return innerFunction; // 내부 함수를 반환
}

let myFunction = outerFunction(); // outerFunction을 호출하고, 그 결과로 innerFunction을 얻음
myFunction(); // 결과: '안녕'

 

이 코드에서 'innerFunction'은 'outerFunction' 안에 정의돼 있고, 'outerVariable'에 접근할 수 있다. 그래서 'myFunction()'을 호출하면 '안녕'이라는 값이 출력된다.

 

이렇게 'innerFunction'이 'outerFunction'의 변수 'outerVariable'에 접근할 수 있는 건 클로저 덕분이다. 'outerFunction'이 실행될 때 'innerFunction'이 만들어지고, 'outerVariable'의 값('안녕')을 기억하고 있어서 나중에 호출할 때 사용할 수 있게 되는 것이다.

 

이 개념을 요약하자면, 클로저는 함수와 그 함수가 만들어진 환경(주변 상황, 변수들의 집합 등)을 함께 기억하여 외부 변수에 접근할 수 있는 기능이라고 할 수 있다. 자바스크립트에서 이걸 잘 이해하면 더 많은 기능을 구현할 수 있다.

 

클로저와 메모리 관리

클로저의 동작 원리 : 클로저는 함수가 생성될 때, 그 함수가 접근할 수 있는 외부 변수들을 기억하고 있는 매커니즘이다. 이는 함수가 외부 변수에 대한 참조를 유지하기 때문에 함수가 종료된 이후에도 그 변수들이 메모리에 유지될 수 있게 해준다.

 

메모리 누수의 가능성 : 클로저가 사용될 떄 주의해야 할 점은 메모리 누수Memory Leak의 가능성이 있다는 것이다. 클로저가 외부 변수들에 대한 참조를 계속 유지하고 있기 때문에, 그 변수들이 더 이상 필요하지 않더라도 메모리에서 해제되지 않을 수 있다.

 

올바른 사용 패턴 : 클로저를 사용할 때 메모리 누수를 줄이기 위해 몇 가지 패턴을 따르는 것이 좋다. 예를 들어, 클로저가 외부 변수들을 오래 유지할 필요가 없다면 함수 내부에서 사용한 변수들을 필요 이상으로 클로저로 포함시키지 않는 것이 좋다.

 

자원해제: 클로저를 사용할 때 주의할 점 중 하나는 자원을 해제하는 것이다. 예를 들어, 이벤트 리스너를 클로저로 만들 경우, 이벤트 리스너가 필요하지 않을 때 명시적으로 제거하거나, 더 이상 필요하지 않은 클로저의 참조를 제거하여 메모리를 해제하는 것이 중요하다.

 

성능 고려 : 클로저는 유용한 기능이지만, 과도하게 사용하면 성능에 영향을 줄 수 있다. 클로저는 함수가 실행될 때마다 새로운 환경을 만들어야 하기 때문에, 반복적으로 사용되는 함수에서는 성능 저하의 원인이 될 수 있다.

 

클로저는 사용할 때는 이러한 점들으 고려하여 적절하게 관리해야 하며, 메모리 관리 측면에서도 주의를 기울어야 한다. 

 

부분 적용 함수

부분 적용 함수란 원본 함수에서 일부 인자를 고정하고(적용하고), 나머지 인자를 나중에 전달할 때 사용할 수 있는 새로운 함수를 생성하는 기법이다. 이 과정에서 원본 함수의 일부 매개변수가 이미 결정되어 있지만, 나머지 매개변수는 나중에 제공될 수 있어야 한다.

function multiply(a, b) {
  return a * b;
}

 

여기서 'multiply' 함수를 부분 적용하여 첫번째 인자를 고정한 후, 새로운 함수를 만들 수 있다.

let multiplyByTwo = multiply.bind(null, 2);

 

이렇게 하면 'multiplyByTwo'는 첫번째 인자 'a'가 2로 고정된 새로운 함수가 되어, 두번째 인자 'b'만 전달하면 곱셈 연산을 할 수 있다.

let result = multiplyByTwo(5); // result는 10

 

위 예제에서 'multiplyByTwo(5)'를 호출하면 내부적으로 'multiply(2, 5)'가 실행되어 10을 반환하는 것이다.

 

활용 : 부분 적용 함수는 함수를 재사용하고 코드를 더 모듈화할 수 있는 효과적인 방법이다. 특히 함수의 일부 인자가 반복적으로 사용되거나, 특정 상황에서 기본 인자를 미리 설정해 놓고 나중에 필요에 따라 추가 인자를 제공할 때 유용하다.

 

부분 적용 함수는 함수형 프로그래밍에서 고차 함수와 관련이 있으며, 자바스크립트에서는 'bind' 메서드를 통해 쉽게 구현할 수 있다. 함수를 부분 적용할 때는 원본 함수의 매개변수 순서에 주의하고, 필요에 따라 'this' 컨텍스트도 설정해야 한다.

 

커링함수

커링은 원래 수학자 하스켈 커리의 이름에서 유래된 용어로, 여러 개의 인자를 받는 함수를 한번에 하나의 인자를 받는 함수들로 분해하는 과정이다. 즉, 커링 함수는 여러 개의 인자를 받는 함수를 함수들의 연쇄로 변환하는 기법이라 할 수 있다.

function add(a, b) {
  return a + b;
}

 

위의 'add'함수를 커링 함수로 변환하면 다음과 같다

function curriedAdd(a) {
  return function(b) {
    return a + b;
  };
}

// 또는 ES6의 화살표 함수를 사용하여 표현할 수도 있어요.
const curriedAdd = a => b => a + b;

 

이제 'curriedAdd' 함수는 한번에 하나의 인자를 받는 함수를 반환한다. 예를 들어,

let addFive = curriedAdd(5);
let result = addFive(3); // result는 8

 

위 예제에서 'addFive'은 'curriedAdd' 함수에 인자 '5'를 전달하여 반환된 함수이고, 이 함수에 다시 인자 '3'을 전달하면 '5+3'이 계산되어 '8'이 결과로 나오게 된다.

 

커링함수는 재사용하고 부분 적용을 쉽게 구현할 수 있게 해준다. 특히 함수의 일부 인자가 반복적으로 사용되거나, 특정 상황에서 기본 인자를 미리 설정해 놓고 나중에 필요에 따라 추가 인자를 제공할 때 유용하다.

 

다중 인자 함수와의 차이점 : 커링 함수는 다중 인자를 받는 함수를 변환하여 단일 인자를 받는 함수들로 나누는 것이지만, 부분 적용 함수와 비슷하지만 조금 다른 개념이다. 부분 적용 함수는 여러 인자 중 일부를 고정하고 나머지 인자를 나중에 전달할 수 있게 만드는 것이고, 커링은 인자를 하나씩 받는 함수들로 분해하는 것이다.