[25.06.17] :: 가디언 앤 시커 프로젝트 15

chooha·2025년 6월 18일

가디언앤시커

목록 보기
15/25
post-thumbnail

📝 개발일지 - 아케인보드 클래스 변경 데이터 충돌 트러블 슈팅

👨‍💻 오늘의 개발 작업

오늘은 아케인보드 시스템에서 캐릭터 클래스 변경 시 이전 클래스의 룬 데이터가 새로운 클래스에 잘못 표시되는 버그를 해결했음

확인해보니 메모리 상 데이터 정리가 불완전해서 발생한 문제였음. 에디터에서는 정상 작동하지만 패키징 빌드에서만 나타나는 까다로운 버그였음


💡 오늘의 5분 기록

1. 문제 발견 - 패키징 빌드에서만 나타나는 이상 현상

증상:

  • 메르시에 배치된 룬이 찬의 아케인보드 인벤토리와 스탯에 적용됨
  • 찬에 배치된 룬이 메르시의 그리드에 표시됨
  • 에디터 환경에서는 정상 작동
  • 패키징된 빌드에서만 발생

2. 디버깅 전략 수립 - 체계적인 로그 추가

핵심 추적 포인트:

  • 클래스 변경 과정에서의 데이터 상태

  • 세이브/로드 시점의 실제 데이터!

  • UI 업데이트 순서와 타이밍

추가한 디버깅 로그:

// SetCurrClass 함수
UE_LOG(LogTemp, Warning, TEXT("클래스 변경 전 배치된 룬 개수: %d"), PlacedRunes.Num());
for (int32 i = 0; i < PlacedRunes.Num(); i++) {
    UE_LOG(LogTemp, Warning, TEXT("  룬 %d: ID=%d, Pos=(%d,%d)"), 
        i, PlacedRunes[i].RuneID, PlacedRunes[i].Pos.X, PlacedRunes[i].Pos.Y);
}

// LoadSavedData 함수  
UE_LOG(LogTemp, Warning, TEXT("로드할 룬 개수: %d"), Runes.Num());
UE_LOG(LogTemp, Warning, TEXT("최종 PlacedRunes 개수: %d"), PlacedRunes.Num());

3. 결정적 단서 발견 - 로그 분석을 통한 원인 파악

로그 패턴 분석:

[찬 → 메르시 클래스 변경]
LogTemp: Warning: 클래스 변경 전 배치된 룬 개수: 1
LogTemp: Warning:   룬 0: ID=2, Pos=(0,0)
LogTemp: Warning: 그리드 상태 초기화 시작
LogTemp: Warning: 초기화 후 배치된 룬 개수: 1  // ← 문제 발견!
LogTemp: Warning:   초기화 후 룬 0: ID=2, Pos=(0,0)

핵심 발견:

  • InitGridState() 호출 후에도 PlacedRunes가 그대로 남아있음
  • 클래스 변경 시 이전 클래스의 룬 데이터가 정리되지 않음

4. 근본 원인 분석 - 데이터 정리 로직의 누락

문제가 된 코드 흐름:

void UGS_ArcaneBoardManager::InitGridState(){
	CurrGridState.Empty();
    // PlacedRunes 초기화 안함

	if (IsValid(CurrGridLayout))
	{
		for (const FGridCellData& Cell : CurrGridLayout->GridCells)
		{
			FGridCellData NewCell = Cell;
			if (NewCell.State == EGridCellState::Empty)
			{
				NewCell.PlacedRuneID = 0;
			}
			else if (NewCell.State == EGridCellState::Occupied)
			{
				PlacedRunes.Add(FPlacedRuneInfo(Cell.PlacedRuneID, Cell.Pos));
			}
			CurrGridState.Add(Cell.Pos, NewCell);

			if (Cell.bIsSpecialCell)
			{
				SpecialCellPos = Cell.Pos;
			}
		}
	}
}
bool UGS_ArcaneBoardManager::SetCurrClass(ECharacterClass NewClass) {
    // ...
    if (bNeedGridReset) {
        InitGridState();        // 그리드 상태만 초기화
        CalculateStatEffects(); // 뒤섞인 PlacedRunes로 계산!
        AppliedBoardStats = CurrBoardStats;
    }
    // ...
}

InitGridState() 함수의 역할:

  • 그리드 셀 상태 초기화
  • 새로운 클래스의 레이아웃 적용
  • BUT: PlacedRunes 배열은 초기화 하지 않음

결과:

  • 메르시로 클래스 변경 → 찬의 룬 데이터가 메모리에 남음
  • 새로운 그리드에 이전 룬 정보가 잘못 적용됨

5. 해결

초기화: InitGridState()에서 정리

void UGS_ArcaneBoardManager::InitGridState() {
    PlacedRunes.Empty();  // 추가된 한 줄
    CurrGridState.Empty();
    // 나머지 초기화 로직...
}

6. 에디터 vs 패키징 환경 차이점 분석

에디터에서 정상인 이유:

  • PIE(Play In Editor) 환경의 빈번한 메모리 리셋
  • Hot Reload로 인한 오브젝트 재생성
  • 가비지 컬렉션 타이밍 차이

패키징에서 문제 드러난 이유:

  • 더 엄격한 메모리 관리
  • 오브젝트 생명주기의 일관성
  • 숨겨진 타이밍 이슈들이 명확하게 드러남

7. 핵심 성과와 교훈

해결된 문제:

  • ✅ 클래스 변경 시 데이터 완전 분리
  • ✅ 패키징 환경에서도 안정적 작동
  • ✅ 메모리 누수 및 데이터 혼재 방지

얻은 교훈:

  • 상태 초기화 함수는 관련된 모든 데이터를 정리해야 함
  • 에디터 환경에서만 테스트하면 안 됨
  • 체계적인 디버깅 로그가 문제 해결의 핵심
  • 메모리 관리는 예상보다 복잡하고 환경에 따라 다름

0개의 댓글