[C#] 리팩토링 과정에서 발생한 객체 참조 공유 문제

shin0112·2025년 10월 17일
0

CSHARP

목록 보기
4/4

✨ 들어가며

오늘은 MonsterRepository를 리팩토링하던 중 몬스터 상태가 전투마다 초기화되지 않는 문제를 마주했다.

원래는 new로 새 List를 만들어 매번 전투에 넘기는 방식이었는데, 던전마다 몬스터 목록을 매칭하기 위해 Dictionary<int, List<List<Monster>>>로 바꾸면서 의도치 않게 몬스터 인스턴스가 메모리상에서 공유되는 구조가 되었다.


1️⃣ 문제 상황

리팩토링 전에는 아래와 같은 방식으로 매번 새로운 리스트를 생성했다.
때문에 랜덤 스폰 시에 Monster를 가지고 와도 항상 새로운 Monster를 가지고 올 수 있다.

List<Monster> Monsters => new()
{
    new Monster("슬라임", 1, new Stats(10, 2, 1)),
    new Monster("고블린", 2, new Stats(20, 3, 2))
};

하지만 던전 단위로 몬스터를 관리하려다 보니, 아래처럼 딕셔너리로 몬스터 목록을 저장하게 되었다.

DungeonMonsters = new Dictionary<int, List<List<Monster>>>
{
    { 1, new() { MonstersNo1, SpecialMonstersNo1 } },
    { 2, new() { MonstersNo2, SpecialMonstersNo2 } }
};

이제 MonstersNo1 안의 Monster 객체들은 딕셔너리에 한 번 들어가면 프로그램이 끝날 때까지 메모리에 남는다.
결국 한 전투에서 IsDead = true가 되면, 그 인스턴스는 다른 전투에서도 그대로 죽은 상태로 유지된다.

2️⃣ 원인

C#에서 class참조형(reference type) 이기 때문에 리스트에 들어간 Monster는 “값의 복사본”이 아니라 “주소”만 전달된다.

즉, Monsters.Add(monsters[0]); 라고 하면, 새로운 몬스터를 만드는 게 아니라 이미 존재하던 객체의 메모리 주소를 가리키는 참조만 추가하는 셈이다.

그래서 어떤 전투에서 HP가 줄거나 죽어도, 그 정보가 그대로 다른 전투까지 이어진다.
이는 객체의 생명주기를 명확히 끊지 못한 구조다.

3️⃣ 해결

이 문제를 해결하기 위해, Monster 클래스에 Clone() 메서드를 추가해서 전투마다 새로운 객체 인스턴스를 생성하도록 했다.

public Monster Clone()
{
    return new Monster(Name, Level, new Stats(Stats.Atk, Stats.Def, Stats.Hp));
}

이러면 전투 시작 시에는 Repository의 몬스터를 그대로 가져오지 않고, 복제본을 생성해서 사용할 수 있다.

var original = monsters[0][random.Next(monsters[0].Count)];
Monsters.Add(original.Clone());

이제 매번 new를 통해 새로운 메모리 공간에 독립적인 Monster 객체가 만들어지므로 전투마다 완전히 초기화된 상태로 시작된다.

4️⃣ 객체 생명주기 정리

시점객체 상태메모리 위치관리 주체
Repository 초기화 시템플릿 데이터 생성힙(Heap)MonsterRepository
전투 시작 시복제된 Monster 인스턴스 생성힙(Heap)BattleManager
전투 종료 시참조 해제 (Monsters.Clear())GC(가비지 컬렉터)가 수거-

이제 Repository는 순수하게 몬스터 정보(템플릿)만 저장하고, 전투마다 생성되는 몬스터들은 잠깐 메모리에 존재하다가 참조가 사라지면 가비지 컬렉터에 의해 정리된다.

✍️ 마치며

이번 문제는 단순한 참조 실수가 아니라, 객체를 한 번만 만들고 계속 돌려쓰는 구조에서 비롯된 오류였다.

Repository는 말 그대로 데이터를 보관하는 곳이어야 하고, 실제 전투에서 쓰는 몬스터는 매번 new로 새로 만들어야 한다.

알고 있는 개념이었지만, 막상 코드에 직접 적용하려고 할 때는 그 내용을 자연스럽게 녹여내기까지 충분한 연습이 필요하다 는 걸 다시금 느낀다.

0개의 댓글