본문 바로가기

개발 공부

클로저(Closure)

클로저(Closure)란 무엇인가?

1.1 정의

클로저(Closure)는 함수와 그 함수가 선언된 어휘적 환경(lexical environment)의 조합입니다.

간단히 말해, 클로저는 특정 함수가 외부 함수의 스코프에 있는 변수를 기억하는 능력을 말합니다.

이로 인해 클로저는 외부 함수의 변수를 계속해서 접근할 수 있습니다.

1.2 기본 개념

  • 함수 스코프: 자바스크립트의 함수는 자신이 선언된 스코프에 대한 접근 권한을 가집니다.
  • 변수의 생명 주기: 일반적으로 함수가 종료되면 그 함수 내에서 선언된 변수는 사라집니다. 그러나 클로저는 외부 함수의 변수를 유지할 수 있습니다.

2. 클로저의 특징

2.1 상태 유지

클로저는 함수가 호출된 후에도 변수를 유지합니다. 이는 데이터 캡슐화를 가능하게 합니다.

2.2 고차 함수

클로저는 고차 함수(higher-order function)와 밀접한 관계가 있습니다. 고차 함수는 다른 함수를 인자로 받거나, 함수를 반환하는 함수입니다.

3. 클로저의 활용 사례

3.1 데이터 은닉

클로저는 객체의 상태를 숨기고, 인터페이스를 통해서만 접근하도록 할 수 있습니다. 이는 데이터 보호와 유효성 검사를 도와줍니다.

3.2 함수 팩토리

클로저는 특정 작업을 수행하는 함수를 생성하는 데 유용합니다. 함수 팩토리 패턴을 사용하여 클로저를 활용할 수 있습니다.

4. TypeScript에서의 클로저 예제

4.1 기본 클로저 예제

function makeCounter() {
    let count = 0; // 클로저가 접근할 수 있는 변수

    return function() {
        count += 1;
        return count;
    };
}

const counter = makeCounter();
console.log(counter()); // 1
console.log(counter()); // 2
console.log(counter()); // 3

 

count 변수를 포함하는 클로저를 반환합니다.

counter를 호출할 때마다 count의 값이 증가하고, 외부에서 직접 접근할 수 없습니다.

 

4.2 데이터 은닉 예제

function createPerson(name: string) {
    let age = 0; // private variable

    return {
        setAge(newAge: number) {
            age = newAge;
        },
        getAge() {
            return age;
        },
        getName() {
            return name;
        }
    };
}

const person = createPerson("Alice");
person.setAge(30);
console.log(`${person.getName()} is ${person.getAge()} years old.`); // Alice is 30 years old.

 

age 변수를 외부에서 직접 접근할 수 없도록 하여, setAge와 getAge 메서드를 통해서만 접근합니다.

5. 클로저의 장단점

5.1 장점

  • 데이터 은닉: 외부에서 접근할 수 없는 변수를 만들 수 있어 데이터 보호에 유리합니다.
  • 상태 유지: 상태를 유지하여, 여러 번 호출되는 상황에서도 이전 상태를 기억할 수 있습니다.

5.2 단점

  • 메모리 사용: 클로저가 참조하는 변수가 메모리에 계속 유지되므로, 메모리 누수(leak) 문제가 발생할 수 있습니다.
  • 디버깅 어려움: 클로저의 상태를 추적하기 어려울 수 있어 디버깅이 복잡해질 수 있습니다.

6. 클로저와 관련된 패턴

6.1 모듈 패턴 (Module Pattern)

모듈 패턴은 클로저를 활용하여 객체나 함수를 감싸는 구조로, 외부에서는 접근할 수 없는 비공개(private) 상태를 유지하면서, 외부에 노출할 메서드나 데이터를 선택적으로 제공하는 패턴입니다. 이 패턴은 클로저를 사용하여 변수나 함수에 대한 접근을 제한함으로써 데이터 은닉성을 보장하고 코드의 모듈성을 강화합니다.

