1. 문제 발견
- DefaultInventory.Start() → InsertPossible 또는 RemainderIfInserted 호출 시 NullReferenceException 발생
- 오류가 ItemSlotWithProcessor 내부의 insertionProcessors 리스트 중 null 요소를 건드릴 때 터짐
- 콘솔 로그로 확인해 보니, ConstraintCollection.Constraints 의 0번 인덱스에 빈(삭제되지 않은) 슬롯(null)이 남아 있었음
2. 디버깅 과정
- InitSlot()에 Debug.Log 추가
- ConstraintCollection.Constraints 리스트 길이와 각 요소 타입(또는 null)을 출력
- 로그 결과
Constraints[0] = NULL
Constraints[1] = MaxCapacityConstraint
Constraints[2] = InstancesOnlyConstraint
- ItemSlotWithProcessor InsertPossible / RemainderIfInserted
- 내부 반복문에 null 체크 로그 삽입
- 어느 인덱스에서 null이 들어오는지 정확히 파악
3. 해결 방법
- Inspector 에셋 정리
- InstantiateConstraint.asset·StandardSlotConstraints.asset 의 Constraints 리스트에서
- “Missing (Mono Script)” 또는 빈 슬롯을 완전히 Remove Element
- 순서대로 MaxCapacityConstraint, InstancesOnlyConstraint 등 올바르게 연결
- InstantiateConstraint.asset·StandardSlotConstraints.asset 의 Constraints 리스트에서
- 코드 레벨 안전 처리
- 이렇게 하면 런타임에 null이 섞이지 않아 이후 InsertPossible·RemainderIfInserted에서 더 이상 예외가 발생하지 않음
void InitSlot()
{
if (ConstraintCollection)
constraints.AddRange(
ConstraintCollection.Constraints
.Where(c => c != null) // null 요소 필터링
);
slot = new ItemSlotWithProcessor(constraints, World);
slot.Changed += FireChange;
}
4. 배운 점
- Unity Asset 직렬화: ScriptableObject(Prefab) 안에 남아 있는 “Missing” 슬롯이 런타임 로직에 치명적인 null을 유발
- 디버깅 팁: 컬렉션 상태를 런타임에 직접 찍어 보는 것이 문제 파악에 유용
- IEnumerable 안전 처리: AddRange(...Where(...)) 패턴으로 null 항목을 사전에 차단
- 에셋 vs 코드: 가능한 한 에셋(Inspector)에서 올바르게 연결하고, 코드에서는 최소한의 방어 로직만 두는 것이 깔끔함
다음에 적용할 점
- Asset 변경 시 에디터 경고(Missing types)를 즉시 해결하기
- 초기화 로직에 null 필터링 습관 들이기
- 중요한 로직(Insert/Remainder) 앞뒤로 작은 단위 디버깅 로그 활용하기