Topic 2: 사용자 정의 예외 🎨
🎯 학습 목표
- 사용자 정의 예외 클래스를 만들 수 있다
- 예외 클래스 계층 구조를 설계할 수 있다
- 게임별 특화된 예외 처리 시스템을 구현할 수 있다
- 맞춤형 오류 메시지와 복구 전략을 작성할 수 있다
- 예외 처리의 최적 실천법을 적용할 수 있다
🚨 맞춤형 경보 시스템
표준 예외만으로는 모든 상황을 표현하기 어려워요! 🤔
맞춤형 보안 시스템 비유:
-
기본 화재경보기 = 표준 예외 🔔
- 일반적인 문제만 감지
- “뭔가 문제가 있어요!”
-
맞춤형 보안 시스템 = 사용자 정의 예외 🎛️
- 구체적인 상황별 경보
- “2층 거실에서 연기 감지!”
- “현관문이 3번 잘못된 비밀번호 입력됨!”
// 표준 예외 (일반적)
throw runtime_error("오류 발생!");
// 사용자 정의 예외 (구체적)
throw WeaponNotEquippedException("전사", "검");
throw InsufficientManaException("마법사", 50, 100);🎮 게임 예외 클래스 설계
기본 게임 예외 클래스
#include <iostream>
#include <exception>
#include <string>
using namespace std;
// 모든 게임 예외의 기본 클래스
class GameException : public exception {
protected:
string message;
string location; // 오류 발생 위치
public:
GameException(const string& msg, const string& loc = "Unknown")
: message(msg), location(loc) {}
const char* what() const noexcept override {
return message.c_str();
}
string getLocation() const { return location; }
virtual string getErrorType() const { return "GameError"; }
virtual void showDetailedError() const {
cout << "🚨 [" << getErrorType() << "]" << endl;
cout << "📍 위치: " << location << endl;
cout << "💬 메시지: " << message << endl;
}
};캐릭터 관련 예외들
// 캐릭터 예외 기본 클래스
class CharacterException : public GameException {
protected:
string characterName;
public:
CharacterException(const string& charName, const string& msg, const string& loc = "Character")
: GameException(msg, loc), characterName(charName) {}
string getCharacterName() const { return characterName; }
string getErrorType() const override { return "CharacterError"; }
void showDetailedError() const override {
cout << "🎭 [" << getErrorType() << "] - " << characterName << endl;
cout << "📍 위치: " << location << endl;
cout << "💬 메시지: " << message << endl;
}
};
// 체력 부족 예외
class InsufficientHealthException : public CharacterException {
private:
int currentHP;
int requiredHP;
public:
InsufficientHealthException(const string& charName, int current, int required)
: CharacterException(charName,
"체력이 부족합니다! (" + to_string(current) + "/" + to_string(required) + ")",
"Health System"),
currentHP(current), requiredHP(required) {}
string getErrorType() const override { return "InsufficientHP"; }
int getCurrentHP() const { return currentHP; }
int getRequiredHP() const { return requiredHP; }
void showDetailedError() const override {
cout << "❤️ [체력 부족] - " << characterName << endl;
cout << "📊 현재 HP: " << currentHP << " (필요: " << requiredHP << ")" << endl;
cout << "💡 권장사항: 포션을 사용하거나 휴식을 취하세요!" << endl;
}
};
// 마나 부족 예외
class InsufficientManaException : public CharacterException {
private:
int currentMP;
int requiredMP;
string spellName;
public:
InsufficientManaException(const string& charName, const string& spell, int current, int required)
: CharacterException(charName,
spell + " 시전을 위한 마나가 부족합니다! (" + to_string(current) + "/" + to_string(required) + ")",
"Magic System"),
currentMP(current), requiredMP(required), spellName(spell) {}
string getErrorType() const override { return "InsufficientMP"; }
void showDetailedError() const override {
cout << "💙 [마나 부족] - " << characterName << endl;
cout << "🔮 주문: " << spellName << endl;
cout << "📊 현재 MP: " << currentMP << " (필요: " << requiredMP << ")" << endl;
cout << "💡 권장사항: 마나 포션을 사용하거나 휴식을 취하세요!" << endl;
}
};
// 장비 미착용 예외
class WeaponNotEquippedException : public CharacterException {
private:
string weaponType;
public:
WeaponNotEquippedException(const string& charName, const string& weapon)
: CharacterException(charName,
weapon + "이(가) 장착되지 않았습니다!",
"Equipment System"),
weaponType(weapon) {}
string getErrorType() const override { return "WeaponNotEquipped"; }
void showDetailedError() const override {
cout << "⚔️ [장비 미착용] - " << characterName << endl;
cout << "🛡️ 필요 장비: " << weaponType << endl;
cout << "💡 권장사항: 인벤토리에서 " << weaponType << "을(를) 장착하세요!" << endl;
}
};게임 시스템 예외들
// 인벤토리 예외
class InventoryException : public GameException {
public:
InventoryException(const string& msg)
: GameException(msg, "Inventory System") {}
string getErrorType() const override { return "InventoryError"; }
};
class InventoryFullException : public InventoryException {
private:
int maxSlots;
public:
InventoryFullException(int slots)
: InventoryException("인벤토리가 가득 찼습니다! (최대 " + to_string(slots) + "개)"),
maxSlots(slots) {}
string getErrorType() const override { return "InventoryFull"; }
void showDetailedError() const override {
cout << "🎒 [인벤토리 가득참]" << endl;
cout << "📦 최대 슬롯: " << maxSlots << "개" << endl;
cout << "💡 권장사항: 불필요한 아이템을 판매하거나 버리세요!" << endl;
}
};
// 상점 거래 예외
class ShopException : public GameException {
public:
ShopException(const string& msg)
: GameException(msg, "Shop System") {}
string getErrorType() const override { return "ShopError"; }
};
class InsufficientGoldException : public ShopException {
private:
int currentGold;
int requiredGold;
string itemName;
public:
InsufficientGoldException(const string& item, int current, int required)
: ShopException(item + " 구매를 위한 골드가 부족합니다!"),
currentGold(current), requiredGold(required), itemName(item) {}
string getErrorType() const override { return "InsufficientGold"; }
void showDetailedError() const override {
cout << "💰 [골드 부족]" << endl;
cout << "🛍️ 구매 희망 아이템: " << itemName << endl;
cout << "💵 현재 골드: " << currentGold << "G (필요: " << requiredGold << "G)" << endl;
cout << "💡 권장사항: 퀘스트를 완료하거나 아이템을 판매하세요!" << endl;
}
};🎯 실전 예제: RPG 게임 시스템
완전한 게임 시스템 구현
#include <iostream>
#include <vector>
#include <string>
using namespace std;
class RPGCharacter {
private:
string name;
int hp, maxHP;
int mp, maxMP;
int gold;
bool weaponEquipped;
vector<string> inventory;
int maxInventorySlots = 5;
public:
RPGCharacter(string n)
: name(n), hp(100), maxHP(100), mp(50), maxMP(50),
gold(100), weaponEquipped(false) {}
// 공격 시도
void attack(const string& target) {
if (!weaponEquipped) {
throw WeaponNotEquippedException(name, "검");
}
if (hp < 10) {
throw InsufficientHealthException(name, hp, 10);
}
hp -= 5; // 공격 시 체력 소모
cout << "⚔️ " << name << "이(가) " << target << "을(를) 공격했습니다!" << endl;
cout << "❤️ 현재 HP: " << hp << "/" << maxHP << endl;
}
// 마법 시전
void castSpell(const string& spellName, int manaCost) {
if (mp < manaCost) {
throw InsufficientManaException(name, spellName, mp, manaCost);
}
mp -= manaCost;
cout << "🔮 " << name << "이(가) " << spellName << "을(를) 시전했습니다!" << endl;
cout << "💙 현재 MP: " << mp << "/" << maxMP << endl;
}
// 아이템 구매
void buyItem(const string& itemName, int price) {
if (gold < price) {
throw InsufficientGoldException(itemName, gold, price);
}
if (inventory.size() >= maxInventorySlots) {
throw InventoryFullException(maxInventorySlots);
}
gold -= price;
inventory.push_back(itemName);
cout << "🛍️ " << itemName << "을(를) 구매했습니다!" << endl;
cout << "💰 현재 골드: " << gold << "G" << endl;
}
// 장비 착용
void equipWeapon() {
weaponEquipped = true;
cout << "⚔️ 무기를 장착했습니다!" << endl;
}
// 포션 사용
void useHealthPotion() {
hp = min(maxHP, hp + 50);
cout << "🍶 체력 포션을 사용했습니다! HP: " << hp << endl;
}
void useManaPotion() {
mp = min(maxMP, mp + 30);
cout << "🧪 마나 포션을 사용했습니다! MP: " << mp << endl;
}
void showStatus() {
cout << "🎮 ===== " << name << "의 상태 =====" << endl;
cout << "❤️ HP: " << hp << "/" << maxHP << endl;
cout << "💙 MP: " << mp << "/" << maxMP << endl;
cout << "💰 Gold: " << gold << "G" << endl;
cout << "⚔️ 무기 장착: " << (weaponEquipped ? "예" : "아니오") << endl;
cout << "🎒 인벤토리 (" << inventory.size() << "/" << maxInventorySlots << "): ";
for (const string& item : inventory) {
cout << item << " ";
}
cout << endl;
cout << "=========================" << endl;
}
};
// 게임 이벤트 시뮬레이터
void simulateGameplay(RPGCharacter& hero) {
cout << "🎮 RPG 게임 시뮬레이션 시작!" << endl;
cout << "===============================" << endl;
hero.showStatus();
cout << endl;
// 이벤트 1: 무기 없이 공격 시도
try {
cout << "🎯 이벤트 1: 고블린과의 전투!" << endl;
hero.attack("고블린");
} catch (const WeaponNotEquippedException& e) {
e.showDetailedError();
cout << "🔧 무기를 장착합니다..." << endl;
hero.equipWeapon();
hero.attack("고블린"); // 재시도
}
cout << endl;
// 이벤트 2: 마나 부족 상황
try {
cout << "🎯 이벤트 2: 강력한 마법 시전!" << endl;
hero.castSpell("파이어볼", 60); // 마나 부족!
} catch (const InsufficientManaException& e) {
e.showDetailedError();
cout << "🧪 마나 포션을 사용합니다..." << endl;
hero.useManaPotion();
hero.castSpell("파이어볼", 45); // 재시도
}
cout << endl;
// 이벤트 3: 상점에서 쇼핑
try {
cout << "🎯 이벤트 3: 마법 상점에서 쇼핑!" << endl;
hero.buyItem("마법검", 80);
hero.buyItem("체력포션", 30);
hero.buyItem("마나포션", 25);
hero.buyItem("전설의방패", 200); // 골드 부족!
} catch (const InsufficientGoldException& e) {
e.showDetailedError();
cout << "💰 골드가 부족하네요. 다음에 다시 오겠습니다!" << endl;
}
cout << endl;
// 이벤트 4: 인벤토리 가득 참
try {
cout << "🎯 이벤트 4: 더 많은 아이템 수집!" << endl;
hero.buyItem("물약", 10);
hero.buyItem("스크롤", 15);
hero.buyItem("보석", 20); // 인벤토리 가득!
} catch (const InventoryFullException& e) {
e.showDetailedError();
cout << "🗑️ 일부 아이템을 정리해야겠네요!" << endl;
}
cout << endl;
// 이벤트 5: 체력 부족 상황
try {
cout << "🎯 이벤트 5: 연속 전투!" << endl;
for (int i = 0; i < 20; i++) {
hero.attack("몬스터" + to_string(i+1));
}
} catch (const InsufficientHealthException& e) {
e.showDetailedError();
cout << "🍶 응급 치료를 받습니다!" << endl;
hero.useHealthPotion();
cout << "✅ 체력이 회복되었습니다!" << endl;
}
cout << endl;
hero.showStatus();
cout << "🎉 모든 이벤트가 안전하게 처리되었습니다!" << endl;
}
int main() {
cout << "🏰 환상의 RPG 모험 🏰" << endl;
cout << "=====================" << endl;
try {
RPGCharacter hero("용감한모험가");
simulateGameplay(hero);
} catch (const GameException& e) {
cout << "🚨 예상치 못한 게임 오류가 발생했습니다!" << endl;
e.showDetailedError();
} catch (const exception& e) {
cout << "❌ 시스템 오류: " << e.what() << endl;
}
cout << "\n🛡️ 게임이 안전하게 종료되었습니다!" << endl;
return 0;
}🏗️ 예외 클래스 계층 구조 설계
계층 구조의 장점
// 최상위 예외 클래스
class GameException : public exception {
// 공통 기능들
};
// 중간 계층 - 도메인별 분류
class CharacterException : public GameException {
// 캐릭터 관련 공통 기능
};
class InventoryException : public GameException {
// 인벤토리 관련 공통 기능
};
// 구체적인 예외들
class InsufficientHealthException : public CharacterException { };
class WeaponNotEquippedException : public CharacterException { };
class InventoryFullException : public InventoryException { };계층 구조를 활용한 포괄적 처리
try {
// 게임 로직 실행
performGameAction();
} catch (const CharacterException& e) {
// 모든 캐릭터 관련 예외를 한번에 처리
cout << "캐릭터 관련 문제 발생!" << endl;
e.showDetailedError();
} catch (const InventoryException& e) {
// 모든 인벤토리 관련 예외를 한번에 처리
cout << "인벤토리 관련 문제 발생!" << endl;
e.showDetailedError();
} catch (const GameException& e) {
// 기타 모든 게임 예외
cout << "게임 시스템 오류!" << endl;
e.showDetailedError();
}🎯 예외 처리 최적 실천법
1. 의미있는 예외 클래스 만들기 💡
// ❌ 너무 일반적
throw runtime_error("오류");
// ✅ 구체적이고 의미있는 예외
throw InsufficientManaException("마법사", "파이어스톰", 30, 80);2. 복구 가능한 예외 설계 🔄
try {
character.castSpell("힐링", 25);
} catch (const InsufficientManaException& e) {
// 자동 복구 시도
character.useManaPotion();
character.castSpell("힐링", 25); // 재시도
}3. 성능을 고려한 예외 처리 ⚡
// 예외는 예외적인 상황에만 사용
if (hp <= 0) { // 일반적인 조건 검사
return false; // 예외 대신 반환값 사용
}
// 정말 예외적인 상황에만 throw 사용
if (corrupted_save_file) {
throw SaveFileCorruptedException(filename);
}💡 핵심 정리
- 사용자 정의 예외: 프로젝트별 맞춤형 예외 클래스로 더 정확한 오류 처리
- 예외 계층:
exception→GameException→CharacterException→ 구체적 예외 순서 - 상세한 정보: 오류 발생 위치, 현재 상태, 권장 해결책 포함
- 복구 전략: 예외 발생 시 자동 복구 또는 대안 제시
- 성능 고려: 예외는 정말 예외적인 상황에만 사용
- 계층별 처리: 구체적 예외부터 일반적 예외 순으로 catch
✅ 실습 체크리스트
🚀 다음 시간 예고
다음 시간에는 디버깅 기법과 도구에 대해 알아볼 거예요!
- 효과적인 디버깅 전략과 방법론
- assert와 디버그 매크로 활용
- 로깅 시스템으로 프로그램 추적하기
- 단계별 디버깅으로 문제 해결하기
“맞춤형 예외로 더 스마트한 에러 처리 시스템을 구축하세요! 🎨✨”
Last updated on