이 문서는 게임 개발을 처음 시작하는 입장에서 마주친 문제들과, 그것을 어떻게 해결했는지를 아주 쉽게 풀어 쓴 기록입니다. 각 문제는 실제로 개발 도중 겪었던 고비들이며, 그걸 극복하면서 어떤 걸 배웠는지도 자세히 정리해두었어요.
1. 클래스 설계와 의존성 관리의 어려움
🔧 문제 상황
처음에 Player, Inventory, Shop, Game 클래스 사이의 관계를 어떻게 설정할지 몰라서 진짜 머리가 아팠어요. 예를 들어, 인벤토리는 플레이어 속성일까? 아니면 별도로 관리하는 게 좋을까? 이런 고민이 많았습니다.
🧪 시도한 해결책
Player 안에 Inventory를 넣고 쓰려고 했는데, Shop에서 인벤토리에 접근하려면 Player 전체를 들고 다녀야 했어요. 너무 복잡하고 비효율적이었어요.
✅ 최종 해결책
Game 클래스를 중심으로 두고, Player, Inventory, Shop 등은 전부 독립 객체로 만들었어요. 그리고 각 씬(Scene)은 필요한 객체만 전달받아서 쓰게 했습니다.
🎓 배운 점
- 클래스끼리 너무 얽히지 않게 만드는 게 진짜 중요하다는 걸 느꼈어요.
- "높은 응집도, 낮은 결합도"가 왜 강조되는지도 체감했어요.
- 이걸 하면서 "의존성 주입"이라는 개념을 살짝 이해하게 됐습니다.
2. 참조 타입과 값 타입 이해의 어려움
🔧 문제 상황
아이템을 복사하지 않고 그대로 Inventory에 넣었더니, 하나를 바꾸면 다른 것도 같이 바뀌더라고요! 알고 보니 C#에서 클래스는 "참조 타입"이더라고요.
🧪 시도한 해결책
그냥 Add()로 넣으니 얕은 복사라서 원본이랑 연결된 상태였어요.
✅ 최종 해결책
아이템 구매할 때 new Item(...)으로 새 객체를 만들어서 넣어주었어요.
// 깊은 복사를 위해 새로운 인스턴스를 생성
Item boughtItem = new Item(
item.Name,
item.Description,
item.Price,
item.Type,
item.AttackBonus,
item.DefenseBonus,
item.HealthBonus
);
🎓 배운 점
- 참조 타입은 같은 주소를 가리키기 때문에 조심해야 해요.
- 얕은 복사 vs 깊은 복사의 차이를 몸으로 배웠습니다.
3. 아이템 장착/해제 시스템 구현의 복잡성
🔧 문제 상황
아이템을 장착하면 능력치가 올라가고, 해제하면 다시 내려가야 하는데 이걸 어디서 처리할지가 복잡했어요.
🧪 시도한 해결책
처음엔 Item이 Player를 직접 참조하게 했는데, 클래스 간 관계가 너무 얽히더라고요.
✅ 최종 해결책
Inventory 클래스 안에 ToggleEquip() 메서드를 만들고, 여기에 능력치 증감 로직까지 다 넣었어요.
public void ToggleEquip(Item item, Player player)
{
if (!items.Contains(item))
{
Console.WriteLine("해당 아이템이 인벤토리에 없습니다.");
return;
}
item.ToggleEquip();
if (player != null)
{
int attackMod = item.IsEquipped ? item.AttackBonus : -item.AttackBonus;
int defenseMod = item.IsEquipped ? item.DefenseBonus : -item.DefenseBonus;
player.UpdateStats(attackMod, defenseMod);
if (item.IsEquipped)
Console.WriteLine($"{item.Name}을(를) 장착했습니다.");
else
Console.WriteLine($"{item.Name}을(를) 해제했습니다.");
}
}
🎓 배운 점
- 클래스는 각자의 역할만 하게 하는 게 유지보수에 좋다!
- 복잡한 상태 관리는 중복되지 않게 한 곳에서 처리해야 해요.
4. 콘솔 UI 구현의 제한사항 극복
🔧 문제 상황
콘솔 환경에서는 화려한 그래픽을 쓸 수가 없어서, 현재 상태나 아이템 장착 여부를 직관적으로 보여주기가 어려웠어요. 특히 텍스트로만 보여주면 어떤 아이템이 장착된 건지, 어떤 효과가 있는지 헷갈리기 쉬웠습니다.
🧪 시도한 해결책
처음엔 단순히 아이템 이름, 설명, 가격만 출력했는데, 게임을 플레이하는 입장에서 너무 불편했어요.
✅ 최종 해결책
- 장착된 아이템 앞에 [E] 표시를 붙였어요.
- 각 아이템을 보기 좋게 출력하도록 레이아웃을 정돈했어요.
- 구분선을 넣고, 메뉴마다 일관성 있는 UI 형식을 사용했어요.
public void DisplayInfo()
{
string displayName = IsEquipped ? $"[E] {Name}" : Name;
Console.WriteLine($"[{displayName}] - {Price}G");
Console.WriteLine($"설명: {Description}");
if (AttackBonus != 0)
Console.WriteLine($"공격력: +{AttackBonus}");
if (DefenseBonus != 0)
Console.WriteLine($"방어력: +{DefenseBonus}");
if (HealthBonus != 0)
Console.WriteLine($"체력: +{HealthBonus}");
Console.WriteLine("------------------------");
}
🎓 배운 점
- 콘솔 환경에서도 창의적으로 정보를 표현할 수 있다는 걸 알게 됐어요.
- 작은 변화지만 사용자 경험(UX)에 큰 영향을 줄 수 있다는 걸 느꼈습니다.
5. 상태 저장과 씬 전환 관리
🔧 문제 상황
게임은 여러 화면(씬)으로 구성되잖아요? 메인 메뉴, 상점, 인벤토리 등에서 이동할 때 데이터가 일관되게 유지되지 않아서 큰 문제였어요.
🧪 시도한 해결책
각 씬에서 필요한 데이터를 따로 복사해서 썼는데, 이러면 한 곳에서 바꿔도 다른 곳에는 반영이 안 돼서 문제였어요.
✅ 최종 해결책
- GameScene이라는 추상 클래스를 만들고 모든 씬이 이걸 상속받도록 했어요.
- Game 클래스가 Player, Inventory, Shop을 중앙에서 관리하게 했어요.
- 각 씬은 필요한 객체만 받아와서 사용하게 했습니다.
abstract class GameScene
{
public string SceneName { get; protected set; }
public virtual void Initialize()
{
Console.Clear();
}
public abstract void Run();
public virtual void Exit()
{
Console.WriteLine($"{SceneName} 씬을 종료합니다...");
Console.WriteLine("계속하려면 아무 키나 누르세요...");
Console.ReadKey();
}
protected int GetUserInput(int min, int max)
{
int input;
bool isValid = false;
do
{
Console.Write("선택: ");
isValid = int.TryParse(Console.ReadLine(), out input);
isValid = isValid && input >= min && input <= max;
if (!isValid)
Console.WriteLine($"잘못된 입력입니다. {min}에서 {max} 사이의 값을 입력하세요.");
} while (!isValid);
return input;
}
}
🎓 배운 점
- 추상 클래스와 상속을 이용하면 중복 코드 없이 일관된 로직을 유지할 수 있어요.
- 데이터 일관성을 유지하려면 중심이 되는 클래스가 꼭 필요하다는 걸 느꼈어요.
6. 사용자 입력 검증과 예외 처리
🔧 문제 상황
플레이어가 숫자가 아니라 글자를 입력하거나, 선택지에 없는 번호를 입력했을 때 프로그램이 꺼지거나 이상해지는 일이 많았어요.
🧪 시도한 해결책
try-catch를 써봤지만 모든 경우를 막기엔 부족했어요.
✅ 최종 해결책
GetUserInput() 메서드를 만들어서 입력값을 범위 안에 있는지 체크하고, 잘못된 입력이면 다시 물어보는 방식으로 처리했어요.
private int GetUserInput(int min, int max)
{
int input;
bool isValid = false;
do
{
Console.Write("선택: ");
isValid = int.TryParse(Console.ReadLine(), out input);
isValid = isValid && input >= min && input <= max;
if (!isValid)
Console.WriteLine($"잘못된 입력입니다. {min}에서 {max} 사이의 값을 입력하세요.");
} while (!isValid);
return input;
}
🎓 배운 점
- 예외를 막기 위한 "방어적 프로그래밍"이 정말 중요하다는 걸 알게 됐어요.
- 이런 기능은 메서드로 따로 빼두면 여기저기 재사용할 수 있어서 편합니다!
7. "육군 아미타이거" 디버그 모드 구현
🔧 문제 상황
게임 기능 테스트할 때 매번 골드를 늘리거나 장비를 추가하려고 코드를 일일이 바꾸는 게 너무 귀찮았어요.
🧪 시도한 해결책
직접 코드에 숫자를 바꾸는 방법은 너무 비효율적이었어요.
✅ 최종 해결책
입력이 잘못되거나 빈 문자열이면 자동으로 디버그 직업 "육군 아미타이거"를 선택하게 했고, 이 직업은 아주 강한 장비와 골드를 갖고 시작합니다.
case "육군 아미타이거":
inventory = new Inventory(5000);
Item k2 = new Item("K2 소총", "대한민국 군인의 주력 소총입니다.", 2000, ItemType.Weapon, 20, 0, 0);
Item k2c1 = new Item("K2C1 소총", "개량형으로 더 강력한 무기입니다.", 3000, ItemType.Weapon, 25, 0, 0);
Item gasMask = new Item("K3 방독면", "화생방 방어용 장비.", 1000, ItemType.Armor, 0, 25, 0);
Item bodyArmor = new Item("레벨3 방탄복", "방어력이 높은 장비.", 2000, ItemType.Armor, 0, 25, 0);
Item helmet = new Item("경량 방탄헬멧", "가벼우면서 튼튼한 헬멧.", 1500, ItemType.Armor, 0, 25, 0);
inventory.AddItem(k2);
inventory.AddItem(k2c1);
inventory.AddItem(gasMask);
inventory.AddItem(bodyArmor);
inventory.AddItem(helmet);
break;
🎓 배운 점
- 디버그 모드는 개발자에게 정말 큰 도움이 됩니다.
- 숨겨진 기능처럼 만들면 플레이어도 재미있게 느낄 수 있어요!
8. 열거형(Enum)과 객체 타입 관리
🔧 문제 상황
아이템 타입(무기/방어구/소비품 등)을 문자열로 관리하다 보니 오타가 나거나 잘못된 비교가 생겨서 버그가 생겼어요.
🧪 시도한 해결책
"Weapon", "Armor"처럼 문자열을 쓰니까 IDE에서 자동완성도 안 되고, 실수도 많았어요.
✅ 최종 해결책
enum을 써서 타입을 명확하게 지정했어요. 덕분에 자동완성도 되고, 오타도 줄고, 가독성도 좋아졌습니다.
public enum ItemType
{
Weapon,
Armor,
Consumable
}
🎓 배운 점
- enum은 초보자라도 꼭 써야 하는 좋은 기능이에요.
- 코드가 안전해지고 보기 좋아집니다!
🎮 스파르타 던전 프로젝트 트러블슈팅 정리

