개발/JavaScript

[JavaScript] 클로저

xuwon 2024. 11. 13. 18:26

자바스크립트에서 중요한 개념인 클로저!

클로저

함수가 자신이 선언될 때의 환경(변수, 상수 등)에 접근할 수 있는 권한을 가진 함수

 

일반적으로 함수가 실행을 마치고 종료되면, 그 함수 내에서 선언된 변수들은 소멸하게 접근할 수 없게 되지만,
클로저는 함수가 종료된 후에도 그 함수가 선언된 시점의 환경을 기억하기 때문에, (렉시컬 스코프)
외부 함수가 종료된 이후에도 내부 함수에서 외부 함수의 변수에 접근할 수 있다.

 

클로저의 생성 원리

주로 함수 내에 중첩된 함수가 있을 때 생성

자바스크립트는 함수가 생성될 때 외부 함수의 변수를 기억하는 방식으로 클로저를 형성함!

 

클로저의 동작 방식

1. 중첩된 함수에서 외부 함수의 변수에 접근할 때 중첩 함수가 외부 함수의 변수들을 계속 참조할 수 있음

2. 외부 함수가 실행을 마쳐도 외부 함수의 변수는 메모리에서 제거 X
Why? 내부 함수가 외부 함수의 변수를 참조하고 있기 때문.

3. 이렇게 내부 함수가 외부 함수의 변수에 접근할 수 있게 된 것을 클로저라고 한다.

function outerFunction() {
    const outerVariable = 'I am from outer';

    function innerFunction() {
        console.log(outerVariable); // 'I am from outer' 출력
    }

    return innerFunction;
}

const closure = outerFunction();
closure(); // outerVariable을 참조하여 'I am from outer' 출력

innerFunctionouterFunction이 종료된 후에도 outerVariable에 접근 가능
outerFunction이 실행을 마쳐도, innerFunctionouterVariable을 참조하고 있기 때문에
outerVariable은 메모리에서 제거되지 않고 남아있음

 

클로저는 상태를 유지하는 데 유용하게 쓰인다.

function counter() {
    let count = 0; // 상태 유지

    return function() {
        count += 1;
        console.log(count);
    };
}

const increment = counter();
increment(); // 1
increment(); // 2
increment(); // 3

counter 함수가 실행되면서 count 변수가 생성되고, count 값을 증가시키는 함수가 반환된다.
incrementcount의 상태를 클로저로 유지하고 있으므로, increment를 호출할 때마다 count 값이 1씩 증가!

 

클로저와 반복문

function createFunctions() {
    const funcs = [];
    
    for (let i = 0; i < 3; i++) {
        funcs.push(function() {
            console.log(i);
        });
    }
    
    return funcs;
}

const functions = createFunctions();
functions[0](); // 0
functions[1](); // 1
functions[2](); // 2

for 반복문을 통해 func 배열에는

function() { console.log(1) }
function() { console.log(2) }
function() { console.log(3) }

이 차례대로 push 된다.

→ 각 함수는 각각의 i 값을 캡처하여 클로저로 형성함!

즉, 각각의 i 값을 기억할 수 있게 됨.

 

* 만약 var로 i를 선언했다면?

var는 블록 스코프가 아닌 함수 스코프를 갖기 때문에,
for 반복문을 스코프로 인식하지 않음!

즉, for 반복문에서 i는 반복문이 종료될 때까지 하나의 동일한 변수로 존재함.
for 블록 내부에서 반복할 때마다 새로 생성 X, 하나의 i가 반복 내내 사용

반복문 종료 후 i3이 되고, func 배열에 저장된 함수들은 모두 마지막에 할당된 i 값인 3을 참조함.

 

클로저와 메모리 관리

클로저는 강력한 기능이지만, 잘못 사용하면 메모리 누수가 발생할 수 있다.
클로저는 외부 함수의 변수를 참조하므로, 이 변수가 메모리에서 제거되지 않고 남아있을 수 있다.

function outer() {
    let largeData = new Array(1000000).fill('*'); // 큰 메모리를 차지하는 데이터

    return function() {
        console.log('Using large data');
    };
}

const inner = outer();
// inner가 참조 중인 largeData는 메모리에서 해제되지 않음

클로저가 생성될 때, 자바스크립트 엔진은 outer 함수에서 선언된 모든 변수에 접근할 수 있도록 내부 환경을 기억한다.
→ 즉, inner에서 직접 largeData에 접근하지 않더라도, 클로저에 의해 outer 스코프가 유지되어 largeData가 메모리에 계속 남아있는 것!

 

클로저의 장단점


장점

1. 데이터 은닉
외부에서 접근할 수 없는 변수를 클로저로 내부에서만 사용 가능하게 하여 데이터 보호

2. 상태 유지
클로저를 통해 상태를 유지하는 함수 생성 가능.

 

단점

1. 메모리 관리 어려움
불필요한 변수 참조로 인해 메모리 누수 발생 가능성 Up

2. 디버깅 난이도
클로저는 함수와 그 환경을 묶어두기 때문에 디버깅이 어렵고, 클로저의 상태 변화를 추적하기가 어려울 수 있음.