스택 분할 시스템 - 부분 이동을 명시적 입력으로 제어하는 구조
목차
1. 시스템 요구 사항
인벤토리 정리 시스템이 병합과 스왑까지 안정적으로 처리되더라도, 여전히 남아 있는 중요한 상호작용이 하나 있었다.
바로 하나의 스택을 둘로 나누어 배치하는 스택 분할이다.
포션을 절반만 옮기거나, 제작 재료를 일부만 분리해 보관하거나, 상점에 판매할 수량만 따로 떼어내는 행위는 실제 게임 플레이에서 매우 빈번하게 발생한다.
하지만 이 기능을 단순히 드래그 중 일부만 이동 같은 암묵적인 규칙으로 처리하면, 플레이어는 언제 분할이 일어나는지 예측하기 어렵고 실수 가능성도 커진다.
특히 문제였던 지점은 병합과 스왑은 결과가 단일하지만, 분할은 수량 선택이라는 추가적인 의사결정을 요구한다는 점이다.
이 선택이 UI 코드 안에서 즉흥적으로 처리되거나, 드래그 조건에 따라 자동으로 분기되기 시작하면, 인벤토리 정리 규칙은 다시 UI 계층으로 흘러들어가게 된다.
따라서 스택 분할은 자동 정리의 일부가 아니라, 플레이어의 명시적인 입력에 의해 호출되는 독립적인 기능으로 정의될 필요가 있었다.
자동 분할이 허용되면, 동일한 입력에서도 상황에 따라 병합·스왑·분할 결과가 달라질 수 있고, 이는 인벤토리 정리 결과를 예측하기 어렵게 만든다.
2. 설계 목표
- 스택 분할은 플레이어의 명시적 입력에 의해서만 발생할 것
- 분할 과정에서 병합·스왑 규칙과 충돌하지 않을 것
- 분할 로직은 UI가 아닌 데이터 레이어에서 수행될 것
- 분할 결과는 항상 예측 가능하고 결정적일 것
- 기존 인벤토리 정리 규칙과 자연스럽게 결합될 것
3. 흐름도
[스택 분할 입력 발생]
↓
[분할 수량 입력 UI 표시]
↓
[입력 수량 유효성 검증]
↓
[원본 슬롯 수량 감소]
↓
[대상 슬롯에 새 스택 생성]
↓
[인벤토리 데이터 갱신]
이 흐름에서 중요한 점은 분할이 병합·스왑 과정 중에 자동으로 끼어들지 않는다는 점이다.
분할은 항상 '의도된 입력 → 검증 → 실행' 이라는 독립된 흐름을 가지며, 그 결과만이 이후 정리 시스템의 입력으로 사용된다.
4. 구현
4.1. 스택 분할 실행 로직
public bool SplitStack(int fromIndex, int toIndex, int splitCount)
{
if (fromIndex == toIndex) return false;
var from = GetSlot(fromIndex);
var to = GetSlot(toIndex);
if (from == null || to == null) return false;
if (from.IsEmpty) return false;
if (!to.IsEmpty) return false;
if (splitCount <= 0) return false;
if (splitCount >= from.count) return false;
from.count -= splitCount;
to.data = from.data;
to.count = splitCount;
return true;
}
이 함수는 하나의 슬롯에서 지정된 수량만큼을 분리해 다른 슬롯으로 이동시키는 책임을 가진다.
병합이나 스왑과 달리, 이 로직은 반드시 외부에서 수량이 확정된 상태로만 호출된다.
함수 진입부에서는 동일 슬롯 분할을 차단해 의미 없는 호출을 제거한다.
이후 원본 슬롯과 대상 슬롯이 모두 유효한지 확인하며, 대상 슬롯이 비어 있는 경우에만 분할이 가능하도록 제한한다.
이는 분할 결과가 병합이나 스왑과 섞이지 않도록 하기 위한 명확한 제약이다.
분할 수량에 대한 검증 역시 이 함수 내부에서 수행된다.
0 이하의 수량이나, 원본 스택 전체를 넘는 수량은 분할의 의미가 없기 때문에 즉시 실패 처리된다.
이 검증을 데이터 레이어에서 수행함으로써, UI 입력이 잘못 전달되더라도 인벤토리 데이터가 오염되지 않도록 했다.
실제 분할은 매우 단순한 형태로 이루어진다.
원본 슬롯의 수량을 감소시키고, 대상 슬롯에 동일한 아이템 데이터와 분할 수량을 설정한다.
슬롯 인스턴스를 교체하지 않고 내부 데이터만 변경함으로써, UI와의 참조 관계를 안정적으로 유지할 수 있다.
반환값은 분할 성공 여부를 의미하며, 상위 시스템에서는 이 결과를 기준으로 UI 갱신이나 추가 입력 처리를 결정할 수 있다.
4.2. 분할 입력과 정리 시스템의 연결
스택 분할은 병합이나 스왑과 달리, 항상 선행 입력(UI) 이 존재해야 한다.
플레이어가 특정 슬롯에서 분할을 선택하면, UI는 분할 수량을 입력받은 뒤 그 결과만을 데이터 레이어에 전달한다.
이때 중요한 점은, UI는 어떻게 나뉘는지를 결정하지 않고 몇 개를 나눌지만 전달한다는 것이다.
분할 이후의 슬롯 배치는 기존의 정리 시스템과 동일한 규칙을 따른다.
즉, 분할로 생성된 스택 역시 이후 드래그 &드롭 시 병합 우선 정책의 대상이 되며, 정리 규칙은 여전히 한 곳에서만 유지된다.
이는 분할 결과 역시 인벤토리 정리 시스템의 규칙 안에 포함되도록 하기 위한 의도적인 설계다.
5. 개발 의도
이 스택 분할 시스템에서 가장 중요하게 지킨 원칙은, 분할은 자동 정리가 아니라, 의도된 조작이라는 점이었다.
병합과 스왑은 플레이어가 기대하는 자연스러운 정리에 가깝지만, 분할은 명확한 수량 선택이라는 의사결정을 포함한다.
이 차이를 무시하고 분할을 암묵적인 규칙으로 처리하면, 인벤토리 시스템은 다시 예측 불가능한 상태로 돌아가게 된다.
그래서 이 시스템에서는 분할을 하나의 독립된 기능으로 정의하고, 명시적인 입력 없이는 절대 발생하지 않도록 구조를 고정했다.
그 결과 인벤토리 시스템은 자동으로 정리되는 부분과 플레이어가 직접 통제하는 부분이 명확히 분리되었고, 데이터 레이어는 항상 일관된 규칙만을 유지할 수 있게 되었다.
이 스택 분할 시스템은 단순한 편의 기능이 아니라, 인벤토리 상호작용의 자유도를 제공하면서도 데이터 무결성과 예측 가능성을 동시에 지키기 위한 마지막 퍼즐 조각으로 설계되었다.
