몬스터 드랍테이블에서 파츠를 "특정 PartDefinition 지정" 방식이 아니라 "레어도만 지정 → 실행 시점에 전체 파츠 풀에서 랜덤 1개 선택 + 해당 레어도 부여" 방식으로 뽑는다.
FNSRewardDropRow/FNSRewardDropResult에서 PartDefinition(TSoftObjectPtr<UNSPartDefinition>) 필드는 완전히 제거. Part 타입 행은 항상 "전체 파츠 풀에서 랜덤 선택" 방식만 사용 (몬스터별로 드랍 파츠 종류를 다르게 할 계획이 없다고 기획 확정 → 특정 파츠 못박는 경로 자체를 없앰).FGameplayTag RarityTag (Part.Rarity.Common/Rare/Epic/Legendary). 이유:DropGroupTag, RewardTypeTag, CurrencyTag, AugmentPoolTag, HealPotionTag)이 전부 태그라서, Rarity만 한글 UENUM이면 이질적임.RewardTypeTag(보상 종류 축)와 Rarity(등급 축)는 서로 다른 축이라 하나의 태그 계층으로 합치지 않음 — Reward.Type.Part.Common 식으로 만들면 RewardTypeTag == ... 정확 일치 비교 5곳(NSRewardHandler.cpp, NSRewardDropResolver.cpp)이 전부 깨짐.ENSPartRarity에 None을 추가하는 것도 검토했으나 기각. None을 추가하면 그 값이 실수로 PartData.CurrentRarity까지 흘러갔을 때 NSPartEquipComponent.cpp의 등급업 로직(static_cast<ENSPartRarity>(uint8(Rarity)+1))이 enum 범위 밖 값을 만들 수 있어 UI switch문에서 미정의 동작 위험이 있음. 태그는 미설정 시 자연히 "invalid 태그"가 되므로 별도 sentinel 값이 필요 없음.NSPartUtils::ResolveRarityFromTag(RarityTag, OutRarity)가 전담 (변환 실패 시 false 반환 → 호출부가 드랍을 안전하게 스킵).ENSPartRarity enum 자체는 원래대로 유지 (Common/Rare/Epic/Legendary, None 없음). 파츠 실제 로직(업그레이드/상점/세이브)은 계속 이 enum을 그대로 사용, 영향 없음.NSPartEquipComponent.cpp:631~647(상점 재고 생성)와 동일하게 DataSS->GetAllPartRows() 순회 + 인덱스 랜덤 선택.Source/NeoSanctum/Data/Part/NSPartTypes.h
None 추가했다가 되돌림. 최종적으로 원본 그대로: Common/Rare/Epic/Legendary만).Source/NeoSanctum/Tag/NSGameplayTags_Part.h / .cpp
Part.Rarity.Common / Rare / Epic / Legendary 4개 태그 신규 추가.Source/NeoSanctum/Progression/Part/NSPartUtils.h / .cpp
ResolveRarityFromTag(const FGameplayTag&, ENSPartRarity&) -> bool 추가. 태그 4개를 순서대로 비교해 매칭되는 enum 값을 반환, 매칭 안 되면 false..h에 #include "GameplayTagContainer.h" 추가, .cpp에 #include "NeoSanctum/Tag/NSGameplayTags_Part.h" 추가.Source/NeoSanctum/Data/Reward/NSRewardTypes.h
FNSRewardDropRow/FNSRewardDropResult 둘 다: PartDefinition 필드 삭제, RarityTag(FGameplayTag, meta=(Categories="Part.Rarity")) 추가.ENSPartRarity/UNSPartDefinition을 안 쓰므로 관련 include·forward-declare 제거.Source/NeoSanctum/Data/Reward/NSRewardDropResolver.cpp
ApplyDropRowToResult: OutResult.PartDefinition = ... 삭제, OutResult.RarityTag = Row.RarityTag; 추가. 리졸버는 태그를 그대로 전달만 함(파츠 세부 로직에는 관여하지 않음 — 책임 분리 유지).Source/NeoSanctum/Progression/Reward/NSRewardHandler.cpp
#include "Engine/AssetManager.h" 제거 (이 파일에서 UAssetManager 쓰는 곳이 없어짐).HandlePartDropResult: DropResult.PartDefinition 참조하던 가드/로그 전부 제거. PartData.IsValid() 결과로만 판별.MakePartDataFromDropResult: UNSDataSubsystem::Get(World)->GetAllPartRows()에서 랜덤 픽 → NSPartUtils::ResolveRarityFromTag로 DropResult.RarityTag 변환(실패 시 무효 PartData 반환) → PartData.CurrentRarity에 반영.ValueRange 롤링은 RandomStream.FRandRange(MinValue, MaxValue)를 써야 함. 중간에 RandRange(int32 전용 오버로드)로 잘못 타이핑되어 float가 int로 잘리는 버그가 있었으나 FRandRange로 수정함.DataTable(디자이너 작업, 코드 범위 밖)
RarityTag만 채움 (Part.Rarity.Common 등). PartDefinition 열 자체가 없어졌으므로 입력 불필요.