구현하고 싶은 기능은 많았지만, 제한된 시간 안에 최대한 깔끔하게 구조화하고자 노력했습니다.
👤 개발자
이재은
맨날 유니티 개발만 하다가 C# 으로 콘솔 게임을 만들게 될 줄은 몰랐다 솔직히 텍스트로만 보이고 개발하다보니 좀 어려운 것도 있었지만 나의 기초를 다듬고 보완하는 시간이였다. 머리가 아팠지만 그럴수록 너무 재밌었고 어떻게 해야할지 감이왔었다. 하나하나 차근차근히 해보니 실행이 잘 될때의 쾌감이란.. 이래서 개발자 한다 ㅎㅎ..
09:00 ~ 11:30 : 텍스트 RPG 기능 개발 (던전 기능 설계 및 레벨업 시스템 구상)
11:30 ~ 13:00 : C# 체크리스트 강의 Day 3 수강
주제: 메서드
주요 내용: 메서드 선언과 호출, 매개변수/반환값, 연산자 실습
🍽️ 점심시간
13:00 ~ 14:00 : 점심시간
✅ 오후
14:00 ~ 18:00 : 텍스트 RPG 기능 구현
던전 시스템 완성 (난이도/보상/실패 확률 등)
클리어 횟수 기반 레벨업 구현
장착 아이템 능력치 반영 문제 해결
저장/불러오기 시 능력치 정상 반영
🍽️ 저녁시간
18:00 ~ 19:00 : 저녁시간
✅ 저녁
19:00 ~ 20:30 : 오늘 작성한 코드 복습 및 구조 점검
20:30 ~ 21:00 : TIL 내용 정리 및 작성
✅ 오늘 학습 키워드
텍스트 RPG 던전 시스템 구현
레벨업 시스템 설계 및 구현
난이도에 따른 전투 결과 처리
경험치 계산 및 능력치 반영
아이템 능력치 저장/불러오기 버그 수정
C# 메서드 선언과 호출
매개변수와 반환값
산술/관계/논리 연산자 실습
코드 구조 복습 및 정리
✅ 오늘 학습 한 내용을 나만의 언어로 정리하기
오늘은 텍스트 RPG의 핵심 기능인 던전 시스템과 레벨업 로직을 본격적으로 구현했다. 난이도에 따라 전투 결과가 달라지고, 클리어 여부에 따라 경험치와 보상을 획득할 수 있도록 설계했다.
레벨업 시스템을 만들었고, 능력치도 구현했다.
저장/불러오기 기능에서도 중요한 이슈가 하나 있었는데, 장착한 아이템의 능력치가 불러오기 후 반영되지 않는 문제였다. 꽤 골치 아픈 버그였지만, 결국 장비 장착 로직과 플레이어 능력치 반영 구조를 다시 살펴보며 해결할 수 있었다. 버그가 사라지는 순간, 굉장히 굉장히.. 뿌듯했다.
오전 강의에서는 메서드의 기본 구조와 개념을 다시 짚었고, 실습 문제를 통해 다양한 연산자를 복습했다. 실제 게임 시스템 안에서 메서드를 설계하고 나니 얼마나 중요한 기초인지 느낄 수 있었다.
저녁 시간에는 오늘 만든 코드를 차분히 복습하면서 구조적으로 더 깔끔하게 만들 수 있는 방법도 고민했다. 하루가 끝날 땐, 나의 실력이 1레벨 올라간 기분이었다. 😊
🧩 학습하며 겪었던 문제점 & 에러
1. 저장 후 불러올 때 장착 아이템 능력치가 반영되지 않음
문제정의
아이템을 장착한 상태로 저장하고 나서 불러오면, 장비는 잘 표시되는데 능력치(공격력/방어력)에 반영되지 않는 문제가 발생함.
시도한 방법
LoadPlayer()에서 장착 상태만 복원하고, 능력치 재계산을 따로 하지 않은 상태였음.
처음엔 장비가 잘 불러와졌기에 기능상 문제 없다고 착각했음.
해결 방법 (코드 포함)
장착 아이템의 능력치를 적용하는 메서드를 새로 만들어, 불러온 후 적용하도록 수정함.
public void LoadPlayer()
{
// 저장된 값 로드
player.Level = data.Level;
player.Exp = data.Exp;
// ...
// 장비 복원
foreach (var item in data.EquippedItems)
{
player.EquipItem(item); // 착용만 했고 능력치 반영이 안 됐었음
}
// ✅ 능력치 반영 추가
player.ApplyEquippedStats();
}
// 새로 만든 메서드
public void ApplyEquippedStats()
{
BaseAttack = originalBaseAttack;
BaseDefense = originalBaseDefense;
foreach (var item in EquippedItems)
{
BaseAttack += item.Attack;
BaseDefense += item.Defense;
}
}
새롭게 알게 된 점
"장착"이라는 상태만 저장하는 것이 아니라, 그 상태가 게임 시스템에 어떤 영향을 주는지까지 고려한 로직이 필요하다는 걸 알게됨.
다시 만나게 된다면
불러온 데이터가 정상 동작하는지 확인할 때 단순히 UI만 보지 말고 값이 정확히 적용되는지 출력해보는 습관을 들이자.
2. 장비 장착 상태에서는 정상인데 +능력치만 사라진 현상
문제정의
저장된 캐릭터를 불러오면 장비창과 인벤토리엔 아이템이 정상적으로 표시되지만, 플레이어 능력치에 +Attack, +Defense 보정치가 반영되지 않음.
시도한 방법
장비가 눈에 보이는 걸로 착용 완료라고 생각했지만, 능력치 수치가 기본값으로만 출력되었고, 로그 찍어보며 확인.
해결 방법 (코드 포함)
EquipItem()과 LoadPlayer() 호출 흐름을 점검하여, 장착 상태를 적용한 직후 능력치를 반영하도록 수정함.
public void EquipItem(Item item)
{
if (!EquippedItems.Contains(item))
EquippedItems.Add(item);
// 능력치 반영
BaseAttack += item.Attack;
BaseDefense += item.Defense;
}
또는 LoadPlayer()에서 일괄 계산하는 방식으로 ApplyEquippedStats()처럼 구성함.
새롭게 알게 된 점
장비 시스템은 UI와 데이터, 기능(능력치 반영)이 완전히 분리되어 있으면 버그가 생기기 쉽다는 걸 느낌.
다시 만나게 된다면
장비 착용 → 능력치 반영 → 화면 출력까지의 흐름을 항상 하나의 덩어리로 인식하자.
📝 메모
오늘은 하루 종일 텍스트 RPG의 구조를 다지고 기능을 쌓아가는 데 집중했다. 특히 던전과 레벨업 시스템을 직접 설계하고 구현하면서, 내가 지금 배우고 있는 C#과 게임 개발 지식들이 하나로 연결되는 느낌이 들었다.
하나하나 해결해나가면서 느낀 점은 처음엔 복잡해 보이던 문제들도 디버깅하고 출력해보며 하나씩 분석하니 결국 실마리가 풀렸다. 그럴때의 쾌감은 이루 말할 수 없다..ㅎㅎ 이래서 개발자 한다고 생각한다!
무엇보다 오늘은 "장비 능력치가 반영되지 않는 문제"를 해결하면서 꽤 큰 성취감을 느꼈다. 단순히 코드가 돌아가는 걸 넘어서, 게임 시스템 전체의 흐름을 고민하게 되었고, 그게 바로 개발자다운 성장이라는 생각이 들었다.
코드를 복습하며 내가 만든 로직을 스스로 설명할 수 있게 되니까 조금은 더 자신감도 생겼다.
콘솔 게임을 통해 C#의 조건문, 반복문, 랜덤 숫자 생성, 리스트 사용을 복습했다. 사용자와 딜러가 번갈아 카드를 뽑고 점수를 비교하는 방식으로 진행되며, 게임 흐름 제어에 while, break, continue 등이 효과적으로 사용되었다.
2. 인터페이스 (interface)
interface는 클래스들이 공통적으로 가져야 할 기능(메서드 시그니처)을 정의하는 틀이다. 클래스에 : IAttackable 같은 식으로 상속하며, 명세된 메서드를 반드시 구현해야 한다. 인터페이스는 다중 상속이 가능하다는 점에서 클래스보다 더 유연한 설계가 가능하다.
3. 열거형 (enum)
열거형은 관련된 상수들을 그룹화해 코드를 더 명확하게 만든다.
예:
enum Scene { Start, PlayerInfo, Shop, Inventory, Battle, Dungeon }
숫자 대신 의미 있는 이름으로 분기를 할 수 있어 가독성이 향상된다.
4. 예외 처리 (try-catch)
사용자의 입력이나 연산 과정에서 오류가 발생할 수 있는데, 이를 미리 감지하고 처리할 수 있도록 try-catch 블록을 사용했다. 특히 int.Parse() 실패에 대비해 TryParse()로 안정성을 확보함.
5. 값 형식과 참조 형식, 박싱과 언박싱
int, float 같은 값형은 스택에 저장되고, class나 object는 힙에 저장되는 참조형이다. 값형을 object에 담을 때는 박싱(Boxing), 다시 꺼낼 땐 언박싱(Unboxing)이 일어난다. 메모리 구조를 이해하면 퍼포먼스 최적화에도 도움이 된다.
6. 델리게이트(delegate)와 람다(lambda)
델리게이트는 메서드를 변수처럼 다룰 수 있는 기능이고, 람다는 익명 메서드를 간결하게 작성할 수 있도록 도와주는 문법이다.
프로그래밍을 하다 보면 "값이 없을 수도 있는 상황"이 자주 발생한다. 예를 들어, 점수가 없을 수도 있고, 데이터가 존재하지 않을 수도 있다. 이럴 때 무작정 값을 쓰려고 하면 오류가 발생할 수 있는데, 이걸 안전하게 처리하는 방법이 바로 Nullable 형식과 null 조건 연산자다!
✅ Nullable 형식 (?)
값 타입에도 null을 허용하도록 해주는 문법
int? score = null; // OK!
int score = null; // 오류! 기본 int는 null 불가
int, float, bool 같은 값 타입은 기본적으로 null을 가질 수 없음
int?, bool?처럼 ?를 붙이면 null을 담을 수 있는 형식이 된다
🔎 사용 예시
int? age = 25;
if (age.HasValue)
Console.WriteLine(age.Value); // 25 출력
else
Console.WriteLine("나이 정보 없음");
HasValue: 값이 있는지 여부
Value: 실제 값을 꺼냄 (null일 경우 예외 발생 주의)
✅ null 조건 연산자 (?.)
객체가 null인지 먼저 확인하고, null이 아니면 멤버에 접근
Player player = null;
int? level = player?.Level;
?. 연산자를 사용하면 player가 null일 경우 null 반환
null이 아니면 .Level 값 반환
📌 예시
string name = null;
Console.WriteLine(name?.ToUpper()); // 출력 안됨, null
name = "철수";
Console.WriteLine(name?.ToUpper()); // CHULSU
이번엔 드디어 **델리게이트(delegate)**와 람다(lambda) 개념에 대해 배웠다. 처음엔 "함수를 변수처럼 넘긴다고?" 싶어서 어려웠는데, 직접 써보니까 유연한 코드 설계를 가능하게 해주는 정말 강력한 기능이라는 걸 느꼈다.
✅ 델리게이트(Delegate)란?
메서드를 참조할 수 있는 변수
함수를 변수처럼 저장하거나, 메서드를 다른 메서드에 전달하고 싶을 때 사용된다.
delegate int Calculate(int x, int y); // 델리게이트 선언
int Add(int a, int b) => a + b;
Calculate calc = Add; // 메서드 참조
Console.WriteLine(calc(3, 5)); // 8 출력
public delegate void AttackHandler(int damage);
public class Enemy
{
public event AttackHandler OnAttack;
public void Attack()
{
OnAttack?.Invoke(10); // 콜백 호출
}
}
public class Player
{
public void TakeDamage(int damage)
{
Console.WriteLine($"플레이어가 {damage} 데미지를 입었습니다.");
}
}
// 사용
Enemy enemy = new Enemy();
Player player = new Player();
enemy.OnAttack += player.TakeDamage;
enemy.Attack(); // → 데미지 전달됨
event 키워드를 사용하면 델리게이트를 외부에서 직접 호출하는 걸 방지할 수 있다.
OnAttack += 함수 식으로 이벤트에 메서드를 등록하고,
Invoke()로 콜백을 실행한다.
🔍 LINQ 완전 정복 - 컬렉션을 자유자재로 다루자!
이번에 공부한 LINQ는 정말 마법 같았다 ✨ 배열이나 리스트에서 데이터를 꺼낼 때마다 반복문을 돌리는 게 너무 귀찮았는데, LINQ를 사용하니까 마치 SQL처럼 직관적으로 데이터를 조회할 수 있었다!
✅ LINQ란?
**컬렉션 데이터(Queryable 데이터)**를 간결하게 다루는 문법
배열, 리스트, 딕셔너리 등에서 원하는 데이터를 쉽게 뽑아올 수 있음
where, select, order by 같은 SQL과 비슷한 구문으로 필터링
내부적으로는 반복문이지만, 코드를 훨씬 읽기 좋게 만듦
✅ LINQ 기본 문법 2가지
📌 1. 쿼리 구문 (SQL 스타일)
int[] numbers = { 1, 2, 3, 4, 5 };
var even = from n in numbers
where n % 2 == 0
select n;
foreach (var num in even)
Console.WriteLine(num); // 2, 4
from ~ in ~ 으로 컬렉션 순회
where는 조건 필터링
select는 어떤 값을 뽑을지 지정
📌 2. 메서드 구문 (람다식 기반)
var even = numbers.Where(n => n % 2 == 0);
foreach (var num in even)
Console.WriteLine(num); // 2, 4
Where는 조건 필터
Select는 변형
람다식과 찰떡궁합!
✅ LINQ 자주 쓰는 메서드 정리
메서드
설명
Where()
조건에 맞는 요소 필터링
Select()
원하는 형태로 변형
OrderBy()
오름차순 정렬
OrderByDescending()
내림차순 정렬
First()
첫 번째 요소 반환 (없으면 예외)
FirstOrDefault()
첫 번째 요소 or 기본값 반환
Any()
조건에 맞는 요소가 있는지 확인 (bool)
All()
모든 요소가 조건을 만족하는지 확인
Count()
요소 개수 반환
ToList()
결과를 리스트로 변환
Distinct()
중복 제거
GroupBy()
특정 키 기준으로 그룹화
✅ 예제: 학생 성적에서 필터링해보기
class Student
{
public string Name { get; set; }
public int Score { get; set; }
}
List<Student> students = new List<Student>
{
new Student { Name = "철수", Score = 85 },
new Student { Name = "영희", Score = 92 },
new Student { Name = "민수", Score = 78 },
};
// 점수 80점 이상 학생 필터링
var top = students.Where(s => s.Score >= 80)
.OrderByDescending(s => s.Score);
foreach (var s in top)
Console.WriteLine($"{s.Name} - {s.Score}점");
람다식으로 Where, OrderBy도 연결해서 사용 가능!
✅ LINQ는 어디에 쓰일까?
게임에서 조건에 맞는 오브젝트 찾기
UI 리스트에서 필터/정렬 기능 구현
서버에서 받아온 데이터 가공
배열/딕셔너리 탐색/변환 등
✅ 정리하며
델리게이트는 "함수를 변수처럼 다룰 수 있다"는 점에서 진짜 좋은 기능이었다. 처음에는 어렵게 느껴졌지만, 이벤트 처리 구조나 콜백 방식에 자주 쓰이고, 나중엔 Unity 에서도 굉장히 자주 만나게 될 것 같다.
람다식은 짧고 간결하게 코드를 표현할 수 있게 해주고, Action, Func 같은 델리게이트 타입과 함께 쓰면 정말 좋은듯 하다!
LINQ는 처음엔 생소하지만, 쓰면 쓸수록 더 적은 코드로 더 많은 일을 할 수 있게 해주는 도구다!
특히 람다식과 함께 쓰면 엄청 좋을듯 하다.
사실 저번에 델리게이트와 람다식, LINQ를 다뤄봤었지만 이번에 강의를 들으면서 다시 정리해봤다!