Skip to Content
💻 코리아IT아카데미 신촌 - 프로그래밍 학습 자료

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); }

💡 핵심 정리

  • 사용자 정의 예외: 프로젝트별 맞춤형 예외 클래스로 더 정확한 오류 처리
  • 예외 계층: exceptionGameExceptionCharacterException → 구체적 예외 순서
  • 상세한 정보: 오류 발생 위치, 현재 상태, 권장 해결책 포함
  • 복구 전략: 예외 발생 시 자동 복구 또는 대안 제시
  • 성능 고려: 예외는 정말 예외적인 상황에만 사용
  • 계층별 처리: 구체적 예외부터 일반적 예외 순으로 catch

✅ 실습 체크리스트

🚀 다음 시간 예고

다음 시간에는 디버깅 기법과 도구에 대해 알아볼 거예요!

  • 효과적인 디버깅 전략과 방법론
  • assert와 디버그 매크로 활용
  • 로깅 시스템으로 프로그램 추적하기
  • 단계별 디버깅으로 문제 해결하기

“맞춤형 예외로 더 스마트한 에러 처리 시스템을 구축하세요! 🎨✨”

Last updated on