[TIL] 251201

๊น€์„ธํฌยท2025๋…„ 12์›” 1์ผ

โœ๏ธToday I Learned

๐Ÿ“… 2025-12-01

  • ํ”Œ๋ ˆ์ด์–ด ์ปจํŠธ๋กค๋Ÿฌ๋ฅผ ์ค‘์žฌ์ž๋กœ ํ™œ์šฉํ•œ ์ œ์ž‘ ์‹œ์Šคํ…œ ๊ตฌํ˜„

ํ”Œ๋ ˆ์ด์–ด ์ปจํŠธ๋กค๋Ÿฌ๋ฅผ ์ค‘์žฌ์ž๋กœ ํ™œ์šฉํ•œ ์ œ์ž‘ ์‹œ์Šคํ…œ ๊ตฌํ˜„

์˜ค๋„ˆ๊ฐ€ ์—†๋Š” ์ œ์ž‘๋Œ€ ์•กํ„ฐ์™€ ํ”Œ๋ ˆ์ด์–ด ๊ฐ„์˜ ๋„คํŠธ์›Œํฌ ํ†ต์‹ ์„ ๊ตฌํ˜„ํ•  ๋•Œ, ํ”Œ๋ ˆ์ด์–ด ์ปจํŠธ๋กค๋Ÿฌ๋ฅผ ์ค‘์žฌ์ž(Mediator) ํŒจํ„ด์œผ๋กœ ํ™œ์šฉํ–ˆ๋‹ค.

๋ฌธ์ œ ์ƒํ™ฉ: ์˜ค๋„ˆ ์—†๋Š” ์•กํ„ฐ์˜ ๋„คํŠธ์›Œํฌ ํ†ต์‹ 

์ œ์ž‘๋Œ€(ATSCraftingTable)๋Š” ์›”๋“œ์— ๋ฐฐ์น˜๋œ ์•กํ„ฐ๋กœ, ํŠน์ • ํ”Œ๋ ˆ์ด์–ด์˜ ์†Œ์œ ๊ฐ€ ์•„๋‹ˆ๋‹ค. ์–ธ๋ฆฌ์–ผ์˜ RPC๋Š” ๊ธฐ๋ณธ์ ์œผ๋กœ Owner๋ฅผ ํ†ตํ•ด ๋ผ์šฐํŒ…๋˜๊ธฐ ๋•Œ๋ฌธ์—, ์˜ค๋„ˆ๊ฐ€ ์—†๋Š” ์•กํ„ฐ์—์„œ ์ง์ ‘ ํด๋ผ์ด์–ธํŠธ RPC๋ฅผ ํ˜ธ์ถœํ•˜๋ฉด ์–ด๋А ํด๋ผ์ด์–ธํŠธ๋กœ ๋ณด๋‚ด์•ผ ํ• ์ง€ ์•Œ ์ˆ˜ ์—†๋‹ค.

ํ•ด๊ฒฐ์ฑ…: ํ”Œ๋ ˆ์ด์–ด ์ปจํŠธ๋กค๋Ÿฌ ์ค‘์žฌ์ž ํŒจํ„ด

ํด๋ผ์ด์–ธํŠธ โ†’ ํ”Œ๋ ˆ์ด์–ด ์ปจํŠธ๋กค๋Ÿฌ โ†’ ์ œ์ž‘๋Œ€

// PlayerController.h
UFUNCTION(Server, Reliable, WithValidation, BlueprintCallable, Category = "Crafting")
void ServerRequestCraft(ATSCraftingTable* CraftingTable, int32 RecipeID);
// PlayerController.cpp
void ATSPlayerController::ServerRequestCraft_Implementation(
    ATSCraftingTable* CraftingTable, int32 RecipeID)
{
    if (!CraftingTable) return;
    
    ATSCharacter* TSCharacter = Cast<ATSCharacter>(GetPawn());
    if (!TSCharacter) return;
    
    // ์ œ์ž‘๋Œ€์˜ ํ•จ์ˆ˜๋ฅผ ๋Œ€์‹  ํ˜ธ์ถœ
    CraftingTable->ServerRequestCraft(RecipeID, TSCharacter);
}

ํด๋ผ์ด์–ธํŠธ UI์—์„œ ์ œ์ž‘ ๋ฒ„ํŠผ์„ ๋ˆ„๋ฅด๋ฉด โ†’ ํ”Œ๋ ˆ์ด์–ด ์ปจํŠธ๋กค๋Ÿฌ์˜ ServerRequestCraft RPC ํ˜ธ์ถœ โ†’ ์„œ๋ฒ„์—์„œ ์ œ์ž‘๋Œ€์˜ ์‹ค์ œ ์ œ์ž‘ ๋กœ์ง ์‹คํ–‰

