📘 6.3.1 클로저로 중요한 정보 지키기 - 우리만의 비밀 금고 만들기
여러분은 혹시 어린 시절, 소중한 일기장을 어디에 숨겨두셨나요? 혹은 용돈을 어느 안전한 곳에 보관해두셨던 기억이 있으신가요? 아마도 가족들도 찾기 어려운, 나만 아는 특별한 장소에 감춰두셨을 거예요.
자바스크립트에도 이렇게 소중한 정보를 감춰두는 방법이 있습니다. 클로저라는 이름의 이 기법은, 마치 우리가 개인적인 물건을 서랍 깊숙이 넣어두듯이, 프로그램의 중요한 정보를 안전하게 보호해주는 역할을 합니다. 오늘은 이런 보호막을 어떻게 만드는지, 함께 차근차근 배워보겠습니다.
🧠 새로운 단어들과 친해지기
우선 이번 시간에 만날 단어들을 살펴보겠습니다. 마치 새로운 친구를 만나기 전에 그 친구에 대해 미리 알아보는 것처럼요.
| 단어 | 부드러운 설명 |
|---|---|
| 정보 숨기기 | 소중한 데이터를 다른 곳에서 함부로 건드릴 수 없게 보호하는 것입니다. 일기장에 자물쇠를 채우는 것과 비슷하다고 생각하시면 됩니다. |
| 정보 보호하기 | 데이터와 그것을 다루는 방법들을 하나의 공간에 모아두는 것입니다. 도시락통에 반찬과 젓가락을 함께 넣는 것처럼 말이에요. |
| 사용 허가하기 | 정보에 접근할 수 있는 경로를 제한하는 것입니다. 집의 현관문으로만 들어올 수 있게 하는 것과 같은 원리입니다. |
| 비밀 변수 | 외부에서 직접 손댈 수 없는 변수입니다. 개인 서랍 안의 물건처럼, 주인만이 꺼내고 넣을 수 있어요. |
| 공개 기능 | 외부에서 안전하게 사용할 수 있도록 제공되는 기능들입니다. 상점의 계산대처럼, 정해진 방식으로만 이용할 수 있어요. |
✨ 클로저 정보 보호란 무엇인지 알아보기
클로저의 정보 보호 기능을 생각해보면, 은행의 안전금고가 떠오릅니다. 금고 안의 귀중품을 우리가 직접 손으로 만질 수는 없지만, 은행 직원이 제공하는 안전한 절차를 통해서만 물건을 넣거나 꺼낼 수 있는 것과 같은 원리입니다.
클로저를 사용하면 중요한 정보를 외부의 시선으로부터 완전히 차단된 공간에 보관할 수 있습니다. 이 정보들은 바깥에서 직접 접근할 수 없지만, 우리가 정해둔 안전한 통로를 통해서는 확인하거나 변경할 수 있습니다. 이때 이 통로에는 여러 가지 보안 장치를 설치할 수 있어요. 예를 들어, 잘못된 값이 들어오면 정중히 거절하거나, 허가받지 않은 접근을 막는 것처럼 말입니다.
이러한 방식의 가장 큰 매력은 실수로 중요한 정보가 손상되는 것을 방지할 수 있다는 점입니다. 마치 소중한 물건을 안전한 곳에 보관해두어 실수로 분실하거나 망가뜨리는 일을 막는 것과 같습니다.
잔잔한 이야기: 할머니의 보석함
클로저의 정보 보호를 좀 더 친근하게 이해하기 위해, 한 가족의 이야기를 들어보겠습니다.
어느 집에 할머니가 물려주신 특별한 보석함이 있었습니다. 이 보석함은 참 똑똑한 기능을 가지고 있었어요.
보석함 안에는 소중한 가족의 보석이 들어있었지만, 보석함의 뚜껑을 직접 열 수는 없었습니다. 대신 보석함 옆면에는 몇 개의 작은 버튼이 있었죠. '보석 확인' 버튼을 누르면 보석함이 "지금 목걸이 3개와 반지 2개가 있어요"라고 알려주고, '보석 넣기' 버튼을 누르면 새로운 보석을 조심스럽게 넣어줬어요.
만약 누군가 잘못된 것을 넣으려고 하면, 보석함의 섬세한 감지 장치가 "이것은 보석이 아닌 것 같아요"라고 부드럽게 알려주며 받아들이지 않았죠. 이렇게 보석함 안의 진짜 보석들은 항상 안전하게 지켜졌습니다.
클로저의 정보 보호도 이와 같습니다. 소중한 정보는 보석함 안에 보관하고, 안전한 버튼들만 외부에 제공하는 것입니다.
🎯 왜 정보 보호를 사용해야 할까요?
그렇다면 왜 이런 정교한 방법을 사용해야 할까요? 일상의 경험으로 생각해보겠습니다.
여러분이 소중한 용돈을 관리한다고 상상해보세요. 용돈을 책상 위에 그냥 놓아두면 누구든지 가져갈 수 있어서 불안하겠죠. 하지만 용돈을 안전한 저금통에 넣고, 정해진 방법으로만 넣고 빼낼 수 있게 하면 훨씬 마음이 편할 거예요.
프로그래밍에서도 마찬가지입니다. 중요한 정보를 그냥 둔다면 실수로 잘못된 값이 들어가거나 다른 부분에서 예상치 못하게 변경될 수 있습니다. 특히 여러 사람이 함께 만드는 큰 프로그램에서는 이런 실수가 자주 일어나곤 해요.
클로저를 활용한 정보 보호는 이런 어려움들을 자연스럽게 해결해줍니다. 개인정보나 중요한 설정값 같은 민감한 정보를 안전하게 지키고, 잘못된 값이 들어오는 것을 미리 방지하며, 정보가 예기치 않은 방식으로 바뀌는 것을 막을 수 있습니다.
⚙️ 기본 사용법 배우기
클로저를 활용한 정보 보호의 기본 구조는 다음과 같습니다.
function create안전한금고() {
// 보호할 중요한 정보 (외부에서 직접 접근 불가)
let 비밀정보 = "중요한 내용";
// 안전한 접근 방법들
function 안전한읽기() {
return 비밀정보;
}
function 안전한변경(새로운값) {
// 여기서 값이 올바른지 검사 가능
if(새로운값 && 새로운값.length > 0) {
비밀정보 = 새로운값;
return true;
}
return false;
}
// 외부에 제공할 안전한 방법들만 반환
return {
읽기: 안전한읽기,
변경: 안전한변경
};
}
이 구조를 통해 비밀정보는 외부에서 직접 건드릴 수 없지만, 읽기와 변경 기능을 통해서는 안전하게 사용할 수 있습니다.
🧪 직접 해보면서 배우기
이제 실제 예제를 통해 클로저의 정보 보호 기능을 차근차근 살펴보겠습니다.
🔹 예제 1: 나만의 비밀 금고 만들기
첫 번째 예제에서는 가장 기본적인 정보 보호 방법을 배워보겠습니다. 일기장에 자물쇠를 채우는 것처럼, 중요한 정보를 안전하게 보호하는 금고를 만들어보겠습니다.
// Ex1) 비밀번호로 보호되는 나만의 비밀 금고를 만들어보자!
// 비밀 금고를 만드는 함수 - 소중한 정보를 안전하게 보관해요
function createSecretBox(mySecret) {
// 보호할 비밀 정보 (외부에서 직접 볼 수 없어요)
let hiddenSecret = mySecret; // 비밀 정보를 안전하게 저장하기
let accessCount = 0; // 접근 시도 횟수를 기록하기 (0부터 시작)
console.log("🔒 비밀 금고가 조심스럽게 만들어졌어요!");
// 비밀을 안전하게 확인하는 방법
function checkSecret(password) {
accessCount = accessCount + 1; // 시도 횟수를 하나씩 증가시키기
// 올바른 비밀번호인지 확인하기
if(password === "1234") { // 입력받은 비밀번호가 "1234"와 일치하는지 확인하기
console.log("✅ 비밀: " + hiddenSecret); // 비밀번호가 맞으면 소중한 비밀 정보 보여주기
return true; // 성공했음을 알려주기
} else {
console.log("❌ 비밀번호가 맞지 않아요..."); // 비밀번호가 틀렸을 때 정중한 실패 메시지 출력하기
return false; // 실패했음을 알려주기
}
}
// 몇 번 시도했는지 확인하는 방법
function getAccessCount() {
return "이 금고를 " + accessCount + "번 열어보셨어요"; // 접근 시도 횟수를 친근하게 안내하기
}
// 외부에서 사용할 수 있는 안전한 방법들만 제공
return {
open: checkSecret, // 비밀 금고를 여는 기능 제공하기
count: getAccessCount // 접근 횟수를 확인하는 기능 제공하기
};
}
// 비밀 금고를 실제로 사용해보기
let myBox = createSecretBox("보물 지도의 위치"); // 나만의 비밀 금고 만들기
myBox.open("wrong"); // 일부러 틀린 비밀번호로 시도해보기
myBox.open("1234"); // 올바른 비밀번호로 시도해보기
console.log(myBox.count()); // 지금까지의 접근 횟수 확인해보기
// 직접 접근을 시도해보기 (불가능할 거예요!)
console.log("직접 접근:", myBox.hiddenSecret); // undefined가 출력됩니다 (잘 보호되고 있어요)
이 예제를 차근차근 살펴보면, 먼저 createSecretBox 함수 안에 비밀 정보를 안전하게 보관해둡니다. 그다음 비밀번호를 확인하는 안전한 방법을 만들어서, 올바른 비밀번호를 입력했을 때만 비밀을 알려주도록 했어요. 마지막으로 직접 접근이 불가능한 것을 확인하여 정보가 안전하게 보호되고 있음을 알 수 있습니다.
🔹 예제 2: 스마트 용돈 관리함 만들기
두 번째 예제에서는 조금 더 섬세한 규칙이 있는 정보 보호를 경험해보겠습니다. 실제 용돈 관리처럼 하루에 쓸 수 있는 돈의 한도가 있는 현명한 용돈 관리함을 만들어보겠습니다.
// Ex2) 하루 사용 한도가 있는 현명한 용돈 관리함을 만들어보자!
// 현명한 용돈 관리함 - 규칙을 지키며 안전하게 용돈을 관리해요
function createSmartMoneyBox(ownerName, initialMoney) {
// 보호할 용돈 정보들
let owner = ownerName; // 주인의 이름을 소중히 저장하기
let totalMoney = initialMoney; // 총 용돈을 안전하게 보관하기
let dailyLimit = 5000; // 하루 사용 한도를 5000원으로 설정하기
let todaySpent = 0; // 오늘 사용한 금액을 0원부터 시작하기
console.log(owner + "님의 용돈 관리함이 정성스럽게 준비되었어요! 💰");
// 용돈을 넣는 기능 (언제나 환영해요!)
function addMoney(amount) {
if(amount <= 0) { // 넣을 돈이 0원 이하인지 확인하기
console.log("❌ 0원보다 많은 돈을 넣어주세요"); // 잘못된 금액일 때 정중한 오류 메시지 출력하기
return false; // 실패했음을 알려주기
}
totalMoney = totalMoney + amount; // 총 용돈에 넣은 돈을 조심스럽게 추가하기
console.log("✅ " + amount + "원이 추가되었어요! 총 " + totalMoney + "원이 있습니다"); // 따뜻한 성공 메시지 출력하기
return true; // 성공했음을 알려주기
}
// 용돈을 쓰는 기능 (여러 규칙을 확인해야 해요!)
function spendMoney(amount) {
// 첫 번째 검사: 양수인지 확인하기
if(amount <= 0) { // 쓸 돈이 0원 이하인지 확인하기
console.log("❌ 0원보다 많은 돈을 써야 해요"); // 잘못된 금액일 때 부드러운 오류 메시지 출력하기
return false; // 실패했음을 알려주기
}
// 두 번째 검사: 가진 돈보다 많이 쓰려는지 확인하기
if(amount > totalMoney) { // 쓰려는 돈이 가진 돈보다 많은지 확인하기
console.log("❌ 돈이 부족해요 (가진 돈: " + totalMoney + "원)"); // 잔액 부족을 정중히 알려주기
return false; // 실패했음을 알려주기
}
// 세 번째 검사: 하루 한도를 초과하는지 확인하기
if(todaySpent + amount > dailyLimit) { // 오늘 쓴 돈과 쓰려는 돈의 합이 하루 한도를 넘는지 확인하기
let remaining = dailyLimit - todaySpent; // 오늘 더 쓸 수 있는 돈을 계산하기
console.log("❌ 하루 한도를 초과해요! 오늘 " + remaining + "원 더 쓸 수 있어요"); // 한도 초과를 친근하게 알려주기
return false; // 실패했음을 알려주기
}
// 모든 검사를 통과하면 돈을 사용하기
totalMoney = totalMoney - amount; // 총 용돈에서 쓴 돈을 조심스럽게 빼기
todaySpent = todaySpent + amount; // 오늘 쓴 돈에 새로 쓴 돈을 추가하기
console.log("✅ " + amount + "원을 사용했어요! 남은 돈: " + totalMoney + "원"); // 따뜻한 성공 메시지 출력하기
return true; // 성공했음을 알려주기
}
// 현재 상태를 확인하는 기능
function checkStatus() {
return { // 현재 상태 정보를 정리해서 반환하기
주인: owner, // 주인의 이름
총금액: totalMoney, // 총 가진 돈
오늘사용: todaySpent, // 오늘 사용한 돈
남은한도: dailyLimit - todaySpent // 오늘 더 쓸 수 있는 돈
};
}
// 외부에서 사용할 수 있는 안전한 방법들
return {
돈넣기: addMoney, // 돈을 넣는 기능 제공하기
돈쓰기: spendMoney, // 돈을 쓰는 기능 제공하기
상태확인: checkStatus // 상태를 확인하는 기능 제공하기
};
}
// 현명한 용돈 관리함을 실제로 사용해보기
let myMoney = createSmartMoneyBox("철수", 10000); // 철수의 용돈 관리함 만들기 (처음에 10000원 넣기)
myMoney.돈넣기(5000); // 용돈 5000원을 추가로 넣어보기
myMoney.돈쓰기(3000); // 문구용품으로 3000원 써보기
myMoney.돈쓰기(3000); // 또 3000원을 쓰려고 시도해보기 (한도 초과!)
console.log("현재 상태:", myMoney.상태확인()); // 현재 상태를 자세히 확인해보기
// 직접 접근을 시도해보기 (불가능할 거예요!)
console.log("직접 접근:", myMoney.totalMoney); // undefined가 출력됩니다 (잘 보호되고 있어요)
이 예제에서는 여러 단계의 정중한 검증을 통해 안전한 용돈 관리를 보여줍니다. 먼저 입력값이 올바른지 확인하고, 잔액이 충분한지 검사한 다음, 하루 사용 한도를 초과하지 않는지 확인해요. 이런 방식으로 실제 은행 시스템처럼 안전한 거래가 이루어집니다.
🔹 예제 3: 나만의 성적 관리 시스템 만들기
세 번째 예제에서는 더욱 섬세한 정보 보호를 경험해보겠습니다. 시험 점수를 관리하면서 자동으로 평균과 최고점을 계산해주는 현명한 성적 관리 시스템을 만들어보겠습니다.
// Ex3) 시험 점수를 안전하게 관리하고 자동으로 통계를 계산해주는 시스템을 만들어보자!
// 현명한 성적 관리 시스템 - 시험 점수를 정성스럽게 관리해요
function createGradeManager(studentName) {
// 보호할 성적 정보들
let student = studentName; // 학생의 이름을 소중히 저장하기
let allScores = []; // 모든 시험 점수 목록을 빈 배열로 시작하기
let totalPoints = 0; // 총점을 0부터 시작하기
let testCount = 0; // 시험을 본 횟수를 0부터 세기
let highestScore = 0; // 최고 점수를 0부터 시작하기
console.log(student + "님의 성적 관리 시스템이 정성스럽게 시작되었어요! 📚");
// 새로운 시험 점수를 추가하는 기능
function addTestScore(score) {
// 점수가 올바른 범위인지 확인하기
if(score < 0 || score > 100) { // 점수가 0점 미만이거나 100점 초과인지 확인하기
console.log("❌ 점수는 0점에서 100점 사이여야 해요"); // 잘못된 점수일 때 부드러운 오류 메시지 출력하기
return false; // 실패했음을 알려주기
}
// 점수 정보를 차근차근 업데이트하기
allScores.push(score); // 점수 목록에 새 점수를 조심스럽게 추가하기
totalPoints = totalPoints + score; // 총점에 새 점수를 더해주기
testCount = testCount + 1; // 시험 횟수를 하나씩 증가시키기
// 최고점이 갱신되었는지 확인하기
if(score > highestScore) { // 새 점수가 현재 최고점보다 높은지 확인하기
highestScore = score; // 최고점을 새 점수로 업데이트하기
console.log("🎉 새로운 최고점을 달성하셨어요!"); // 최고점 달성을 진심으로 축하하기
}
console.log("✅ " + score + "점이 소중히 기록되었어요"); // 점수 기록 완료를 따뜻하게 알려주기
return true; // 성공했음을 알려주기
}
// 평균 점수를 계산하는 기능
function getAverage() {
if(testCount === 0) { // 시험을 한 번도 보지 않았는지 확인하기
return 0; // 시험을 보지 않았으면 0을 반환하기
}
let average = totalPoints / testCount; // 총점을 시험 횟수로 나누어 평균을 계산하기
return Math.round(average * 10) / 10; // 소수점 첫째자리까지 정성스럽게 반올림하기
}
// 상세한 성적표를 만드는 기능
function getDetailedReport() {
return { // 성적표 정보를 정리해서 반환하기
학생이름: student, // 학생의 이름
시험횟수: testCount, // 총 시험을 본 횟수
평균점수: getAverage(), // 평균 점수를 계산해서 포함하기
최고점수: highestScore, // 현재까지의 최고 점수
모든점수: allScores.slice() // 점수 목록의 안전한 복사본 제공하기 (원본 보호)
};
}
// 격려 메시지를 받는 기능
function getEncouragement() {
let average = getAverage(); // 현재 평균 점수를 계산하기
if(average >= 90) { // 평균이 90점 이상인지 확인하기
return "🌟 정말 훌륭해요! 계속 꾸준히 해주세요!";
} else if(average >= 80) { // 평균이 80점 이상인지 확인하기
return "👍 잘하고 계세요! 조금만 더 노력하면 최고가 될 거예요!";
} else if(average >= 70) { // 평균이 70점 이상인지 확인하기
return "💪 좋은 실력이에요! 꾸준히 하시면 더 좋아질 거예요!";
} else { // 평균이 70점 미만일 때
return "🌱 시작이 반이에요! 포기하지 말고 계속 도전해주세요!";
}
}
// 외부에서 사용할 수 있는 기능들
return {
점수추가: addTestScore, // 점수를 추가하는 기능 제공하기
평균확인: getAverage, // 평균을 확인하는 기능 제공하기
성적표: getDetailedReport, // 성적표를 조회하는 기능 제공하기
격려받기: getEncouragement // 격려 메시지를 받는 기능 제공하기
};
}
// 성적 관리 시스템을 실제로 사용해보기
let myGrades = createGradeManager("수빈"); // 수빈이의 성적 관리 시스템 만들기
myGrades.점수추가(85); // 수학 시험 85점을 추가해보기
myGrades.점수추가(92); // 영어 시험 92점을 추가해보기
myGrades.점수추가(110); // 일부러 잘못된 점수를 입력해보기 (100점 초과)
console.log("평균:", myGrades.평균확인()); // 현재 평균 점수를 확인해보기
console.log("성적표:", myGrades.성적표()); // 상세한 성적표를 조회해보기
console.log("격려:", myGrades.격려받기()); // 따뜻한 격려 메시지 받아보기
// 직접 접근을 시도해보기 (불가능할 거예요!)
console.log("직접 접근:", myGrades.allScores); // undefined가 출력됩니다 (잘 보호되고 있어요)
이 예제에서는 점수 입력 시 범위 검증, 자동 통계 계산, 격려 메시지 제공 등의 기능을 통해 완전한 성적 관리 시스템을 구현했습니다. 특히 원본 배열의 복사본을 제공하여 외부에서 원본 정보를 변경하는 것을 정중히 방지했어요.
🔄 정보 보호 구현하는 순서 정리하기
지금까지 배운 클로저를 활용한 정보 보호 과정을 자연스럽게 정리해보겠습니다.
첫 번째 단계는 보호할 정보 찾아내기입니다. 어떤 정보가 중요하고 외부로부터 보호되어야 하는지 차분히 파악하는 것이죠. 집에서 중요한 물건을 금고에 넣어야 할지 결정하는 것과 같습니다.
두 번째 단계는 클로저로 정보 숨기기입니다. 바깥쪽 함수 안에 보호할 변수들을 선언하여 외부에서 직접 접근할 수 없게 만듭니다. 이는 정보를 보이지 않는 안전한 공간에 보관하는 것과 같아요.
세 번째 단계는 안전한 접근 방법 만들기입니다. 숨겨진 정보를 다룰 수 있는 안전한 함수들을 정성스럽게 작성해요. 이때 필요에 따라 올바른지 검사하거나 보안 규칙을 추가할 수 있습니다.
마지막으로 가장 중요한 것은 공개 통로 제공하기입니다. 외부에서 사용할 수 있는 안전한 방법들만 선별하여 반환합니다. 이렇게 하면 내부 정보는 완전히 보호되면서도 필요한 기능은 안전하게 제공할 수 있어요.
🧚♀️ 이야기로 다시 배우기: 현명한 도서관
지금까지 배운 내용을 하나의 차분한 이야기로 다시 정리해볼까요?
어느 조용한 마을에 현명한 도서관이 있었습니다. 이 도서관의 정성스러운 사서 선생님은 수십 년 동안 소중한 책들을 지켜오고 있었어요.
도서관에는 섬세한 규칙이 있었습니다. 책들은 모두 특별한 서고에 보관되어 있어서, 방문자들은 책을 직접 볼 수가 없었어요. 대신 사서 선생님이 정성스럽게 마련한 안내 데스크에서만 책을 빌리고 반납할 수 있었답니다.
만약 누군가 "어린이용 과학책을 빌려주세요"라고 정중히 부탁하면, 사서 선생님은 목록을 차분히 확인한 후 적절한 책을 찾아서 건네주었어요. 하지만 너무 어려운 책을 요청하면 "이 책은 좀 어려우실 것 같아요, 이 책부터 읽어보시는 게 어떨까요?"라고 부드럽게 다른 책을 추천했죠.
또한 책을 반납할 때도 섬세한 상태 검사를 통해 책이 손상되지 않았는지 확인한 후에야 다시 서고로 소중히 돌려보냈어요. 이렇게 해서 수십 년 동안 소중한 책들이 안전하게 보존될 수 있었습니다.
클로저의 정보 보호도 이와 같습니다. 중요한 정보는 안전한 공간에 보관하고, 신뢰할 수 있는 방법으로만 접근할 수 있게 하는 것입니다.
🧠 자주 하는 실수와 주의할 점
클로저를 활용한 정보 보호를 처음 배울 때 자주 하는 실수들을 미리 알아두면 더 안전한 코딩을 할 수 있습니다.
❌ 실수 1: 중요한 정보를 그대로 노출하기
가장 흔한 실수는 보호해야 할 정보를 외부에 그대로 공개하는 것입니다.
// ❌ 잘못된 방법: 비밀번호가 그대로 보여요
function createBadBox() {
let password = "1234";
return {
password: password // 비밀번호가 외부에 노출되어요!
};
}
let badBox = createBadBox();
console.log(badBox.password); // "1234" 누구나 볼 수 있어요!
badBox.password = "9999"; // 비밀번호도 마음대로 바꿀 수 있어요!
// ✅ 올바른 방법: 비밀번호를 숨기고 확인만 가능하게 해요
function createGoodBox() {
let password = "1234"; // 숨겨진 비밀번호
return {
checkPassword: function(inputPassword) {
return inputPassword === password;
}
};
}
let goodBox = createGoodBox();
console.log(goodBox.password); // undefined (보호됨)
console.log(goodBox.checkPassword("1234")); // true
이런 실수가 발생하는 이유는 정보 보호의 핵심을 놓쳤기 때문입니다. 중요한 정보는 절대 직접 노출하면 안 되고, 안전한 방법으로만 접근할 수 있게 해야 합니다.
❌ 실수 2: 이상한 값도 허용하기
두 번째 흔한 실수는 입력값에 대한 검증을 하지 않는 것입니다.
// ❌ 잘못된 방법: 어떤 값이든 받아들여요
function createBadScore() {
let score = 0;
return {
setScore: function(newScore) {
score = newScore; // 음수나 문자도 허용해버려요!
return score;
}
};
}
let badGame = createBadScore();
badGame.setScore(-100); // 음수 점수!
badGame.setScore("abc"); // 문자도 점수로!
// ✅ 올바른 방법: 올바른 값만 허용해요
function createGoodScore() {
let score = 0;
return {
setScore: function(newScore) {
if(newScore >= 0 && newScore <= 100 && typeof newScore === "number") {
score = newScore;
console.log("✅ 점수가 " + newScore + "점으로 설정되었어요");
return true;
} else {
console.log("❌ 0에서 100 사이의 숫자만 입력해주세요");
return false;
}
}
};
}
입력값 검증은 정보의 안전성을 보장하는 중요한 보안 장치입니다. 은행에서 거래할 때 신분증을 확인하는 것과 같은 원리예요.
❌ 실수 3: 배열이나 객체를 그대로 반환하기
세 번째 실수는 배열이나 객체의 원본을 그대로 반환하여 외부에서 변경할 수 있게 하는 것입니다.
// ❌ 잘못된 방법: 원본 배열을 그대로 반환
function createBadList() {
let items = ["사과", "바나나"];
return {
getItems: function() {
return items; // 원본 배열을 그대로!
}
};
}
let badList = createBadList();
let myItems = badList.getItems();
myItems.push("독버섯"); // 원본이 망가져요!
// ✅ 올바른 방법: 복사본을 반환해요
function createGoodList() {
let items = ["사과", "바나나"];
return {
getItems: function() {
let copy = [];
for(let i = 0; i < items.length; i++) {
copy.push(items[i]);
}
return copy; // 안전한 복사본
},
addItem: function(item) {
if(item && item.length > 0) {
items.push(item);
console.log("✅ " + item + "이(가) 추가되었어요");
return true;
}
return false;
}
};
}
원본 정보를 보호하기 위해서는 항상 복사본을 제공해야 합니다. 이는 도서관에서 원본 책이 아닌 복사본을 빌려주는 것과 같은 원리예요.
여러분께 연습문제를 드리기 전에, 잠시 마음을 차분히 하시고 지금까지 배운 내용들을 한 번 더 생각해보시면 좋겠습니다. 클로저를 활용한 정보 보호는 단순히 기능을 구현하는 것을 넘어서, 우리의 코드를 더욱 안전하고 신뢰할 수 있게 만드는 소중한 도구입니다. 마치 우리가 일상에서 소중한 물건을 안전한 곳에 보관하는 것처럼, 프로그램의 중요한 정보도 이렇게 정성스럽게 지켜나가는 것이죠.
✏️ 연습문제로 개념 다지기
이제 배운 내용을 연습문제를 통해 차근차근 익혀보겠습니다.
Ex1) 간단한 비밀번호 관리 시스템을 만들어보자
function createPasswordManager() {
let password = "secret123"; // 저장된 비밀번호를 안전하게 설정하기
let attempts = 0; // 시도 횟수를 0부터 기록하기
return {
checkPassword: function(input) { // 비밀번호를 확인하는 기능 만들기
attempts = attempts + 1; // 시도 횟수를 하나씩 증가시키기
if(input === password) { // 입력받은 비밀번호가 저장된 비밀번호와 같은지 확인하기
console.log("✅ 로그인에 성공했어요!"); // 성공 메시지를 따뜻하게 출력하기
return true; // 성공했음을 알려주기
} else {
console.log("❌ 비밀번호가 맞지 않아요 (시도: " + attempts + "번)"); // 실패 메시지와 시도 횟수를 정중히 출력하기
return false; // 실패했음을 알려주기
}
},
getAttempts: function() { // 시도 횟수를 확인하는 기능 만들기
return attempts; // 현재까지 시도한 횟수를 반환하기
}
};
}
let manager = createPasswordManager(); // 비밀번호 관리 시스템을 만들기
manager.checkPassword("wrong"); // 일부러 틀린 비밀번호로 시도해보기
manager.checkPassword("secret123"); // 올바른 비밀번호로 시도해보기
console.log("총 시도 횟수:", manager.getAttempts()); // 총 시도 횟수를 확인해보기
이 연습문제를 통해 클로저의 기본적인 정보 보호 원리를 이해할 수 있습니다.
Ex2) 안전한 카운터를 만들어보자
function createSafeCounter() {
let count = 0; // 카운트 값을 0부터 시작하기
return {
up: function() { // 카운트를 증가시키는 기능 만들기
count = count + 1; // 카운트를 하나씩 증가시키기
return count; // 증가된 카운트 값을 반환하기
},
down: function() { // 카운트를 감소시키는 기능 만들기
if(count > 0) { // 카운트가 0보다 큰지 확인하기 (음수 방지)
count = count - 1; // 카운트를 하나씩 감소시키기
}
return count; // 현재 카운트 값을 반환하기
},
reset: function() { // 카운트를 초기화하는 기능 만들기
count = 0; // 카운트를 0으로 다시 설정하기
return count; // 초기화된 카운트 값을 반환하기
},
getValue: function() { // 현재 값을 확인하는 기능 만들기
return count; // 현재 카운트 값을 반환하기
}
};
}
let counter = createSafeCounter(); // 안전한 카운터를 만들기
console.log(counter.up()); // 1이 출력될 거예요 (0에서 1로 증가)
console.log(counter.up()); // 2가 출력될 거예요 (1에서 2로 증가)
console.log(counter.down()); // 1이 출력될 거예요 (2에서 1로 감소)
console.log(counter.getValue()); // 1이 출력될 거예요 (현재 값 확인)
이 문제는 클로저의 상태 관리와 안전한 접근 제어를 연습하는 데 도움이 됩니다.
Ex3) 다음 코드가 정보를 제대로 보호하고 있는지 확인해보자
function createBox(item) {
let contents = item; // 상자 안의 내용물을 안전하게 저장하기
return {
peek: function() { // 내용물을 확인하는 기능 만들기
return contents; // 상자 안의 내용물을 반환하기
},
change: function(newItem) { // 내용물을 변경하는 기능 만들기
contents = newItem; // 새로운 내용물로 바꾸기
}
};
}
let box = createBox("보물"); // 보물이 들어있는 상자를 만들기
console.log(box.peek()); // "보물"이 출력될 거예요 (내용물 확인)
console.log(box.contents); // undefined가 출력될 거예요 (직접 접근 불가능!)
box.change("새 보물"); // 내용물을 "새 보물"로 바꾸기
console.log(box.peek()); // "새 보물"이 출력될 거예요 (변경된 내용물 확인)
정답: 이 코드는 정보를 잘 보호하고 있습니다. contents 변수는 외부에서 직접 접근할 수 없고, 정해진 방법으로만 접근 가능해요.
🤔 심화 문제로 실력 확인하기
기본 연습을 마쳤다면, 이제 조금 더 깊이 있는 문제들을 통해 클로저 정보 보호에 대한 이해를 확인해보겠습니다.
Q1. 다음 코드의 보안 문제점을 찾고 개선해보세요.
function createUserData(name, age) {
let userData = {
name: name,
age: age,
password: "1234"
};
return {
getUserData: function() {
return userData; // 문제점: 원본 객체를 그대로 반환
}
};
}
let user = createUserData("김철수", 25);
let data = user.getUserData();
data.password = "해킹됨"; // 원본이 변경되어버려요!
정답: 원본 객체를 그대로 반환하는 것이 문제입니다.
개선된 코드:
function createUserData(name, age) {
let userName = name; // 사용자 이름을 안전하게 저장하기
let userAge = age; // 사용자 나이를 안전하게 저장하기
let password = "1234"; // 비밀번호를 안전하게 저장하기
return {
getName: function() { // 이름을 확인하는 기능 만들기
return userName; // 저장된 이름을 반환하기
},
getAge: function() { // 나이를 확인하는 기능 만들기
return userAge; // 저장된 나이를 반환하기
},
checkPassword: function(inputPassword) { // 비밀번호를 확인하는 기능 만들기
return inputPassword === password; // 입력받은 비밀번호가 저장된 비밀번호와 같은지 확인하기
}
};
}
해설: 개별 접근 방법을 제공하고 민감한 정보는 직접 노출하지 않는 것이 안전합니다.
Q2. 클로저를 활용해서 간단한 쇼핑카트 시스템을 만들어보세요.
function createShoppingCart() {
let items = []; // 상품 목록을 빈 배열로 시작하기
let totalPrice = 0; // 총 가격을 0원부터 시작하기
return {
addItem: function(itemName, price) { // 상품을 추가하는 기능 만들기
if(itemName && price > 0) { // 상품명이 있고 가격이 0보다 큰지 확인하기
items.push({name: itemName, price: price}); // 상품 정보를 객체로 만들어서 목록에 추가하기
totalPrice = totalPrice + price; // 총 가격에 상품 가격을 더하기
console.log("✅ " + itemName + "이(가) 추가되었어요 (" + price + "원)"); // 추가 완료를 따뜻하게 알려주기
return true; // 성공했음을 알려주기
}
console.log("❌ 올바른 상품 정보를 입력해주세요"); // 잘못된 입력일 때 정중한 오류 메시지 출력하기
return false; // 실패했음을 알려주기
},
getTotal: function() { // 총 가격을 확인하는 기능 만들기
return totalPrice; // 현재 총 가격을 반환하기
},
getItemCount: function() { // 상품 개수를 확인하는 기능 만들기
return items.length; // 상품 목록의 길이(개수)를 반환하기
},
getItemList: function() { // 상품 목록을 확인하는 기능 만들기
let itemCopy = []; // 복사본 배열을 만들기
for(let i = 0; i < items.length; i++) { // 원본 배열의 모든 항목을 반복하기
itemCopy.push({ // 각 상품 정보를 복사해서 복사본 배열에 추가하기
name: items[i].name, // 상품명을 복사하기
price: items[i].price // 가격을 복사하기
});
}
return itemCopy; // 안전한 복사본을 반환하기
}
};
}
let cart = createShoppingCart(); // 쇼핑카트 시스템을 만들기
cart.addItem("사과", 1000); // 사과 1000원을 추가해보기
cart.addItem("바나나", 1500); // 바나나 1500원을 추가해보기
console.log("총 금액:", cart.getTotal()); // 총 금액을 확인해보기 (2500원)
console.log("상품 개수:", cart.getItemCount()); // 상품 개수를 확인해보기 (2개)
console.log("상품 목록:", cart.getItemList()); // 상품 목록을 확인해보기
정답: 이 코드는 클로저를 활용하여 쇼핑카트 정보를 안전하게 보호하면서도 필요한 기능들을 제공합니다.
해설: 내부 배열과 총액을 직접 노출하지 않고 복사본을 반환하여 원본 정보를 보호했습니다.
📚 이전 단원 복습 문제
6단원까지 왔으니, 이전에 배운 중요한 내용들을 복습해볼까요?
복습 1) 함수와 변수 스코프 (5단원 복습)
// 다음 코드의 출력 결과를 예상해보세요
function outerFunction() {
let outerVar = "바깥쪽 변수";
function innerFunction() {
let innerVar = "안쪽 변수";
console.log(outerVar); // 이 출력 결과는?
console.log(innerVar); // 이 출력 결과는?
}
innerFunction();
// console.log(innerVar); // 이 줄의 주석을 해제하면 어떻게 될까요?
}
outerFunction();
정답:
- 첫 번째 console.log는 "바깥쪽 변수"를 출력합니다 (내부 함수에서 외부 변수에 접근 가능)
- 두 번째 console.log는 "안쪽 변수"를 출력합니다 (같은 스코프 내의 변수)
- 주석을 해제하면 오류가 발생합니다 (innerVar는 innerFunction 안에서만 사용 가능)
복습 2) 반복문과 조건문 (3-4단원 복습)
// 1부터 10까지 숫자 중에서 짝수만 출력하는 코드를 작성해보세요
for(let i = 1; i <= 10; i++) {
if(i % 2 === 0) {
console.log(i + "는 짝수입니다");
}
}
정답: 이 코드는 2, 4, 6, 8, 10을 각각 "숫자는 짝수입니다" 형태로 출력합니다.
지금까지 클로저를 활용한 정보 보호 방법을 차근차근 알아보았습니다. 이 기법은 중요한 정보를 안전하게 지키면서도 필요한 기능은 제공할 수 있는 강력한 도구입니다. 개인 금고처럼, 소중한 정보를 외부의 위험으로부터 보호하면서도 정당한 사용자에게는 편리한 접근 방법을 제공하는 것이죠. 이제 여러분도 클로저를 사용해서 더욱 안전하고 믿을 수 있는 프로그램을 만들 수 있을 거예요.
✅ 학습 완료 체크리스트
이번 시간에 배운 내용들을 모두 이해했는지 확인해보세요!
| 학습 내용 | 이해했나요? |
|---|---|
| 클로저 정보 보호의 기본 개념 | ✅ |
| 기본 사용법과 문법 | ✅ |
| 주요 특징과 차이점 | ✅ |
| 자주 하는 실수들 | ✅ |
| 실전 예제 이해 | ✅ |
🎯 추가 연습 문제들
조금 더 연습하고 싶은 분들을 위한 추가 문제들입니다!
추가 문제 1. 간단한 점수 관리 시스템을 만들어보세요.
function createScoreManager(playerName) {
let score = 0;
let player = playerName;
return {
getScore: function() {
return player + "님의 점수: " + score;
},
addScore: function(points) {
if(points > 0) {
score = score + points;
return true;
}
return false;
}
};
}
let scoreManager = createScoreManager("김철수");
console.log(scoreManager.getScore());
scoreManager.addScore(100);
console.log(scoreManager.getScore());
추가 문제 2. 안전한 카운터를 만들어보세요.
function createSafeCounter() {
let count = 0;
return {
increment: function() {
count = count + 1;
return count;
},
decrement: function() {
if(count > 0) {
count = count - 1;
}
return count;
},
getValue: function() {
return count;
}
};
}
let counter = createSafeCounter();
console.log(counter.increment()); // 1
console.log(counter.increment()); // 2
console.log(counter.decrement()); // 1
추가 문제 3. 다음 코드에서 데이터가 제대로 보호되고 있는지 확인해보세요.
function createBox(item) {
let contents = item;
return {
peek: function() {
return contents;
},
replace: function(newItem) {
contents = newItem;
}
};
}
let box = createBox("보물");
console.log(box.peek()); // "보물"
console.log(box.contents); // undefined (보호됨)
box.replace("새 보물");
console.log(box.peek()); // "새 보물"
답: 이 코드는 데이터가 잘 보호되고 있습니다. contents 변수는 외부에서 직접 접근할 수 없고, 정해진 메서드(peek, replace)로만 접근 가능합니다.
추가 문제 4. 클로저를 활용해서 간단한 도서관 시스템을 만들어보세요.
function createLibrary() {
let books = [];
let maxBooks = 5;
return {
addBook: function(bookTitle) {
if(books.length >= maxBooks) {
console.log("❌ 도서관이 가득 찼습니다");
return false;
}
if(bookTitle && bookTitle.length > 0) {
books.push(bookTitle);
console.log("✅ '" + bookTitle + "'이(가) 추가되었어요");
return true;
}
console.log("❌ 올바른 책 제목을 입력해주세요");
return false;
},
getBookList: function() {
let bookCopy = [];
for(let i = 0; i < books.length; i++) {
bookCopy.push(books[i]);
}
return bookCopy;
},
getBookCount: function() {
return books.length + "/" + maxBooks;
}
};
}
let library = createLibrary();
library.addBook("해리포터");
library.addBook("반지의 제왕");
console.log("도서 목록:", library.getBookList());
console.log("보유 현황:", library.getBookCount());
답: 이 코드는 클로저를 활용하여 도서 목록을 안전하게 보호하면서도 필요한 기능들을 제공합니다.
설명: 내부 배열을 직접 노출하지 않고 복사본을 반환하여 원본 데이터를 보호했습니다.
🔄 단계별 진행 과정 정리
지금까지 배운 내용을 단계별로 다시 한번 정리해볼게요.
1단계 과정: 1) 보호할 데이터를 외부 함수에 선언 → 2) 안전한 접근 방법을 내부 함수로 구현 → 3) 비밀번호로 보안 강화 → 4) 공개할 방법들만 반환 → 5) 외부에서는 정해진 방법으로만 접근 가능
2단계 과정: 1) 용돈과 규칙을 클로저로 보호 → 2) 입금과 출금에 검증 로직 추가 → 3) 하루 사용 한도로 안전장치 구현 → 4) 상태 조회 기능 제공 → 5) 규칙 위반 시 명확한 메시지 출력
3단계 과정: 1) 성적 관련 데이터를 클로저로 보호 → 2) 점수 입력 시 범위 검증 → 3) 자동으로 통계 계산 및 업데이트 → 4) 읽기 전용 정보 제공 → 5) 원본 데이터는 안전하게 숨김
📂 마무리 정보
오늘 배운 6.3.1 내용이 여러분의 자바스크립트 지식에 잘 저장되었나요? 다음 시간에는 더 흥미로운 내용으로 만나요!
기억할 점: 오늘 배운 내용을 꼭 연습해보시고, 궁금한 점이 있으면 언제든 다시 돌아와서 읽어보세요.
무료 JavaScript 학습 플랫폼에서 단계별 학습과 실시간 코드 실행을 통해
더욱 효과적이고 재미있게 학습하실 수 있습니다.
'6. 함수의 비밀 (클로저) > 6.3 클로저 활용 예제' 카테고리의 다른 글
| 6.3.2 함수 팩토리 만들기 - 똑똑한 함수 제조공장 세우기 (0) | 2025.07.07 |
|---|