🔧 주요 클래스 구조
- 게임의 전체 진행을 총괄하는 "중앙 관리자" 역할이에요.
- 플레이어, 인벤토리, 상점 같은 객체를 생성하고 서로 연결해줘요.
- 씬(Scene) 간 전환도 이 클래스에서 담당해요.
- 사용자 입력을 받을 때 GetUserInput() 같은 검증 메서드도 가지고 있어요.
📌 Player 클래스
- 플레이어의 이름, 직업, 능력치(공격력, 방어력, 체력)를 저장해요.
- 직업에 따라 시작 능력치가 달라지고, 장착한 아이템에 따라 능력치가 변동돼요.
- DisplayStatus() 메서드로 플레이어 상태를 보여줄 수 있어요.
📌 Item 클래스
- 모든 아이템의 속성(이름, 설명, 가격, 타입, 능력치 보너스 등)을 저장해요.
- 장착 여부를 관리하는 IsEquipped 속성이 있어요.
- DisplayInfo() 메서드로 아이템 정보를 예쁘게 출력해요.
📌 Inventory 클래스
- 플레이어가 가진 아이템 리스트를 저장해요.
- 골드를 저장하고 사용하는 메서드도 있어요.
- 아이템 장착/해제 기능이 포함되어 있어요.
- DisplayInventory()로 현재 인벤토리 내용을 볼 수 있어요.
📌 Shop 클래스
- 상점에서 판매할 아이템 리스트를 가지고 있어요.
- 아이템을 사거나 팔 수 있는 기능이 있어요 (BuyItem(), SellItem()).
- 판매 가격은 구매 가격의 절반으로 계산돼요.
📌 GameScene (추상 클래스)
- 공통적인 씬 동작을 정의한 "틀(template)" 같은 역할이에요.
- Initialize(), Run(), Exit() 같은 기본 동작이 들어있어요.
- 씬마다 다르게 동작하는 부분은 Run()에서 구체적으로 구현해요.
📌 구체적인 씬 클래스들
- StartScene: 게임 시작화면 + 직업 선택.
- StatusScene: 플레이어 능력치 확인.
- InventoryScene: 인벤토리 확인 및 장착/해제.
- ShopScene: 상점에서 아이템 사고팔기.
- DungeonScene: 전투 및 보상 (개발 중).

