📘 6.2.2 클로저는 언제 만들어질까요? - 특별한 조건들을 알아보기
지난 시간에 우리는 클로저라는 놀라운 개념을 만났습니다. 함수가 마치 개인 서랍장처럼 자신의 환경을 기억하는 신기한 능력을 가질 수 있다는 것을 알게 되었죠.
오늘은 한 걸음 더 나아가서 "그렇다면 이런 능력은 언제 생기는 걸까요?"라는 자연스러운 궁금증을 풀어보겠습니다. 바로 클로저가 만들어지는 특별한 조건들에 대해 알아보는 시간입니다. 함께 이 흥미로운 탐험을 떠나보겠습니다.
🧠 새로운 단어들과 친해지기
클로저가 언제 만들어지는지 이해하는 데 도움이 되는 단어들을 알아보겠습니다.
단어 | 의미 |
---|---|
클로저 만들어지는 조건 | 클로저가 만들어지기 위해 반드시 필요한 상황들을 의미합니다. 마치 무지개가 나타나기 위한 조건 같습니다. |
안쪽 함수 (inner function) | 다른 함수 안에서 만들어진 함수를 말합니다. 마치 상자 안의 작은 상자 같습니다. |
바깥쪽 변수 사용하기 | 안쪽 함수가 바깥쪽 함수의 변수를 사용하는 것을 의미합니다. 마치 집 안의 공용 물건을 사용하는 것 같습니다. |
함수 돌려주기 | 함수를 실행 결과로 돌려주는 것을 말합니다. 마치 선물을 정성껏 포장해서 전달하는 것 같습니다. |
클로저 생기는 순간 | 클로저가 정확히 언제 만들어지는지를 나타내는 순간입니다. 마치 별이 태어나는 순간 같습니다. |
'클로저가 만들어지는 조건'이라는 말은 마치 "특별한 일이 일어나기 위한 조건들"과 같습니다. 모든 조건이 딱 맞아떨어져야만 신기한 클로저 능력이 생길 수 있습니다.
✨ 클로저 만들어지는 조건, 그것은 무엇일까요?
클로저가 만들어지는 것은 정말 특별한 일입니다. 마치 무지개가 나타나려면 비와 햇빛이 동시에 있어야 하는 것처럼, 클로저도 특정한 조건들이 모두 갖춰져야만 만들어집니다.
클로저가 만들어지기 위해서는 정확히 3가지 조건이 모두 필요합니다. 이 조건들 중 하나라도 빠지면 클로저는 만들어지지 않습니다. 마치 케이크를 만들 때 밀가루, 계란, 설탕이 모두 있어야 하는 것과 같습니다.
첫 번째 조건은 함수 안에 또 다른 함수가 있어야 한다는 것입니다. 이를 함수 안의 함수라고 합니다. 두 번째 조건은 안쪽 함수가 바깥쪽 함수의 변수를 사용해야 한다는 것입니다. 세 번째 조건은 안쪽 함수가 바깥으로 나가야 한다는 것입니다(return되거나 다른 곳에 저장되거나).
이 모든 조건이 갖춰지면 놀라운 일이 일어납니다. 바깥쪽 함수가 끝나더라도 안쪽 함수는 여전히 바깥쪽 함수의 변수들을 기억하고 사용할 수 있게 되는 것입니다.
마음속 이야기: 우리 학교의 특별한 졸업식
클로저가 만들어지는 조건을 더 쉽게 이해하기 위해 '우리 학교의 특별한 졸업식' 이야기를 들어보겠습니다.
우리 학교에는 참 특별한 졸업식이 있습니다. 이 졸업식에서는 학생들이 특별한 능력을 받을 수 있는데, 그러려면 3가지 조건을 모두 만족해야 합니다.
첫 번째 조건은 선생님과 제자가 함께 있어야 한다는 것입니다. 마치 바깥쪽 함수(선생님) 안에 안쪽 함수(제자)가 있어야 하는 것과 같습니다. 선생님 혼자 있거나 제자 혼자 있어서는 특별한 능력이 생기지 않습니다.
두 번째 조건은 제자가 선생님의 도구를 사용해야 한다는 것입니다. 제자가 선생님의 교과서나 실험 도구를 사용해서 뭔가를 해봐야 합니다. 이는 안쪽 함수가 바깥쪽 함수의 변수를 사용하는 것과 같습니다.
세 번째 조건은 제자가 학교를 졸업해서 세상으로 나가야 한다는 것입니다. 제자가 학교에만 계속 있다면 특별한 능력을 받을 수 없습니다. 이는 안쪽 함수가 바깥으로 반환되는 것과 같습니다.
이 3가지 조건이 모두 갖춰지면 놀라운 일이 일어납니다! 제자가 졸업해서 학교를 떠나도(바깥쪽 함수가 끝나도), 여전히 선생님의 도구들을 사용할 수 있게 됩니다(바깥쪽 함수의 변수에 접근 가능). 이것이 바로 클로저의 특별한 능력입니다.
🎯 왜 클로저가 언제 만들어지는지 배워야 할까요?
그렇다면 우리는 왜 클로저가 언제 만들어지는지 배워야 할까요? 여러 소중한 이유들이 있습니다.
첫째, 원하는 대로 클로저를 만들 수 있습니다. 클로저가 필요한 상황에서 확실하게 만들 수 있고, 클로저가 필요하지 않은 상황에서는 만들지 않을 수 있습니다.
둘째, 코드의 문제를 찾아 해결할 수 있습니다. "왜 내 코드가 예상대로 동작하지 않을까?"라고 궁금할 때, 클로저가 만들어졌는지 또는 만들어지지 않았는지 확인해서 문제를 해결할 수 있습니다.
셋째, 컴퓨터 메모리를 효율적으로 사용할 수 있습니다. 클로저는 변수들을 계속 기억하고 있어서 메모리를 사용하는데, 언제 만들어지는지 알면 불필요한 클로저 생성을 피할 수 있습니다.
마지막으로, 고급 프로그래밍 기법을 이해할 수 있습니다. 많은 아름다운 프로그래밍 기법들이 클로저를 바탕으로 만들어져 있는데, 클로저가 만들어지는 조건을 알면 이런 기법들을 더 잘 이해할 수 있습니다.
⚙️ 기본 사용법 배우기
클로저가 만들어지는 조건은 특별한 문법이 있는 것은 아닙니다. 대신 우리가 이미 알고 있는 함수 문법들이 특정한 조건으로 배치될 때 클로저가 만들어집니다.
🔹 클로저가 만들어지는 3가지 필수 조건
function 바깥쪽함수() {
// 🔸 1단계: 바깥쪽 함수의 변수 (기억할 환경 준비)
let 기억할변수 = "중요한 정보";
// 🔸 2단계: 안쪽 함수 선언 (함수 안의 함수 조건 충족)
function 안쪽함수() {
// 🔸 3단계: 바깥쪽 변수 사용 (바깥쪽 변수 사용 조건 충족)
return 기억할변수; // 이 부분이 핵심!
}
// 🔸 4단계: 안쪽 함수 반환 (바깥으로 내보내기 조건 충족)
return 안쪽함수; // 클로저 완성!
}
// 🎉 모든 조건이 갖춰져서 클로저 생성됨!
let 클로저함수 = 바깥쪽함수();
🔹 클로저가 만들어지는 조건 체크리스트
클로저가 만들어지려면 다음 조건들을 확인해야 합니다.
함수 안의 함수: 함수 안에 함수가 있어야 하고,
바깥쪽 변수 사용하기: 안쪽 함수가 바깥쪽 변수를 사용해야 하며,
함수 돌려주기: 안쪽 함수가 바깥으로 나가야 합니다.
이 세 조건이 모두 충족되어야만 클로저가 생성됩니다.
🎯 5단원 복습 문제 - 함수 연습하기
6단원을 배우기 전에 5단원에서 배운 함수를 복습해보겠습니다. 중첩 함수와 함수 반환은 클로저를 만드는 핵심 요소입니다.
시간의 강을 거슬러 올라가 우리가 함께 배운 함수들을 다시 한 번 들여다보는 것은, 마치 소중한 기억들을 차근차근 되짚어보는 것과 같습니다. 그 안에는 우리가 함께 쌓아온 배움의 온기가 고스란히 남아 있습니다.
복습 문제 1: 중첩 함수의 기본 구조 이해하기
// 중첩 함수가 바깥쪽 변수를 어떻게 사용하는지 확인하기
function outerFunction() {
let outerVariable = "바깥쪽 변수"; // 바깥쪽 함수의 변수
let anotherVariable = "또 다른 변수"; // 바깥쪽 함수의 또 다른 변수
console.log("바깥쪽 함수 실행:", outerVariable);
function innerFunction() {
// 안쪽 함수는 바깥쪽 변수들을 자유롭게 사용 가능
console.log("안쪽 함수에서 사용:", outerVariable);
console.log("안쪽 함수에서 사용:", anotherVariable);
let innerVariable = "안쪽 변수"; // 안쪽 함수만의 변수
console.log("안쪽 함수만의 변수:", innerVariable);
}
innerFunction(); // 안쪽 함수 실행하기
// console.log(innerVariable); // 오류! 바깥쪽에서는 안쪽 변수에 접근 불가
}
// 중첩 함수 테스트하기
console.log("=== 중첩 함수 복습하기 ===");
outerFunction();
해답과 설명: 중첩 함수에서는 안쪽 함수가 바깥쪽 함수의 모든 변수에 접근할 수 있습니다. 하지만 바깥쪽 함수는 안쪽 함수의 변수에 접근할 수 없습니다. 이는 마치 집 안의 방에서는 거실의 물건을 사용할 수 있지만, 거실에서는 방 안의 물건을 직접 가져올 수 없는 것과 같습니다.
복습 문제 2: 함수를 반환하는 방법 연습하기
// 함수를 반환하는 것과 함수 실행 결과를 반환하는 것의 차이
function returnFunction() {
let message = "안녕하세요!";
function greeting() {
return message + " 함수를 반환했어요!";
}
return greeting; // 함수 자체를 반환 (클로저의 기본!)
}
function returnResult() {
let message = "안녕하세요!";
function greeting() {
return message + " 함수 결과를 반환했어요!";
}
return greeting(); // 함수 실행 결과를 반환 (클로저 아님)
}
// 차이점 확인하기
console.log("=== 함수 반환 복습하기 ===");
let functionResult = returnFunction(); // 함수를 받기
let directResult = returnResult(); // 실행 결과를 받기
console.log("함수를 받은 경우:");
console.log(typeof functionResult); // "function" 출력
console.log(functionResult()); // 함수를 실행해서 결과 얻기
console.log("실행 결과를 받은 경우:");
console.log(typeof directResult); // "string" 출력
console.log(directResult); // 바로 결과 확인하기
해답과 설명: 함수를 반환하면 나중에 원할 때 그 함수를 실행할 수 있습니다. 하지만 함수 실행 결과를 반환하면 바로 그 값만 받게 되어서 나중에 다시 실행할 수 없습니다. 클로저를 만들려면 반드시 함수 자체를 반환해야 합니다.
🧪 직접 해보면서 배우기
이제 실제 예제들을 통해 클로저가 언제 만들어지고 언제 만들어지지 않는지 차근차근 살펴보겠습니다.
🔹 예제 1: 클로저 탐정이 되어보기
첫 번째 예제에서는 여러 가지 상황을 보고 클로저가 만들어지는지 탐정처럼 찾아보겠습니다.
// Ex1) 클로저 탐정 게임에서 진짜 클로저를 찾아보자!
// 클로저 탐정 게임 - 각 경우를 분석해보자!
console.log("🕵️♂️ 클로저 탐정 게임 시작!");
// 사건 1: 의심스러운 함수 (클로저일까?)
function case1() {
let evidence = "증거품"; // 바깥쪽 변수 만들기
console.log("사건 1: 증거품 발견 -", evidence); // 증거품 발견 메시지 출력하기
return evidence; // 함수가 아닌 값만 반환하기 (클로저 조건 불충족!)
}
// 사건 2: 또 다른 의심스러운 함수
function case2() {
let witness = "목격자"; // 바깥쪽 변수 만들기
function detective() { // 안쪽 함수 만들기
let clue = "단서"; // 안쪽 함수만의 변수 만들기 (바깥쪽 변수를 사용하지 않음!)
console.log("사건 2: 단서 발견 -", clue); // 단서 발견 메시지 출력하기
return clue; // 안쪽 변수만 반환하기
}
return detective; // 함수는 반환하지만 바깥쪽 변수 미사용으로 진짜 클로저 아님
}
// 사건 3: 진짜 의심스러운 함수
function case3() {
let secret = "비밀 정보"; // 바깥쪽 변수 만들기
function investigator() { // 안쪽 함수 만들기
console.log("사건 3: 비밀 발견 -", secret); // 바깥쪽 변수 사용하기 (조건 충족!)
return secret; // 바깥쪽 변수 반환하기
}
investigator(); // 함수를 실행만 하고 반환하지 않기 (조건 불충족!)
return "수사 완료"; // 함수가 아닌 문자열만 반환하기
}
// 사건 4: 완벽한 클로저 사건!
function case4() {
let masterKey = "마스터 열쇠"; // 바깥쪽 변수 만들기
function inspector() { // 안쪽 함수 만들기
console.log("사건 4: 마스터 열쇠로 해결 -", masterKey); // 바깥쪽 변수 사용하기 (조건 충족!)
return masterKey; // 바깥쪽 변수 반환하기
}
return inspector; // 안쪽 함수 자체를 반환하기 (모든 조건 충족!)
}
// 탐정 결과 발표
console.log("\n--- 탐정 결과 발표 ---");
console.log("사건 1 수사:");
let result1 = case1(); // 단순한 함수 실행하기
console.log("결과:", result1, "(클로저 아님)"); // 문자열 결과 확인하기
console.log("\n사건 2 수사:");
let suspect2 = case2(); // 함수는 받았지만 클로저인가?
suspect2(); // 실행해보면 바깥쪽 변수를 사용하지 않음
console.log("결과: 클로저 아님 (바깥쪽 변수 미사용)");
console.log("\n사건 3 수사:");
let result3 = case3(); // 함수가 아닌 문자열만 받기
console.log("결과:", result3, "(클로저 아님)"); // 문자열 결과 확인하기
console.log("\n사건 4 수사:");
let realClosure = case4(); // 진짜 클로저 발견하기!
realClosure(); // 바깥쪽 함수가 끝났지만 여전히 masterKey 접근 가능!
console.log("결과: 진짜 클로저 발견! 🎉"); // 성공 메시지 출력하기
이 예제는 클로저 탐정 게임을 통해 각 상황에서 클로저가 만들어지는지 확인해볼 수 있습니다. 진짜 클로저는 마지막 사건에서만 발견됩니다.
🔹 예제 2: 클로저 공방에서 일어나는 일들
두 번째 예제에서는 클로저가 만들어지는 과정을 공방에 비유해서 단계별로 살펴보겠습니다.
// Ex2) 클로저 공방에서 단계별로 제품이 만들어지는 과정을 체험해보자!
// 클로저 공방 - 단계별 생산 과정 체험하기
console.log("🏭 클로저 공방에 오신 것을 환영합니다!");
function closureFactory(productName) {
console.log("1단계: 공방 가동 시작!"); // 공방 시작 알림하기
console.log("2단계: 제품명 설정 -", productName); // 제품명 설정 완료 알림하기
// 공방의 중요한 정보들 (바깥쪽 변수들)
let factoryCode = "FACTORY-2024"; // 공방 코드 저장하기
let productionDate = "2024년 12월"; // 생산 날짜 저장하기
let workerName = "장인 A"; // 작업자 이름 저장하기
console.log("3단계: 공방 정보 준비 완료"); // 정보 준비 완료 알림하기
console.log(" - 공방 코드:", factoryCode); // 공방 코드 출력하기
console.log(" - 생산 날짜:", productionDate); // 생산 날짜 출력하기
// 제품 검사관 만들기 (안쪽 함수)
function productInspector() {
console.log("5단계: 제품 검사 시작!"); // 검사 시작 알림하기
// 공방 정보들을 사용해서 검사 (바깥쪽 변수 사용!)
let inspectionReport = productName + " 검사 완료\n"; // 제품명으로 보고서 시작하기
inspectionReport += "공방: " + factoryCode + "\n"; // 공방 코드 추가하기
inspectionReport += "날짜: " + productionDate + "\n"; // 생산 날짜 추가하기
inspectionReport += "검사자: " + workerName; // 검사자 이름 추가하기
console.log("6단계: 검사 보고서 작성 완료!"); // 보고서 완료 알림하기
console.log(inspectionReport); // 완성된 검사 보고서 출력하기
return inspectionReport; // 검사 보고서 반환하기
}
console.log("4단계: 검사관 양성 완료"); // 검사관 양성 완료 알림하기
console.log("7단계: 검사관을 외부로 파견합니다 (클로저 생성!)"); // 클로저 생성 알림하기
return productInspector; // 검사관을 바깥으로 보내기!
}
// 공방에서 검사관 받기
console.log("\n--- 클로저 공방 운영 ---");
let toyInspector = closureFactory("장난감"); // 장난감 전용 검사관 받기
console.log("\n8단계: 공방 문 닫음 (바깥쪽 함수 종료)"); // 공방 문 닫기 알림하기
console.log("9단계: 하지만 검사관은 여전히 일할 수 있어요!"); // 클로저 지속 알림하기
// 검사관이 여전히 공방 정보를 기억하고 있나?
console.log("\n--- 검사관 활동 ---");
toyInspector(); // 공방이 문 닫았지만 여전히 모든 정보를 기억하며 검사하기!
console.log("\n🎯 클로저 완성! 검사관이 공방 정보를 기억하고 있어요!");
이 예제는 클로저 공방을 통해 클로저가 만들어지는 과정을 단계별로 보여줍니다. 공방이 문을 닫아도(바깥쪽 함수가 끝나도) 검사관은 여전히 공방의 정보를 기억하고 있습니다.
🔹 예제 3: 우리 반의 특별한 수업
세 번째 예제에서는 여러 개의 독립적인 클로저가 만들어지는 과정을 우리 반 수업에 비유해서 알아보겠습니다.
// Ex3) 우리 반에서 각 학생에게 개인 맞춤 학습 도우미를 만들어주자!
// 우리 반의 개인 맞춤 학습 도우미 만들기
console.log("📚 우리 반 특별 수업 시작!");
function createStudyHelper(studentName, subject) {
console.log(studentName + " 학생의 " + subject + " 학습 도우미 만들기 시작!"); // 수업 시작 알림하기
// 각 학생만의 학습 정보 (바깥쪽 변수들)
let student = studentName; // 학생 이름 저장하기
let studySubject = subject; // 과목 이름 저장하기
let studyCount = 0; // 학습 횟수 저장하기 (0부터 시작)
let studyPoints = 100; // 학습 점수 저장하기 (100부터 시작)
console.log("학습 준비 완료:"); // 준비 완료 알림하기
console.log(" - 학생:", student); // 학생 이름 출력하기
console.log(" - 과목:", studySubject); // 과목 이름 출력하기
console.log(" - 학습 점수:", studyPoints); // 초기 학습 점수 출력하기
// 개인 맞춤 학습 도우미 만들기 (안쪽 함수)
function personalHelper() {
studyCount = studyCount + 1; // 학습 횟수를 1 증가시키기 (바깥쪽 변수 수정!)
let studyResult = student + "이 " + studySubject + " 공부를 합니다! 📖"; // 학습 메시지 만들기
studyResult += " (학습 횟수: " + studyCount + "회)"; // 학습 횟수 추가하기
console.log(studyResult); // 학습 결과 출력하기
if (studyCount >= 3) { // 학습 횟수가 3번 이상인지 확인하기
console.log(student + "의 학습 점수가 상승했습니다! 🌟"); // 점수 상승 알림하기
studyPoints = studyPoints + 10; // 학습 점수를 10 증가시키기 (바깥쪽 변수 수정!)
}
return studyResult; // 학습 결과 반환하기
}
console.log(student + "님의 개인 학습 도우미 완성!"); // 도우미 완성 알림하기
console.log("도우미를 학생에게 전달합니다 (클로저 생성!)"); // 클로저 생성 알림하기
return personalHelper; // 개인 학습 도우미 전달하기!
}
// 여러 학생들에게 각자의 학습 도우미 만들어주기
console.log("\n--- 여러 학생들의 학습 도우미 만들기 ---");
let aliceHelper = createStudyHelper("앨리스", "수학"); // 앨리스를 위한 수학 도우미 만들기
let bobHelper = createStudyHelper("밥", "과학"); // 밥을 위한 과학 도우미 만들기
let charlieHelper = createStudyHelper("찰리", "영어"); // 찰리를 위한 영어 도우미 만들기
console.log("\n--- 수업 종료 후 각자 학습 시간 ---");
console.log("(각 도우미는 자신만의 정보를 기억해요!)");
console.log("\n앨리스의 수학 공부:");
aliceHelper(); // 1번째 학습하기
aliceHelper(); // 2번째 학습하기
aliceHelper(); // 3번째 학습하기 (점수 상승!)
console.log("\n밥의 과학 공부:");
bobHelper(); // 1번째 학습하기 (앨리스와 독립적!)
bobHelper(); // 2번째 학습하기
console.log("\n찰리의 영어 공부:");
charlieHelper(); // 1번째 학습하기 (다른 학생들과 독립적!)
console.log("\n🎯 각 학생의 학습 도우미가 독립적으로 정보를 기억하고 있어요!");
이 예제는 우리 반 수업을 통해 여러 개의 독립적인 클로저가 어떻게 만들어지는지 보여줍니다. 각 학생의 학습 도우미는 자신만의 정보를 독립적으로 기억하고 있습니다.
🔄 클로저 만들어지는 순서 정리하기
지금까지 배운 클로저가 만들어지는 과정을 자연스럽게 정리해보겠습니다.
첫 번째 단계는 조건 확인입니다. 클로저가 만들어질 수 있는 3가지 조건(함수 안의 함수, 바깥쪽 변수 사용, 함수 돌려주기)이 모두 갖춰져 있는지 확인합니다.
두 번째 단계는 바깥쪽 함수 실행입니다. 바깥쪽 함수가 호출되면서 바깥쪽 변수들이 메모리에 생성됩니다. 이때는 아직 클로저가 만들어지지 않았습니다.
세 번째 단계는 안쪽 함수 선언입니다. 바깥쪽 함수 안에서 안쪽 함수가 선언되고, 이 안쪽 함수가 바깥쪽 변수들을 참조하는 관계가 설정됩니다.
네 번째 단계는 클로저 생성입니다. 안쪽 함수가 바깥으로 반환되는 순간, 클로저가 정식으로 만들어집니다. 이 순간이 가장 중요합니다.
다섯 번째 단계는 바깥쪽 함수 종료입니다. 일반적으로는 함수가 끝나면 그 안의 변수들이 사라지지만, 클로저 때문에 참조된 변수들은 계속 살아있게 됩니다.
마지막으로 가장 중요한 것은 클로저 활용입니다. 이제 언제든지 클로저 함수를 호출해서 보존된 바깥쪽 변수들을 사용할 수 있습니다.
🧚♀️ 이야기로 다시 배우기: 개인 맞춤 스마트 카드
지금까지 배운 내용을 하나의 조용한 이야기로 다시 정리해보겠습니다.
우리 동네에는 작은 IT 회사가 있습니다. 이 회사는 참 특별한 서비스를 제공합니다.
회사 직원이 고객의 집으로 가서(바깥쪽 함수 실행), 그 집의 중요한 정보들을 모아놓습니다(바깥쪽 변수들). 가족 구성원, 취미, 좋아하는 음식, 일상 패턴 등등... 이런 정보들을 특별한 데이터베이스에 저장해둡니다.
그다음 직원은 개인 맞춤 스마트 카드를 하나 만듭니다(안쪽 함수). 이 카드에는 참 똑똑한 기능이 있습니다. 바로 그 집의 모든 정보에 접근할 수 있는 특별한 프로그램이 들어있는 것입니다(바깥쪽 변수 참조).
이제 가장 중요한 순간이 옵니다! 직원은 일을 마치고 회사로 돌아가면서(바깥쪽 함수 종료), 스마트 카드를 고객에게 선물로 줍니다(함수 반환). 이 순간이 바로 클로저가 만들어지는 순간입니다.
놀라운 일이 일어납니다. 직원이 다른 지역으로 출장을 가버려서 연결이 끊어진 것 같지만(바깥쪽 함수 종료), 스마트 카드는 여전히 그 집의 모든 정보를 활용해서 맞춤 서비스를 제공할 수 있습니다.
더 흥미로운 것은, 회사에서 여러 개의 스마트 카드를 만들면, 각각의 카드가 서로 다른 집의 정보를 기억한다는 점입니다. 김씨 집 카드는 김씨 집 정보를, 박씨 집 카드는 박씨 집 정보를 기억하는 식으로요.
이 개인 맞춤 스마트 카드가 바로 클로저이고, 집의 데이터베이스가 바깥쪽 함수의 환경입니다. 직원이 떠난 후(바깥쪽 함수가 끝난 후)에도 카드는 여전히 모든 것을 기억하고 있습니다.
🧠 자주 하는 실수와 주의할 점
클로저가 언제 만들어지는지 배우면서 많은 분들이 실수하는 부분들을 미리 알아두면 더 안전한 코딩을 할 수 있습니다.
❌ 실수 1: 바깥쪽 변수를 사용하지 않는 경우
// 클로저인 줄 알았지만 아닌 경우
function notAClosure() {
let outerVariable = "바깥쪽 변수";
function inner() {
let innerVariable = "안쪽 변수"; // 바깥쪽 변수를 사용하지 않음!
console.log("안쪽에서:", innerVariable);
return innerVariable;
}
return inner; // 함수는 반환하지만 클로저가 아님
}
// 진짜 클로저 만들기
function realClosure() {
let outerVariable = "바깥쪽 변수";
function inner() {
console.log("바깥쪽 변수 사용:", outerVariable); // 바깥쪽 변수 사용!
return outerVariable;
}
return inner; // 진짜 클로저!
}
let func1 = notAClosure();
let func2 = realClosure();
console.log("가짜 클로저 실행:");
func1(); // 단순한 함수 실행
console.log("진짜 클로저 실행:");
func2(); // 클로저 능력 발동!
이런 실수가 생기는 이유는 안쪽 함수가 바깥쪽 변수를 사용해야 한다는 조건을 놓치기 때문입니다. 함수 안에 함수가 있다고 해서 무조건 클로저가 되는 것은 아닙니다.
❌ 실수 2: 함수를 반환하지 않는 경우
// 함수를 실행만 하고 반환하지 않는 경우
function almostClosure() {
let secret = "비밀 정보";
function revealSecret() {
console.log("비밀:", secret); // 바깥쪽 변수 사용함
return secret;
}
revealSecret(); // 함수 실행만 함
return "끝"; // 함수가 아닌 문자열만 반환
}
// 올바른 클로저 만들기
function correctClosure() {
let secret = "비밀 정보";
function revealSecret() {
console.log("비밀:", secret);
return secret;
}
return revealSecret; // 함수 자체를 반환!
}
let result1 = almostClosure(); // "끝" 문자열만 받음
let result2 = correctClosure(); // 클로저 함수를 받음
console.log("결과1:", result1); // "끝"
console.log("결과2 실행:");
result2(); // 클로저 동작!
함수를 반환하는 것과 함수를 실행한 결과를 반환하는 것은 완전히 다릅니다. 클로저를 만들려면 반드시 함수 자체를 반환해야 합니다.
❌ 실수 3: 모든 안쪽 함수가 클로저라고 생각하기
// 안쪽 함수가 있어도 클로저가 안 되는 경우들
function confusingExample() {
let data = "정보";
// 이 함수는 클로저가 될 수 없어요 (반환되지 않음)
function helper1() {
console.log("도우미1:", data);
}
// 이 함수도 클로저가 될 수 없어요 (바깥쪽 변수 미사용)
function helper2() {
console.log("도우미2: 독립적");
}
// 이 함수만 클로저가 될 수 있어요!
function realHelper() {
console.log("진짜 도우미:", data); // 바깥쪽 변수 사용
return data;
}
helper1(); // 그냥 실행
helper2(); // 그냥 실행
return realHelper; // 이것만 클로저!
}
let closure = confusingExample();
closure(); // 클로저 동작
함수 안에 여러 안쪽 함수가 있어도, 반환되는 함수만 클로저가 될 수 있습니다.
✏️ 연습문제로 개념 다지기
이제 배운 내용을 연습문제를 통해 차근차근 익혀보겠습니다.
Ex1) 다음 코드들 중 클로저가 만들어지는 것을 찾아보세요
// A) 바깥쪽 변수는 있지만 안쪽 함수가 없는 경우
function testA() {
let x = 10; // 바깥쪽 변수 만들기
return x; // 변수 값만 반환하기 (함수 반환 아님)
}
// B) 안쪽 함수는 있지만 반환하지 않는 경우
function testB() {
let y = 20; // 바깥쪽 변수 만들기
function inner() { // 안쪽 함수 만들기
return y; // 바깥쪽 변수 사용하기
}
inner(); // 안쪽 함수 실행만 하기 (반환하지 않음)
}
// C) 모든 조건을 만족하는 완벽한 클로저
function testC() {
let z = 30; // 바깥쪽 변수 만들기
function inner() { // 안쪽 함수 만들기
return z; // 바깥쪽 변수 사용하기
}
return inner; // 안쪽 함수 반환하기 (클로저 완성!)
}
정답: C만 클로저가 만들어집니다.
해설:
- A: 함수 안의 함수가 없음
- B: 안쪽 함수를 반환하지 않음
- C: 모든 조건(함수 안의 함수 + 바깥쪽 변수 사용 + 함수 반환) 충족
Ex2) 클로저가 만들어지는 조건 3가지를 설명해보세요
정답:
- 함수 안의 함수: 함수 안에 다른 함수가 있어야 함
- 바깥쪽 변수 사용하기: 안쪽 함수가 바깥쪽 함수의 변수를 사용해야 함
- 함수 돌려주기: 안쪽 함수가 바깥으로 반환되어야 함
Ex3) 간단한 클로저를 만들어보세요
function createReminder(message) {
// 바깥쪽 변수
let reminder = message; // 알림 메시지 저장하기
// 안쪽 함수 (바깥쪽 변수 사용)
function showReminder() {
console.log("알림:", reminder); // 저장된 알림 메시지 출력하기
return reminder; // 알림 메시지 반환하기
}
// 안쪽 함수 반환
return showReminder; // 클로저 함수 반환하기
}
let myReminder = createReminder("숙제하기"); // 클로저 만들기
myReminder(); // "알림: 숙제하기" 출력하기
🤔 심화 문제로 실력 확인하기
기본 연습을 마쳤다면, 이제 조금 더 깊이 있는 문제들을 통해 클로저가 만들어지는 조건에 대한 이해를 확인해보겠습니다.
Q1. 다음 코드에서 몇 개의 클로저가 생성되는지 분석해보세요.
function multipleTest() {
let a = 1; // 바깥쪽 변수 a 만들기
let b = 2; // 바깥쪽 변수 b 만들기
function func1() {
return a; // 바깥쪽 변수 a 사용하기
}
function func2() {
return b; // 바깥쪽 변수 b 사용하기
}
function func3() {
let c = 3; // 안쪽 함수만의 변수 만들기
return c; // 바깥쪽 변수 사용하지 않음
}
return [func1, func2, func3]; // 모든 함수들을 배열로 반환하기
}
let functions = multipleTest(); // 함수 배열 받기
정답: 2개의 클로저가 생성됩니다.
해설: func1과 func2는 바깥쪽 변수를 사용하므로 클로저가 되지만, func3는 바깥쪽 변수를 사용하지 않으므로 일반 함수입니다.
Q2. 클로저가 만들어지는 조건을 활용해서 안전한 카운터를 만들어보세요.
function createSafeCounter() {
let count = 0; // 밖에서 직접 접근할 수 없는 안전한 변수 만들기
function counter() { // 카운터 기능을 가진 안쪽 함수 만들기
count = count + 1; // 바깥쪽 변수 사용 및 수정하기
console.log("현재 카운트:", count); // 현재 카운트 값 출력하기
return count; // 현재 카운트 값 반환하기
}
return counter; // 클로저 생성하기
}
let myCounter = createSafeCounter(); // 안전한 카운터 만들기
myCounter(); // 1 출력하기
myCounter(); // 2 출력하기
myCounter(); // 3 출력하기
// count 변수에 직접 접근할 수 없어서 안전!
정답: 클로저를 활용하여 count 변수를 보호하면서도 카운터 기능을 제공하는 안전한 시스템을 만들 수 있습니다.
해설: 밖에서는 count에 직접 접근할 수 없고, 오직 counter 함수를 통해서만 안전하게 카운트를 증가시킬 수 있습니다.
💫 마무리하며
지금까지 클로저가 언제 만들어지는지에 대한 비밀을 모두 파헤쳐보았습니다. 마치 특별한 능력이 생기는 조건들처럼, 클로저도 정확한 3가지 조건이 모두 갖춰져야만 만들어진다는 것을 배웠습니다.
가장 중요한 것은 이 조건들을 정확히 이해하고, 필요할 때는 확실하게 클로저를 만들고, 필요하지 않을 때는 만들지 않는 것입니다. 이제 여러분도 클로저 전문가가 되어 필요한 순간에 딱 맞는 클로저를 만들어낼 수 있을 것입니다.
다음 시간에는 더 흥미진진한 자바스크립트 개념들을 함께 탐험해보겠습니다. 여러분의 코딩 실력이 하루하루 성장하는 모습을 보니 정말 뿌듯합니다. 계속 함께 걸어가겠습니다. ✨
✅ 학습 완료 체크리스트
이번 시간에 배운 내용들을 모두 이해했는지 확인해보세요.
학습 내용 | 이해했나요? |
---|---|
클로저 생성 조건의 기본 개념 | ✅ |
기본 사용법과 문법 | ✅ |
주요 특징과 차이점 | ✅ |
자주 하는 실수들 | ✅ |
실전 예제 이해 | ✅ |
5단원 함수 복습 | ✅ |
🎯 추가 연습 문제들
조금 더 연습하고 싶은 분들을 위한 추가 문제들입니다.
추가 문제 1. 다음 코드에서 클로저가 생성되는지 확인하고 이유를 설명해보세요.
function test1() {
let x = 10;
function inner() {
console.log("Hello");
}
return inner;
}
function test2() {
let y = 20;
function inner() {
return y;
}
return inner;
}
답:
test1
: 클로저가 생성되지 않습니다. 내부 함수가 외부 변수x
를 사용하지 않기 때문입니다.test2
: 클로저가 생성됩니다. 모든 조건(중첩 함수 + 외부 변수 사용 + 함수 반환)을 충족합니다.
추가 문제 2. 클로저 생성 조건을 만족하는 함수를 만들어보세요.
function createMultiplier(factor) {
// 외부 변수: factor (매개변수)
function multiply(number) {
// 외부 변수 사용
return number * factor;
}
// 내부 함수 반환
return multiply;
}
let double = createMultiplier(2);
console.log(double(5)); // 10
🔄 단계별 진행 과정 정리
지금까지 배운 내용을 단계별로 다시 한번 정리해보겠습니다.
1단계 과정: 1) 클로저가 되지 않는 3가지 경우 확인 → 2) 각 경우에서 빠진 조건 파악 → 3) 모든 조건을 갖춘 올바른 클로저 생성 → 4) 클로저 동작 확인
2단계 과정: 1) 외부 함수 실행 및 변수 생성 → 2) 내부 함수 선언 및 외부 변수 참조 → 3) 내부 함수 반환으로 클로저 생성 → 4) 외부 함수 종료 → 5) 클로저를 통한 외부 변수 접근 확인
3단계 과정: 1) 매개변수를 기억하는 클로저 생성 → 2) 여러 변수를 기억하는 클로저 생성 → 3) 독립적인 클로저들 생성 → 4) 각 클로저의 독립성 확인
📂 마무리 정보
오늘 배운 6.2.2
내용이 여러분의 자바스크립트 지식 보관함에 잘 저장되었나요? 다음 시간에는 더 흥미진진한 내용으로 만나겠습니다.
기억할 점: 오늘 배운 내용을 꼭 연습해보시고, 궁금한 점이 있으면 언제든 다시 돌아와서 읽어보세요.
무료 JavaScript 학습 플랫폼에서 단계별 학습과 실시간 코드 실행을 통해
더욱 효과적이고 재미있게 학습하실 수 있습니다.
'6. 함수의 비밀 (클로저) > 6.2 클로저 개념' 카테고리의 다른 글
6.2.1 클로저가 뭐예요? - 기억하는 특별한 함수 (0) | 2025.07.05 |
---|