์ œ์ž‘๋Œ€ โ†’ ํ”Œ๋ ˆ์ด์–ด ์ปจํŠธ๋กค๋Ÿฌ โ†’ ํด๋ผ์ด์–ธํŠธ

// CraftingTable.cpp - ์ œ์ž‘ ์™„๋ฃŒ ํ›„
void ATSCraftingTable::StartCrafting(int32 RecipeID, ATSCharacter* InstigatorCharacter)
{
    // ... ์ œ์ž‘ ๋กœ์ง ...
    
    ATSPlayerController* PC = Cast<ATSPlayerController>(InstigatorCharacter->GetController());
    if (!PC) return;
    
    int32 SlotIndex = CraftingInventory->PlaceCraftResult(PC, RecipeData.ResultItemID, RemainingQuantity);
    
    // ํ”Œ๋ ˆ์ด์–ด ์ปจํŠธ๋กค๋Ÿฌ๋ฅผ ํ†ตํ•ด ํด๋ผ์ด์–ธํŠธ์— ์•Œ๋ฆผ
    PC->ClientNotifyCraftResult(SlotIndex);
}
// PlayerController.cpp
void ATSPlayerController::ClientNotifyCraftResult_Implementation(int32 SlotIndex)
{
    // ๋ธ๋ฆฌ๊ฒŒ์ดํŠธ ๋ธŒ๋กœ๋“œ์บ์ŠคํŠธ๋กœ UI ์—…๋ฐ์ดํŠธ
    OnCraftComplete.Broadcast(SlotIndex);
}

์ œ์ž‘ ์™„๋ฃŒ โ†’ ํ”Œ๋ ˆ์ด์–ด ์ปจํŠธ๋กค๋Ÿฌ์˜ ClientNotifyCraftResult RPC๋กœ ๊ฒฐ๊ณผ ์ „๋‹ฌ โ†’ OnCraftComplete ๋ธ๋ฆฌ๊ฒŒ์ดํŠธ ๋ธŒ๋กœ๋“œ์บ์ŠคํŠธ โ†’ UI๊ฐ€ ์Šฌ๋กฏ ์—…๋ฐ์ดํŠธ

์ค‘์žฌ์ž ํŒจํ„ด์˜ ์žฅ์ 

1. ๋„คํŠธ์›Œํฌ ๋ผ์šฐํŒ… ๋ฌธ์ œ ํ•ด๊ฒฐ

  • ํ”Œ๋ ˆ์ด์–ด ์ปจํŠธ๋กค๋Ÿฌ๋Š” ํ•ญ์ƒ ๋ช…ํ™•ํ•œ Owner ๊ด€๊ณ„๋ฅผ ๊ฐ€์ง
  • ์„œ๋ฒ„์™€ ํด๋ผ์ด์–ธํŠธ ๊ฐ„ ์•ˆ์ •์ ์ธ RPC ํ†ต์‹  ๋ณด์žฅ

2. ๊ถŒํ•œ ๊ฒ€์ฆ ์ค‘์•™ํ™”

bool ATSPlayerController::ServerRequestCraft_Validate(
    ATSCraftingTable* CraftingTable, int32 RecipeID)
{
    return CraftingTable && RecipeID > 0;
}

ํ”Œ๋ ˆ์ด์–ด ์ปจํŠธ๋กค๋Ÿฌ์—์„œ ํ•œ ๋ฒˆ๋งŒ ๊ฒ€์ฆํ•˜๋ฉด ๋จ

3. UI ํ†ต์‹  ๊ฐ„์†Œํ™”

  • UI ์œ„์ ฏ โ†’ ๋ธ๋ฆฌ๊ฒŒ์ดํŠธ ๋ฐ”์ธ๋”ฉ โ†’ ํ”Œ๋ ˆ์ด์–ด ์ปจํŠธ๋กค๋Ÿฌ๋งŒ ์•Œ๋ฉด ๋จ
  • ์ œ์ž‘๋Œ€ ์•กํ„ฐ๋ฅผ ์ง์ ‘ ์ฐธ์กฐํ•  ํ•„์š” ์—†์Œ

์ œ์ž‘๋Œ€ ์ข…๋ฃŒ ์ฒ˜๋ฆฌ

// PlayerController.h
UFUNCTION(Server, Reliable, BlueprintCallable, Category = "Crafting")
void ServerNotifyCraftingTableClosed(UTSCraftingTableInventory* CraftingInventory);

