Topic 2: 캡슐화와 접근 제어 🔒
🎯 학습 목표
- 캡슐화의 개념과 중요성을 이해할 수 있다
- public, private, protected 접근 제어자의 차이를 설명할 수 있다
- getter/setter 메서드를 적절히 활용할 수 있다
- 정보 은닉을 통해 안전한 클래스를 설계할 수 있다
🏦 은행 금고로 이해하는 캡슐화
캡슐화를 이해하는 가장 쉬운 방법은 은행 금고를 생각해보는 것입니다! 🏦
은행 금고의 특징
- 🔒 금고 내부: 돈과 중요한 서류 (private 데이터)
- 🚪 금고 문: 특별한 열쇠나 비밀번호 필요 (접근 제어)
- 👤 은행원: 정해진 절차로만 금고 접근 (public 메서드)
class BankVault {
private: // 금고 내부 - 직접 접근 불가!
double money;
string secretDocument;
public: // 은행원을 통한 접근 - 안전한 방법
void depositMoney(double amount);
double withdrawMoney(double amount, string password);
};🔐 접근 제어자 완벽 이해
세 가지 접근 레벨
| 접근 제어자 | 클래스 내부 | 파생 클래스 | 외부 |
|---|---|---|---|
| public | ✅ | ✅ | ✅ |
| protected | ✅ | ✅ | ❌ |
| private | ✅ | ❌ | ❌ |
실생활 비유로 이해하기 🏠
class House {
public: // 현관문 - 누구나 접근 가능
string address;
void ringDoorbell() { cout << "딩동! 🔔" << endl; }
protected: // 거실 - 가족과 친척만 접근
string familyPhoto;
void watchTV() { cout << "TV 시청 중... 📺" << endl; }
private: // 침실 - 나만 접근 가능
string diary;
double hiddenMoney;
void readDiary() { cout << "일기 읽는 중... 📔" << endl; }
};💡 실습: 스마트폰 클래스
#include <iostream>
#include <string>
using namespace std;
class Smartphone {
private:
// 보호해야 할 중요한 정보들
string password;
int batteryLevel;
bool isLocked;
string ownerName;
public:
// 생성자 - 초기 설정
Smartphone(string owner, string pwd) {
ownerName = owner;
password = pwd;
batteryLevel = 100;
isLocked = true;
cout << ownerName << "님의 스마트폰이 생성되었습니다! 📱" << endl;
}
// 잠금 해제 - 비밀번호 확인 필요
bool unlock(string inputPassword) {
if (inputPassword == password) {
isLocked = false;
cout << "✅ 잠금이 해제되었습니다!" << endl;
return true;
} else {
cout << "❌ 비밀번호가 틀렸습니다!" << endl;
return false;
}
}
// 잠금
void lock() {
isLocked = true;
cout << "🔒 화면이 잠겼습니다." << endl;
}
// 앱 실행 - 잠금 해제 상태에서만 가능
void runApp(string appName) {
if (!isLocked) {
cout << "📱 " << appName << " 앱을 실행합니다!" << endl;
batteryLevel -= 5;
} else {
cout << "❌ 먼저 잠금을 해제하세요!" << endl;
}
}
// 배터리 확인 (getter)
int getBatteryLevel() {
return batteryLevel;
}
// 충전 (setter의 변형)
void charge() {
batteryLevel = 100;
cout << "⚡ 충전 완료! 배터리: 100%" << endl;
}
// 상태 표시
void showStatus() {
cout << "\n=== 📱 스마트폰 상태 ===" << endl;
cout << "소유자: " << ownerName << endl;
cout << "배터리: " << batteryLevel << "%" << endl;
cout << "잠금 상태: " << (isLocked ? "잠김 🔒" : "해제 🔓") << endl;
}
};
int main() {
// 스마트폰 생성
Smartphone myPhone("김철수", "1234");
// 상태 확인
myPhone.showStatus();
// 잠긴 상태에서 앱 실행 시도
myPhone.runApp("카카오톡");
// 틀린 비밀번호로 해제 시도
myPhone.unlock("0000");
// 올바른 비밀번호로 해제
myPhone.unlock("1234");
// 이제 앱 실행 가능
myPhone.runApp("카카오톡");
myPhone.runApp("유튜브");
// 배터리 확인
cout << "현재 배터리: " << myPhone.getBatteryLevel() << "%" << endl;
// 충전
myPhone.charge();
// 다시 잠금
myPhone.lock();
myPhone.showStatus();
return 0;
}🎯 Getter와 Setter 패턴
왜 직접 접근하지 않을까? 🤔
class Student {
private:
int age;
double gpa;
public:
// Getter - 값을 읽기
int getAge() const { // const: 이 함수는 멤버 변수를 바꾸지 않음
return age;
}
// Setter - 값을 설정 (유효성 검사 포함!)
void setAge(int newAge) {
if (newAge > 0 && newAge < 150) {
age = newAge;
} else {
cout << "❌ 유효하지 않은 나이입니다!" << endl;
}
}
// GPA getter
double getGPA() const {
return gpa;
}
// GPA setter - 범위 제한
void setGPA(double newGPA) {
if (newGPA >= 0.0 && newGPA <= 4.5) {
gpa = newGPA;
} else {
cout << "❌ GPA는 0.0 ~ 4.5 사이여야 합니다!" << endl;
}
}
};Getter/Setter의 장점
-
유효성 검사 ✅
void setSpeed(int speed) { if (speed >= 0 && speed <= 200) { this->speed = speed; } } -
계산된 속성 🧮
double getAreaCircle() const { return 3.14 * radius * radius; // 저장하지 않고 계산해서 반환 } -
접근 로그 📝
int getSecretData() { cout << "[LOG] 민감한 데이터 접근!" << endl; return secretData; }
🏗️ 실전 예제: 게임 캐릭터 강화 시스템
#include <iostream>
#include <string>
using namespace std;
class RPGCharacter {
private:
string name;
int level;
int hp;
int maxHp;
int attackPower;
int exp;
int expToNextLevel;
// 레벨업 처리 (private 메서드)
void checkLevelUp() {
while (exp >= expToNextLevel) {
exp -= expToNextLevel;
level++;
maxHp += 10;
hp = maxHp; // 레벨업하면 HP 회복!
attackPower += 5;
expToNextLevel = level * 100;
cout << "🎉 레벨 업! 현재 레벨: " << level << endl;
cout << "💪 공격력 상승! 현재 공격력: " << attackPower << endl;
}
}
public:
// 생성자
RPGCharacter(string charName) {
name = charName;
level = 1;
maxHp = 100;
hp = maxHp;
attackPower = 10;
exp = 0;
expToNextLevel = 100;
cout << name << " 캐릭터가 생성되었습니다! ⚔️" << endl;
}
// 경험치 획득
void gainExp(int amount) {
if (amount > 0) {
exp += amount;
cout << "✨ 경험치 " << amount << " 획득!" << endl;
checkLevelUp(); // private 메서드 호출
}
}
// 데미지 받기
void takeDamage(int damage) {
if (damage > 0) {
hp -= damage;
if (hp < 0) hp = 0;
cout << "💥 " << damage << " 데미지를 받았습니다!" << endl;
cout << "❤️ 남은 HP: " << hp << "/" << maxHp << endl;
}
}
// HP 회복
void heal(int amount) {
if (amount > 0) {
hp += amount;
if (hp > maxHp) hp = maxHp;
cout << "💚 " << amount << " HP 회복!" << endl;
cout << "❤️ 현재 HP: " << hp << "/" << maxHp << endl;
}
}
// Getter 메서드들
string getName() const { return name; }
int getLevel() const { return level; }
int getHp() const { return hp; }
int getAttackPower() const { return attackPower; }
// 상태 표시
void showStatus() const {
cout << "\n=== 캐릭터 정보 ===" << endl;
cout << "이름: " << name << endl;
cout << "레벨: " << level << endl;
cout << "HP: " << hp << "/" << maxHp << endl;
cout << "공격력: " << attackPower << endl;
cout << "경험치: " << exp << "/" << expToNextLevel << endl;
}
};
int main() {
RPGCharacter hero("용사");
hero.showStatus();
// 몬스터 사냥으로 경험치 획득
cout << "\n🐉 몬스터를 처치했습니다!" << endl;
hero.gainExp(50);
cout << "\n🐉 강한 몬스터를 처치했습니다!" << endl;
hero.gainExp(80); // 레벨업!
// 전투 중 데미지
hero.takeDamage(30);
// 회복
hero.heal(20);
hero.showStatus();
return 0;
}📋 캡슐화 체크리스트
✅ 좋은 캡슐화
- 중요한 데이터는 private으로 보호
- public 메서드로 안전한 접근 제공
- 유효성 검사 포함
- 의미 있는 메서드 이름
❌ 나쁜 캡슐화
- 모든 멤버 변수가 public
- getter/setter만 무작정 추가
- 유효성 검사 없음
- 내부 구현이 그대로 노출
💡 핵심 정리
- 캡슐화: 데이터와 메서드를 하나로 묶고 보호하는 것
- private: 클래스 내부에서만 접근 가능 (가장 안전)
- public: 어디서든 접근 가능 (인터페이스)
- protected: 상속 관계에서 사용 (다음에 배울 예정)
- getter/setter: 안전한 데이터 접근 방법
✅ 실습 체크리스트
🚀 다음 시간 예고
다음 시간에는 멤버 함수와 this 포인터에 대해 알아볼 거예요!
- this 포인터의 개념
- const 멤버 함수
- 메서드 체이닝
“캡슐화는 안전한 프로그램의 첫걸음입니다! 🔐”
Last updated on