제작 조건 검증 및 제작 실행 구조

목차

1. 요구 사항

2. 설계 목표

3. 흐름도

4. 구현

5. 개발 의도

1. 시스템 요구 사항

제작 슬롯과 선택 상태를 정리한 이후, 제작 시스템에서 가장 중요한 단계는 제작 버튼을 눌렀을 때 실제로 아이템을 만들어도 되는지 판단하고, 그 결과를 안정적으로 반영하는 구조였다.

제작은 단순히 아이템을 하나 추가하는 행위가 아니라, 재료 소모와 재화 차감이 동시에 이루어지는 트랜잭션에 가깝다.

이 과정에서 조건 검증과 실행 로직이 섞이면, 일부 재화만 차감되거나 제작 실패 후에도 아이템이 추가되는 등 치명적인 오류가 발생할 수 있다.

초기 구현에서는 버튼 클릭 시점에 재료 수량 비교와 제작 실행이 한 함수 안에서 동시에 이루어졌고, 이로 인해 어디까지가 검증이고, 어디서부터가 실제 변경인가가 코드상으로 명확하지 않았다.

특히 UI 텍스트를 그대로 신뢰해 제작을 실행하는 구조는, 표시와 실제 상태가 어긋날 경우 위험 요소가 될 수 있었다.

그래서 제작 시스템에서는 조건 검증과 실제 제작 처리를 명확히 분리하고, 제작 버튼은 항상 하나의 진입점만을 통해 동작하도록 구조를 재정리할 필요가 있었다.

2. 설계  목표

- 제작 조건 검증과 제작 실행을 명확히 분리할 것

- 제작 버튼 단일 진입점만을 사용하도록 할 것

- 재료·재화 차감과 아이템 지급을 하나의 흐름으로 묶을 것

- 실패 시 시스템 상태가 변경되지 않도록 할 것

3. 흐름도

이 구조에서 중요한 점은, 제작 실행이 조건 검증을 통과한 경우에만 발생한다는 것이다.

조건 검증 단계에서는 시스템 상태를 절대 변경하지 않고, 오직 제작이 가능한가만 판단한다.

실제 데이터 변경은 제작 실행 단계에서만 이루어진다.

4. 구현

4.1. 제작 시도 단일 진입점
private void TryCraft(Item item, int index)
{
    int haveItems = int.Parse(haveItemTxt.text);
    int needItems = int.Parse(needItemTxt.text);
    int haveCoins = int.Parse(haveCoinTxt.text);
    int needCoins = int.Parse(needCoinTxt.text);

    if (haveItems >= needItems && haveCoins >= needCoins)
        CraftItem(item, index, needItems, needCoins);
    else
        StartCoroutine(ShowTemporaryMsg(cantCraftMsg, 0.5f));
}

제작 버튼은 항상 TryCraft() 함수 하나로만 진입한다.

이 함수의 역할은 제작 가능 여부 판단에만 집중한다.

실제 제작 처리는 여기서 직접 수행하지 않고, 조건을 만족한 경우에만 CraftItem()을 호출한다.

여기서 UI 텍스트를 기반으로 수량을 읽는 구조를 유지한 이유는, 제작 패널의 UI가 항상 선택 상태 기준으로 실시간 갱신되고 있기 때문이다.

다만 이 구조는 UI가 단일 선택 상태(selectedIndex)를 기준으로 항상 데이터와 동기화되고 있다는 전제 하에서만 안전하다.

이 조건은 이전 게시글에서 설계한 선택 상태 단일화 구조를 통해 확보되어 있으며,

UI가 현재 상태를 단순히 표현하는 계층으로만 동작하도록 제한했기 때문에 검증 단계에서 UI 값을 참조해도 불일치가 발생하지 않는다.

4.2. 제작 실행 로직 분리
private void CraftItem(Item item, int index, int requiredItem, int requiredCoin)
{
    materialItems[index].count -= requiredItem;
    itemManager.coin -= requiredCoin;

    itemManager.AddItem(item.itemCode, 1);
    UpdateUIAfterCraft(index);
}

CraftItem() 함수는 실제 데이터 변경만을 담당한다.

이 함수 안에서는 조건 판단을 하지 않으며, 이미 검증을 통과했다는 전제 하에 재료 차감, 재화 차감, 아이템 지급을 순차적으로 수행한다.

이렇게 역할을 분리함으로써, 제작 실패 시 데이터가 일부만 변경되는 상황을 구조적으로 차단할 수 있다.

아이템 지급은 ItemManager.AddItem()을 통해 처리한다.

이 방식은 제작 시스템이 인벤토리 내부 구조를 직접 알 필요 없이, 아이템 매니저에 아이템을 추가하라는 요청만 전달하도록 만든다.

이로 인해 제작 시스템과 인벤토리 시스템 간 결합도가 낮아지고, 이후 인벤토리 구조가 변경되더라도 제작 로직은 영향을 받지 않는다.

4.3. 제작 결과 UI 반영
private void UpdateUIAfterCraft(int index)
{
    haveItemTxt.text = materialItems[index].count.ToString();
    haveCoinTxt.text = itemManager.coin.ToString();
}

제작이 완료된 후에는 UI를 즉시 갱신한다.

다만 이 갱신은 상태를 계산하는 과정이 아니라, 이미 변경된 상태를 화면에 반영하는 단계다.

실제 데이터 변경은 모두 이전 단계에서 끝났고, UI는 그 결과를 그대로 보여주기만 한다.

이 구조 덕분에 제작 성공 후 UI가 잘못된 값을 표시할 여지가 줄어든다.

4.4. 실패 피드백 처리와 코루틴 사용
private IEnumerator ShowTemporaryMsg(GameObject obj, float duration)
{
    obj.SetActive(true);
    yield return new WaitForSeconds(duration);
    obj.SetActive(false);
}

제작 조건을 만족하지 못한 경우에는, 시스템 상태를 변경하지 않고 짧은 메시지 피드백만 제공한다.

이때 Unity의 Coroutine을 사용해 일정 시간 동안만 메시지를 표시하도록 했다.

코루틴은 프레임 흐름을 막지 않으면서 시간 기반 처리를 할 수 있기 때문에, UI 피드백에 적합하다.

Invoke나 Update 기반 타이머보다 코드 의도가 명확하고 관리가 쉬운 것도 장점이다.

5. 개발 의도

이 게시글에서의 핵심은 제작을 하나의 안전한 트랜잭션처럼 다루는 구조를 만드는 것이었다.

제작 가능 여부 판단과 실제 제작 실행을 분리함으로써, 실패 시에는 아무것도 변하지 않고 성공 시에는 모든 변경이 일관되게 적용되도록 설계했다.

제작 버튼은 단일 진입점만을 가지며, 제작 실행은 항상 검증 이후에만 이루어진다.

이 구조 덕분에 이후 제작 결과 연출, 제작 수량 확장, 제작 실패 확률 같은 요소를 추가하더라도, 시스템의 중심 흐름은 흔들리지 않는다.

'선택 상태 → 조건 검증 → 제작 실행' 이라는 단계가 명확히 분리되어 있기 때문이다.