// PlayerController.cpp
void ATSPlayerController::ServerNotifyCraftingTableClosed_Implementation(
    UTSCraftingTableInventory* CraftingInventory)
{
    if (CraftingInventory)
    {
        CraftingInventory->OnPlayerClosedUI(this);
    }
}

ํด๋ผ์ด์–ธํŠธ๊ฐ€ ์ œ์ž‘๋Œ€ UI๋ฅผ ๋‹ซ์œผ๋ฉด โ†’ ์„œ๋ฒ„์— ์•Œ๋ฆผ โ†’ ์ œ์ž‘๋Œ€ ์ธ๋ฒคํ† ๋ฆฌ ์ •๋ฆฌ. ์ด๊ฒƒ๋„ ํ”Œ๋ ˆ์ด์–ด ์ปจํŠธ๋กค๋Ÿฌ๋ฅผ ํ†ตํ•ด ์ฒ˜๋ฆฌ.


๐Ÿ’ก ๋А๋‚€ ์  (What I Felt)

์ฒ˜์Œ์—” "์ œ์ž‘๋Œ€์—์„œ ์ง์ ‘ RPC ๋‚ ๋ฆฌ๋ฉด ๋˜์ง€ ์•Š๋‚˜?"๋ผ๊ณ  ์ƒ๊ฐํ–ˆ๋Š”๋ฐ, Owner๊ฐ€ ์—†๋Š” ์•กํ„ฐ๋Š” ์–ด๋А ํด๋ผ์ด์–ธํŠธํ•œํ…Œ ๋ณด๋‚ผ์ง€ ๋ชฐ๋ผ์„œ ์•ˆ ๋œ๋‹ค๋Š” ๊ฑธ ๊นจ๋‹ฌ์•˜๋‹ค. ํ”Œ๋ ˆ์ด์–ด ์ปจํŠธ๋กค๋Ÿฌ๊ฐ€ ์ค‘๊ฐ„์—์„œ ์ „๋‹ฌํ•˜๋Š” ์ค‘์žฌ์ž ์—ญํ• ์„ ํ•˜๋‹ˆ๊นŒ ๊น”๋”ํ•˜๊ฒŒ ํ•ด๊ฒฐ๋๋‹ค.

์ด ํŒจํ„ด์˜ ํ•ต์‹ฌ์€ "ํ†ต์‹  ๊ฒฝ๋กœ๋ฅผ ๋ช…ํ™•ํ•˜๊ฒŒ ๋งŒ๋“œ๋Š” ๊ฒƒ"์ธ ๊ฒƒ ๊ฐ™๋‹ค. UI โ†’ PlayerController โ†’ CraftingTable โ†’ PlayerController โ†’ UI ์ด๋Ÿฐ ์‹์œผ๋กœ ํ•ญ์ƒ ํ”Œ๋ ˆ์ด์–ด ์ปจํŠธ๋กค๋Ÿฌ๋ฅผ ๊ฑฐ์น˜๋‹ˆ๊นŒ ๋ˆ„๊ฐ€ ๋ˆ„๊ตฌํ•œํ…Œ ๋งํ•˜๋Š”์ง€ ํ—ท๊ฐˆ๋ฆด ์ผ์ด ์—†๋‹ค.

๊ทผ๋ฐ ์ด๋ ‡๊ฒŒ ํ•˜๋ฉด ํ”Œ๋ ˆ์ด์–ด ์ปจํŠธ๋กค๋Ÿฌ๊ฐ€ ๋„ˆ๋ฌด ๋งŽ์€ ๊ฑธ ์•Œ๊ฒŒ ๋˜๋Š” ๊ฑด ์•„๋‹Œ๊ฐ€ ์‹ถ๊ธฐ๋„ ํ•˜๋‹ค. ์ œ์ž‘๋Œ€๋„ ์•Œ๊ณ , ์ธ๋ฒคํ† ๋ฆฌ๋„ ์•Œ๊ณ , UI๋„ ์•Œ๊ณ ... ๋‚˜์ค‘์— ์‹œ์Šคํ…œ์ด ๋” ๋ณต์žกํ•ด์ง€๋ฉด ํ”Œ๋ ˆ์ด์–ด ์ปจํŠธ๋กค๋Ÿฌ๊ฐ€ God Object๊ฐ€ ๋  ์ˆ˜๋„ ์žˆ์„ ๊ฒƒ ๊ฐ™์€๋ฐ, ๊ทธ๋•Œ ๊ฐ€์„œ ๋ฆฌํŒฉํ† ๋ง ๊ณ ๋ฏผํ•ด๋ด์•ผ๊ฒ ๋‹ค.

0๊ฐœ์˜ ๋Œ“๊ธ€