자바스크립트 클로저

자바스크립트 클로저

열세 번째 포스팅에서 스코프를 다루었다면, 이번에는 "문법 및 핵심 개념" 범주에 속하는 "클로저"를 탐구한다. 클로저(Closure)는 함수가 외부 스코프의 변수를 기억하고 접근할 수 있는 메커니즘으로, 데이터 캡슐화와 상태 관리에 활용된다.


클로저는 스코프와 밀접하게 연관되며, 자바스크립트의 강력한 기능을 제공한다. 이번 포스팅에서는 클로저의 개념, 동작 원리, 활용 방법, 그리고 실습 예제를 작성해볼 예정이다.


스코프와 함께 클로저는 자바스크립트의 고급 문법을 이해하는 데 필수적이다. 콘솔에서 예제를 실행하며 클로저의 동작을 체감할 수 있다.


클로저의 개념

클로저는 함수가 선언된 스코프의 변수를 함수 종료 후에도 기억하는 현상이다. 이는 함수와 스코프 체인이 결합된 결과이다. 다음은 기본적인 클로저 예제이다:

function outer() {
    var outerVar = '외부 변수';
    function inner() {
        console.log(outerVar);
    }
    return inner;
}
const innerFunc = outer();
innerFunc(); // "외부 변수"
        

위 예제는 내부 함수가 외부 변수에 접근한다.

클로저는 데이터의 비공개성과 상태 유지를 가능하게 한다. 이는 자바스크립트의 함수형 프로그래밍에서 핵심이다.


클로저의 동작 원리

클로저는 실행 컨텍스트와 스코프 체인에 의해 구현된다.


1. 스코프 체인

내부 함수는 외부 스코프를 참조한다:

function outer() {
    var x = 10;
    return function() {
        console.log(x);
    };
}
const closure = outer();
closure(); // 10
        

2. 메모리 유지

외부 함수가 종료되어도 변수는 유지된다:

function createCounter() {
    var count = 0;
    return function() {
        return ++count;
    };
}
const counter = createCounter();
console.log(counter()); // 1
console.log(counter()); // 2
        

예제: 클로저 메모리

<!DOCTYPE html>
<html lang="ko">
<head>
    <meta charset="UTF-8">
    <title>클로저 메모리</title>
</head>
<body>
    <button id="btn">증가</button>
    <div id="count">0</div>
    <script>
        const btn = document.getElementById('btn');
        const countDiv = document.getElementById('count');
        const increment = (function() {
            var count = 0;
            return function() {
                return ++count;
            };
        })();

        btn.addEventListener('click', () => {
            countDiv.textContent = increment();
        });
    </script>
</body>
</html>
        

이 예제는 클로저로 카운터를 유지한다.


클로저 활용

클로저는 다양한 상황에서 활용된다.


1. 데이터 캡슐화

비공개 변수를 생성한다:

function createPerson(name) {
    var age = 0;
    return {
        getName: () => name,
        getAge: () => age,
        setAge: (newAge) => age = newAge
    };
}
const person = createPerson('홍길동');
console.log(person.getName()); // "홍길동"
console.log(person.getAge()); // 0
person.setAge(25);
console.log(person.getAge()); // 25
        

2. 함수 팩토리

동적인 함수를 생성한다:

function multiplier(factor) {
    return function(num) {
        return num * factor;
    };
}
const double = multiplier(2);
console.log(double(5)); // 10
        

예제: 클로저 팩토리

<!DOCTYPE html>
<html lang="ko">
<head>
    <meta charset="UTF-8">
    <title>클로저 팩토리</title>
</head>
<body>
    <input type="number" id="input" value="5">
    <button id="doubleBtn">2배</button>
    <button id="tripleBtn">3배</button>
    <div id="result"></div>
    <script>
        function createMultiplier(factor) {
            return function(num) {
                return num * factor;
            };
        }
        const double = createMultiplier(2);
        const triple = createMultiplier(3);

        const input = document.getElementById('input');
        const doubleBtn = document.getElementById('doubleBtn');
        const tripleBtn = document.getElementById('tripleBtn');
        const result = document.getElementById('result');

        doubleBtn.addEventListener('click', () => {
            result.textContent = double(parseInt(input.value));
        });

        tripleBtn.addEventListener('click', () => {
            result.textContent = triple(parseInt(input.value));
        });
    </script>
</body>
</html>
        

이 예제는 클로저로 곱셈 함수를 생성한다.


실습 예제: 클로저 상태 관리

클로저를 활용한 상태 관리 예제이다:

<!DOCTYPE html>
<html lang="ko">
<head>
    <meta charset="UTF-8">
    <title>클로저 상태</title>
    <style>
        #toggleBox {
            width: 100px;
            height: 100px;
            background: gray;
        }
    </style>
</head>
<body>
    <button id="toggleBtn">토글</button>
    <div id="toggleBox"></div>
    <script>
        const toggleBtn = document.getElementById('toggleBtn');
        const toggleBox = document.getElementById('toggleBox');
        const toggleState = (function() {
            var isOn = false;
            return function() {
                isOn = !isOn;
                toggleBox.style.backgroundColor = isOn ? 'red' : 'gray';
                return isOn;
            };
        })();

        toggleBtn.addEventListener('click', () => {
            toggleState();
        });
    </script>
</body>
</html>
        

이 예제는 클로저로 상태를 관리한다.

Closure Example

실무에서의 클로저

실무에서는 이벤트 핸들러에 활용한다:

<!DOCTYPE html>
<html lang="ko">
<head>
    <meta charset="UTF-8">
    <title>클로저 이벤트</title>
</head>
<body>
    <button id="btn">클릭</button>
    <div id="log"></div>
    <script>
        const btn = document.getElementById('btn');
        const log = document.getElementById('log');
        btn.addEventListener('click', (function() {
            var clicks = 0;
            return function() {
                clicks++;
                log.textContent = `클릭 횟수: ${clicks}`;
            };
        })());
    </script>
</body>
</html>
        

이 예제는 클로저로 클릭 횟수를 기록한다.


주의할 점

클로저는 메모리를 유지하므로 누수가 발생할 수 있다. 불필요한 클로저는 정리한다. 복잡한 클로저는 가독성을 저하시킨다.


결론

클로저의 개념, 활용을 학습하며 실습 예제를 통해 이해한다. 다음 포스팅에서는 this 바인딩에 대해 작성해보려고 한다.

+ Recent posts