6.1.1 모듈 패턴의 특징

  • 비공개 상태 유지: 모듈 내부의 데이터는 클로저를 통해 은닉되며, 외부에서 직접 접근할 수 없습니다.
  • 공개 메서드 제공: 모듈 패턴은 데이터나 기능을 공개할 때는 특정 인터페이스를 통해 접근할 수 있도록 합니다. 이렇게 하면 사용자나 다른 코드에서 허가된 방식으로만 모듈에 접근하게 됩니다.
const counterModule = (() => {
    let count = 0; // 비공개 상태

    return {
        increment() {
            count += 1;
            return count;
        },
        decrement() {
            count -= 1;
            return count;
        },
        getCount() {
            return count;
        }
    };
})();

console.log(counterModule.increment()); // 1
console.log(counterModule.increment()); // 2
console.log(counterModule.getCount());  // 2
console.log(counterModule.decrement()); // 1

6.1.3 모듈 패턴의 장점

  1. 데이터 은닉: count 변수는 모듈 내부에만 존재하므로 외부에서는 직접 수정할 수 없습니다. 이렇게 데이터 보호 및 보안이 강화됩니다.
  2. 캡슐화: 모듈 패턴은 데이터를 캡슐화하여 상태를 보호하면서, 필요한 부분만 외부에 노출시킬 수 있습니다.
  3. 의도된 인터페이스 제공: 사용자가 모듈의 메서드를 통해서만 데이터를 변경하거나 조회할 수 있으므로, 코드의 유지보수성과 가독성이 향상됩니다.

6.2 일급 함수 (First-Class Functions)

자바스크립트에서 일급 함수란, 함수를 다른 변수처럼 다룰 수 있다는 개념을 말합니다. 즉, 함수를 변수에 할당하거나, 함수의 인자로 전달하거나, 함수에서 반환하는 등 다양한 방식으로 활용할 수 있습니다. 클로저는 이러한 일급 함수의 특성을 잘 활용하여 함수형 프로그래밍 패러다임을 가능하게 합니다.

6.2.1 일급 함수의 특징

  1. 함수는 값처럼 사용: 함수를 변수에 할당하거나, 객체의 속성으로 저장할 수 있습니다.
  2. 함수를 인자로 전달 가능: 함수를 다른 함수의 인자로 전달하여 동작을 변경하거나 제어할 수 있습니다.
  3. 함수를 반환할 수 있음: 함수가 또 다른 함수를 반환하여, 고차 함수를 쉽게 구현할 수 있습니다.

6.2.2 클로저와 일급 함수의 관계

클로저는 함수가 외부 스코프의 변수를 참조할 수 있기 때문에, 함수가 일급 객체로 다뤄질 때 매우 유용하게 작동합니다. 예를 들어, 함수가 다른 함수 내부에서 정의되었을 때, 그 함수가 외부 함수의 변수에 접근하여 상태를 기억할 수 있습니다.

function multiplier(factor: number) {
    return function(number: number) {
        return number * factor;
    };
}

const double = multiplier(2);
const triple = multiplier(3);

console.log(double(5)); // 10
console.log(triple(5)); // 15

 

multiplier 함수는 factor라는 값을 클로저로 캡처합니다. 이로 인해 double과 triple 함수는 자신이 생성된 시점에 주어진 factor 값을 기억하며, 이를 이용해 입력 값을 각각 두 배, 세 배로 계산합니다.

6.2.4 일급 함수의 장점

  1. 고차 함수 구현: 일급 함수는 고차 함수(higher-order function)를 쉽게 구현할 수 있어, 재사용 가능한 코드를 만들 수 있습니다.
  2. 함수형 프로그래밍: 일급 함수와 클로저의 결합은 자바스크립트에서 함수형 프로그래밍을 지원하는 중요한 기초입니다.
  3. 유연한 코드: 함수를 값으로 다룰 수 있기 때문에, 코드를 동적으로 생성하거나 행동을 제어하는 유연한 로직을 구성할 수 있습니다.