클로저는 자바스크립트에서 가장 중요한 개념 중 하나로, "함수가 선언될 때의 렉시컬 환경(Lexical Environment)을 기억하고 접근할 수 있는 함수"를 말합니다. 이를 통해, 함수가 생성될 때의 변수와 그 값들에 접근하거나 유지할 수 있습니다.
목차
위의 목차를 클릭하면 해당 글로 자동 이동 합니다.
1. 클로저란?
MDN에서 정의한 클로저는 아래와 같습니다.
“A closure is the combination of a function and the lexical environment within which that function was declared.”
클로저는 함수와 그 함수가 선언됐을 때의 렉시컬 환경(Lexical environment)과의 조합이다.
여기서 중요하게 봐야할 부분은 함수가 선언됐을 때의 렉시컬 환경입니다.
주요 개념
렉시컬 스코프(lexical scope): 함수가 정의된 위치에 따라 해당 함수의 범위(scope)가 결정됩니다. 클로저는 이를 활용하여 함수가 선언된 당시의 스코프를 기억합니다.
function outerFunc() {
let x = 10;
let innerFunc = function () {
console.log(x);
};
innerFunc();
}
outerFunc(); // 10
위 예시에서 outerFunc 함수를 호출하면 결과로 10이 출력됩니다. 이는 innerFunc이 outerFunc 내부에서 선언했기 때문에, 상위 스코프인 outerFunc의 x 변수에 접근할 수 있어서 입니다. 즉, 자신이 속한 렉시컬 스코프를 참조 할 수 있는 것입니다.
동작 방식
- innerFunc 호출 시 함수 내부에서 변수 x를 탐색.
- 탐색 실패.
- 상위 스코프인 outerFunc 함수 내부에서 변수 x를 탐색.
2. 클로저 동작 예제
클로저의 정의로 다시 돌아가 보겠습니다.
“A closure is the combination of a function and the lexical environment within which that function was declared.”
클로저는 함수와 그 함수가 선언됐을 때의 렉시컬 환경(Lexical environment)과의 조합이다.
여기서 말하는 함수란 반환된 내부함수를 의미하고 함수가 선언될 때의 렉시컬 환경(Lexical environment)이란 내부 함수가 선언됐을 때의 스코프를 의미합니다. 즉, 클로저는 반환된 내부함수가 자신이 선언됐을 때의 환경(Lexical environment)인 스코프를 기억하여 자신이 선언됐을 때의 환경(스코프) 밖에서 호출되어도 그 환경(스코프)에 접근할 수 있는 함수를 말합니다. 간단히 말하면 클로저는 자신이 생성될 때의 환경(Lexical environment)을 기억하는 함수다라고 말할 수 있습니다.
function createCounter() {
let count = 0; // 외부 함수의 변수
return function () {
count++; // 내부 함수가 외부 변수 참조
return count;
};
}
const counter = createCounter();
console.log(counter()); // 출력: 1
console.log(counter()); // 출력: 2
위 예시 코드에서 createCounter 함수는 count를 증가시키는 내부 함수를 반환하고 생명 주기(life-cycle)를 마감했습니다. 즉, createCounter 함수는 실행된 이후 콜스택(실행 컨텍스트 스택)에서 제거되었으므로 함수 createCounter의 변수 count 또한 더이상 유효하지 않게 되어 변수 count에 접근할 수 있는 방법이 달리 없는 것 처럼 보입니다.
결과를 보면 생명 주기(life-cycle)가 종료되어 실행 컨텍스트 스택에서 제거된 함수 createCounter의 지역변수 count가 여전히 참조되어 값이 증가하고 있습니다. count 변수는 createCounter 실행 후에도 메모리에 남아 있으며, 반환된 함수는 이를 계속 참조합니다.
이처럼 자신을 포함하고 있는 외부함수보다 내부함수가 더 오래 유지되는 경우, 외부 함수 밖에서 내부함수가 호출되더라도 외부함수의 지역 변수에 접근할 수 있는데 이러한 함수를 클로저(Closure)라고 부릅니다.
주의 사항 및 해결 방법
클로저를 사용할 때 메모리 누수를 주의해야 합니다. 클로저가 참조하는 데이터가 해제되지 않으면 필요 이상의 메모리를 차지할 수 있습니다. 이를 방지하려면 사용이 끝난 클로저 참조를 제거하는 것이 좋습니다.
필요하지 않은 참조 제거
function createClosure() {
let data = { key: "value" };
return function () {
console.log(data);
};
}
const closure = createClosure();
closure(); // 사용 후
// 참조 제거
closure = null; // data도 메모리에서 해제 가능
3. 클로저 활용 사례
정보 은닉
클로저는 외부에서 접근할 수 없는 비공개 데이터를 유지하기에 적합합니다.
아래 코드에서 user는 createUser 함수 내부의 지역 변수로 선언되어 있습니다. 이는 외부에서 user 객체에 직접 접근하거나 수정할 수 없도록 설계된 것으로, 정보 은닉이 이루어진 상태입니다.
function createUser() {
let user = {
name: "kim",
age: 20,
email: "kim@email.com",
password: "1234!@",
};
this.getUser = function (email, password) {
if (user.email === email && user.password === password) {
const { password, ...rest } = user;
console.log(rest);
} else {
console.log("check email & password!");
}
};
}
const user = new createUser();
user.getUser("kim@email.com", "1234!@");
- 외부 접근 제한: user 객체는 createUser 함수 내에서만 접근 가능하며, 반환된 getUser 메서드를 통해서만 간접적으로 접근할 수 있습니다. 이는 user 객체의 민감한 정보(password)를 보호합니다.
- 안전한 데이터 접근: 외부에서 user의 속성 값을 직접 변경할 수 없으므로, user 객체의 상태는 createUser 내부에서만 유지됩니다. getUser 메서드는 인증 절차(이메일과 비밀번호 확인)를 통해서만 데이터를 노출하도록 제어합니다.
상태 유지
클로저를 이용하면 함수 호출 간 상태를 유지할 수 있습니다.
아래 코드는 사용자의 로그인 상태를 유지하는 간단한 예시입니다.
function createAuth() {
let isLogIn = false;
return {
login: function () {
isLogIn = true;
console.log("Log in!");
},
logout: function () {
isLogIn = false;
console.log("Log out!");
},
checkStatus: function () {
console.log(`Log in: ${isLoggedIn}`);
}
};
}
const auth = createAuth();
auth.checkStatus(); // Log in: false
auth.login();
auth.checkStatus(); // Log in: true
auth.logout();
auth.checkStatus(); // Log in: false
- isLogIn은 createAuth 함수의 지역 변수로, 외부에서 직접 접근할 수 없습니다.
- login, logout, checkStatus 메서드가 클로저를 형성하며, isLogIn 상태를 참조하고 업데이트합니다.
- 이를 통해 사용자 로그인 상태를 안전하게 유지합니다.
추천글
'개발 공부 일지 > JavaScript' 카테고리의 다른 글
[JS] 자바스크립트의 == 와 === 의 차이 (4) | 2024.12.11 |
---|---|
[JS] Promise.all 과 Promise.allSettled 차이 (5) | 2024.12.09 |
[JS] IntersectionObserver API란? (2) | 2024.11.04 |
[JS] Throttle과 Debounce (0) | 2024.10.31 |
[JS] Axios 오류 처리 (1) | 2024.09.25 |