🔧 기능 구현 요약
- 플레이어 이름을 입력하고, 직업을 선택해요.
- 기본 직업: 전사(검 + 갑옷), 마법사(지팡이 + 로브), 궁수(활 + 조끼).
- 디버그 직업: "육군 아미타이거" → 고성능 장비 + 많은 골드 지급.
🎒 인벤토리 시스템
- 아이템 추가/삭제 가능.
- 아이템 장착/해제 → 능력치 반영됨.
- 골드 부족 시 구매 실패 메시지 출력.
- 장착된 아이템은 [E] 표시로 확인 가능.
🛒 상점 시스템
- ShopScene에서 아이템 목록 확인 후 구매/판매 가능.
- 판매 시 가격은 원래 가격의 절반으로 책정됨.
- 골드가 부족하면 구매할 수 없음.
- 다양한 아이템 구성 (무기, 방어구, 소비품).
📊 상태 확인 시스템
- 플레이어 이름, 직업, 능력치를 모두 출력.
- 장착된 아이템과 그 효과도 함께 보여줌.
- 능력치는 기본 능력치 + 장비 보너스로 계산.
🗡️ 던전 시스템 (개발 중)
- 난이도 선택 가능 (쉬움, 일반, 어려움).
- 전투 결과에 따라 보상 획득 or 체력 감소.
- 랜덤 아이템 드랍 시스템 구현 예정.
🚀 향후 개선 사항 및 개발 계획
- 코드 중복 줄이기 (공통 메서드는 별도로 만들기)
- 예외 처리 강화 (잘못된 입력 처리)
- 콘솔 UI 더 보기 좋게 만들기 (색상, 구분선 등)
- 특히 요정대사에 색상 넣고 싶다. 내 구글링이 좀만 더 우월했어도....
- 저장/불러오기 기능 도입 (파일로 저장 예정...아...마?)
🧱 앞으로 만들 기능들
- 던전 시스템 완성 (적을 만들기 보단. 일단. 시뮬레이션 식. 즉 난이도를 누르면 랜덤하게 체력 다운 등. 아니다, 피로도를...구현해버릴까?
- 경험치/레벨업 시스템 추가
- 휴식 시스템 추가(여관으로 만들기!)
- 스킬 시스템 추가 (직업별 고유 스킬)
- 간단한 스토리 이벤트 삽입
'팀스파르타 코딩' 카테고리의 다른 글
| 📚 TIL - 2025년 4월 16일 (수) / C# 문법 학습(인터페이스, 열거형, 예외처리, 델리게이트) (0) | 2025.04.16 |
|---|---|
| 📚 복습 정리 - 텍스트 RPG 개발에 사용된 C# 개념들 (개념 + 실습 응용) (0) | 2025.04.16 |
| 📘내일의 학습 목표: 인터페이스와 열거형 (1) | 2025.04.15 |
| 🧠2025년 4월 15일 (월) / C# 객체지향 심화용 - 제너릭과 out, ref 키워드 (0) | 2025.04.15 |
| 📚 TIL - 2025년 4월 15일 (월) / C# 객체지향 기본 (0) | 2025.04.15 |