<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>blog13869 님의 블로그</title>
    <link>https://blog13869.tistory.com/</link>
    <description>blog13869 님의 블로그 입니다.</description>
    <language>ko</language>
    <pubDate>Wed, 5 Aug 2026 16:39:07 +0900</pubDate>
    <generator>TISTORY</generator>
    <ttl>100</ttl>
    <managingEditor>blog13869</managingEditor>
    <item>
      <title>8/4 TIL</title>
      <link>https://blog13869.tistory.com/75</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;[ 강의 1 - 6 NetRole ]&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;lt; 매크로를 통한 NetRole 출력 &amp;gt;&lt;/p&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&amp;nbsp;새 C++ 클래스 &amp;gt; Pawn 부모 클래스 &amp;gt; &amp;ldquo;CXPawn&amp;rdquo;&lt;/li&gt;
&lt;li&gt;&amp;nbsp;Path:&amp;nbsp;`Player`&lt;/li&gt;
&lt;li&gt;`BP_GameModeBase&amp;nbsp;&amp;gt;&amp;nbsp;Details&amp;nbsp;&amp;gt;&amp;nbsp;Default&amp;nbsp;Pawn&amp;nbsp;Class`에&amp;nbsp;`CXPawn`&amp;nbsp;지정&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&amp;lt; CFM.h &amp;gt;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre class=&quot;reasonml&quot;&gt;&lt;code&gt;// 현재 할당돼있는 NetRole을 출력해서 확인하게 해주는 함수
static FString GetRoleString(const AActor* InActor)
{
    FString RoleString = TEXT(&quot;None&quot;);

    if (IsValid(InActor) == true)
    {
       FString LocalRoleString = UEnum::GetValueAsString(TEXT(&quot;Engine.ENetRole&quot;), InActor-&amp;gt;GetLocalRole());
       FString RemoteRoleString = UEnum::GetValueAsString(TEXT(&quot;Engine.ENetRole&quot;), InActor-&amp;gt;GetRemoteRole());
       
       RoleString = FString::Printf(TEXT(&quot;%s / %s&quot;), *LocalRoleString, *RemoteRoleString);
    }

    return RoleString;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&amp;lt; CFMPawn.h &amp;gt;&lt;/b&gt;&lt;/p&gt;
&lt;pre class=&quot;cpp&quot;&gt;&lt;code&gt;#pragma once

#include &quot;CoreMinimal.h&quot;
#include &quot;GameFramework/Pawn.h&quot;
#include &quot;CFMPawn.generated.h&quot;

UCLASS()
class CFM_API ACFMPawn : public APawn
{
    GENERATED_BODY()
    
protected:
    
    // 게임이 시작됐을 때 바로 호출
    virtual void BeginPlay() override;

    //빙의됐을 때 호출
    virtual void PossessedBy(AController* NewController) override;

};&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&amp;lt; CFMPawn.cpp &amp;gt;&lt;/b&gt;&lt;/p&gt;
&lt;pre class=&quot;reasonml&quot;&gt;&lt;code&gt;#include &quot;CFMPawn.h&quot;
#include &quot;CFM.h&quot;


void ACFMPawn::BeginPlay()
{
    Super::BeginPlay();
    
    // 시작되자마자 현재 적용돼있는 NetRole 출력
    FString NetRoleString = CFMFunctionLibrary::GetRoleString(this);
    FString CombinedString = FString::Printf(TEXT(&quot;CXPawn::BeginPlay() %s [%s]&quot;), *CFMFunctionLibrary::GetNetModeString(this), *NetRoleString);
    CFMFunctionLibrary::MyPrintString(this, CombinedString, 10.f);
}

void ACFMPawn::PossessedBy(AController* NewController)
{
    Super::PossessedBy(NewController);
    
    // 게임 시작 한 뒤 컨트롤러에 Pawn이 할당됐을 때 NetRole 출력
    FString NetRoleString = CFMFunctionLibrary::GetRoleString(this);
    FString CombinedString = FString::Printf(TEXT(&quot;CXPawn::PossessedBy() %s [%s]&quot;), *CFMFunctionLibrary::GetNetModeString(this), *NetRoleString);
    CFMFunctionLibrary::MyPrintString(this, CombinedString, 10.f);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;950&quot; data-origin-height=&quot;549&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/A7gzU/dJMcacxipx8/cAmn4SkmW0N6Dlj0Kiweak/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/A7gzU/dJMcacxipx8/cAmn4SkmW0N6Dlj0Kiweak/img.png&quot; data-alt=&quot;BP_GameModeBase &amp;amp;gt; Details &amp;amp;gt; Default Pawn Class에 CXPawn을 지정&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/A7gzU/dJMcacxipx8/cAmn4SkmW0N6Dlj0Kiweak/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FA7gzU%2FdJMcacxipx8%2FcAmn4SkmW0N6Dlj0Kiweak%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;950&quot; height=&quot;549&quot; data-origin-width=&quot;950&quot; data-origin-height=&quot;549&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;BP_GameModeBase &amp;gt; Details &amp;gt; Default Pawn Class에 CXPawn을 지정&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;lt; 실행 시 나오는 로그 확인 &amp;gt;&lt;/p&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1292&quot; data-origin-height=&quot;52&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cIfreX/dJMcahZClTl/v8BvUn25PsIVXrnQVKK3Wk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cIfreX/dJMcahZClTl/v8BvUn25PsIVXrnQVKK3Wk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cIfreX/dJMcahZClTl/v8BvUn25PsIVXrnQVKK3Wk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcIfreX%2FdJMcahZClTl%2Fv8BvUn25PsIVXrnQVKK3Wk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1292&quot; height=&quot;52&quot; data-origin-width=&quot;1292&quot; data-origin-height=&quot;52&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;

&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;시작 시 Simulated Proxy : 피동기화 상태 ( SetOwner 가 아직 X )&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;background-color: #fafafa; color: #333333; text-align: start;&quot;&gt;PlayerController할당 : Autonomous Proxy : 서버로부터 데이터를 수신 받아서 동기화도 되면서, 서버로 송신도 가능&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;lt; NetRole을 활용하여 정의된 중요 함수들 &amp;gt;&lt;/p&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&amp;lt; AActor::HasAuthority() &amp;gt;&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;입력&amp;nbsp;관련&amp;nbsp;로직이나&amp;nbsp;UI는&amp;nbsp;Autonomous&amp;nbsp;Proxy에서&amp;nbsp;수행&lt;/li&gt;
&lt;li&gt;AController::IsLocalController()&amp;nbsp;와&amp;nbsp;APawn::IsLocallyControlled()&amp;nbsp;함수가&amp;nbsp;제공&lt;/li&gt;
&lt;/ul&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;681&quot; data-origin-height=&quot;210&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cZQ6HZ/dJMcajiTEiw/r6HAhUa5tkum0pn9kkW1L0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cZQ6HZ/dJMcajiTEiw/r6HAhUa5tkum0pn9kkW1L0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cZQ6HZ/dJMcajiTEiw/r6HAhUa5tkum0pn9kkW1L0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcZQ6HZ%2FdJMcajiTEiw%2Fr6HAhUa5tkum0pn9kkW1L0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;681&quot; height=&quot;210&quot; data-origin-width=&quot;681&quot; data-origin-height=&quot;210&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;

&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;763&quot; data-origin-height=&quot;237&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bePw9M/dJMcadbNkQ8/EiS7PxNJXoCjE7LjJlK5r0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bePw9M/dJMcadbNkQ8/EiS7PxNJXoCjE7LjJlK5r0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bePw9M/dJMcadbNkQ8/EiS7PxNJXoCjE7LjJlK5r0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbePw9M%2FdJMcadbNkQ8%2FEiS7PxNJXoCjE7LjJlK5r0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;684&quot; height=&quot;212&quot; data-origin-width=&quot;763&quot; data-origin-height=&quot;237&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;

&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;985&quot; data-origin-height=&quot;806&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/2TYxC/dJMcafU8eVv/f7GmBCE4JD8FZE4MsWJhR1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/2TYxC/dJMcafU8eVv/f7GmBCE4JD8FZE4MsWJhR1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/2TYxC/dJMcafU8eVv/f7GmBCE4JD8FZE4MsWJhR1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F2TYxC%2FdJMcafU8eVv%2Ff7GmBCE4JD8FZE4MsWJhR1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;685&quot; height=&quot;561&quot; data-origin-width=&quot;985&quot; data-origin-height=&quot;806&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;lt; 각 액터에 따른 특징 &amp;gt;&lt;/p&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;GameMode 는 HasAuthority() 함수를 호출할 필요 X&lt;/li&gt;
&lt;li&gt;Pawn 은 Authority, Autonomous Proxy, Simulated Proxy가 혼재&lt;/li&gt;
&lt;li&gt;UI&amp;nbsp;관련&amp;nbsp;로직은&amp;nbsp;클라이언트에서만&amp;nbsp;수행&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;lt; 로컬 롤과 리모트 롤로 분류한 이유 &amp;gt;&lt;/p&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;만약&amp;nbsp;로컬&amp;nbsp;롤만&amp;nbsp;있었다면&amp;nbsp;분간하기가&amp;nbsp;어려웠을&amp;nbsp;케이스들이&amp;nbsp;존재&lt;br /&gt;서버에 접속한 플레이어의 캐릭터 A와 서버에서 스폰 된 캐릭터 B가 있다고 가정한다면&lt;br /&gt;A와 B의 로컬 롤은 *&lt;b&gt;Authority&lt;/b&gt;*&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;로컬 롤만으로는 서버에 접속한 플레이어의 캐릭터인지, 서버에서 스폰된건지 구분이 불가능&lt;br /&gt;여기서&amp;nbsp;만약&amp;nbsp;리모트&amp;nbsp;롤까지&amp;nbsp;알게된다면,&amp;nbsp;서버에&amp;nbsp;접속한&amp;nbsp;플레이어의&amp;nbsp;캐릭터는&amp;nbsp;**&lt;b&gt;Autonomus&amp;nbsp;Proxy&lt;/b&gt;**이고, &lt;br /&gt;서버에서 스폰된 캐릭터는 `None`이므로 구분이 가능&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;구분이 가능해야 서버에서 호출되고 오너 클라이언트에서 실행될 RPC를 구현이 가능&lt;br /&gt;멀티플레이에서 각 액터들을 좀 더 정확하게 분류하게 위해 둘로 분류&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;[ 강의 2-1. Remote Procedure Call 기초 ]&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;lt; RPC(Remote Procedure Call) &amp;gt;&lt;/p&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;호출하는&amp;nbsp;PC와&amp;nbsp;실행하는&amp;nbsp;PC가&amp;nbsp;달라도&amp;nbsp;되게끔&amp;nbsp;해주는&amp;nbsp;통신&amp;nbsp;기법&lt;/li&gt;
&lt;li&gt;게임에 큰 영향을 끼치지 않는 일시적인 효과들에 사용하는 편&lt;/li&gt;
&lt;li&gt;주로 코스메틱(사운드, 파티클 등)에 사용&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1528&quot; data-origin-height=&quot;970&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/kEIMr/dJMcacKUkm9/POojqGQAKH6Z9lUTQ94y40/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/kEIMr/dJMcacKUkm9/POojqGQAKH6Z9lUTQ94y40/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/kEIMr/dJMcacKUkm9/POojqGQAKH6Z9lUTQ94y40/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FkEIMr%2FdJMcacKUkm9%2FPOojqGQAKH6Z9lUTQ94y40%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1528&quot; height=&quot;970&quot; data-origin-width=&quot;1528&quot; data-origin-height=&quot;970&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;lt; Call과 Invoke &amp;gt;&lt;/p&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&amp;lt; Call &amp;gt;&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;정적인 의미&lt;/li&gt;
&lt;li&gt;컴파일 타임에 어떤 함수인지, 호출하는 곳과 실행하는 곳이 결정&lt;/li&gt;
&lt;li&gt;지금까지 정의했던 일반적인 전역 함수와 멤버 함수가 이에 해당&lt;/li&gt;
&lt;li&gt;&lt;b&gt;직접적&lt;/b&gt;으로&amp;nbsp;함수를&amp;nbsp;호출하고&amp;nbsp;실행&amp;nbsp;(&lt;b&gt;Direct&lt;/b&gt;)&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&amp;lt; Invoke &amp;gt;&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;동적인&amp;nbsp;의미&lt;/li&gt;
&lt;li&gt;런타임에 어떤 함수인지, 호출하는 곳과 실행하는 곳이 결정&lt;/li&gt;
&lt;li&gt;함수 포인터, 동적 바인딩, RPC 같은 개념들이 이에 해당&lt;/li&gt;
&lt;li&gt;&lt;b&gt;간접적&lt;/b&gt;으로 함수를 호출하고 실행 (&lt;b&gt;Indirect&lt;/b&gt;)&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;RPC도 마찬가지로 &quot;&lt;span data-token-index=&quot;1&quot;&gt;RPC를 Invoke&lt;/span&gt;&quot;라고 표현&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;lt; Actor Ownership &amp;gt;&lt;/p&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;트워크&amp;nbsp;멀티플레이가&amp;nbsp;적용되려면,&amp;nbsp;액터는&amp;nbsp;서버에서&amp;nbsp;스폰되고&amp;nbsp;`bReplicates`가&amp;nbsp;`true`여야&amp;nbsp;가능 &lt;br /&gt;서버에서 스폰된 후 `SetOwner(PlayerController)` 함수를 호출해야 Client-Owned Actor가 할당&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;Client-Owned&amp;nbsp;Actor&amp;nbsp;&lt;/b&gt;:&amp;nbsp;PlayerController&amp;nbsp;==&amp;nbsp;Local&amp;nbsp;PlayerController&amp;nbsp; &lt;br /&gt;&amp;nbsp; ex) 내 플레이어 캐릭터&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Owned&amp;nbsp;by&amp;nbsp;different&amp;nbsp;client&lt;/b&gt;&amp;nbsp;:&amp;nbsp;PlayerController&amp;nbsp;!==&amp;nbsp;Local&amp;nbsp;PlayerController &lt;br /&gt;&amp;nbsp;&amp;nbsp;ex)&amp;nbsp;내&amp;nbsp;화면에&amp;nbsp;보이는&amp;nbsp;친구의&amp;nbsp;플레이어&amp;nbsp;캐릭터&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Server-Owned&amp;nbsp;Actor&lt;/b&gt;&amp;nbsp;:서버에서&amp;nbsp;스폰되었지만&amp;nbsp;SetOwner()&amp;nbsp;함수&amp;nbsp;호출&amp;nbsp;X &lt;br /&gt;&amp;nbsp;&amp;nbsp;ex)&amp;nbsp;보물상자&amp;nbsp;액터&lt;/li&gt;
&lt;li&gt;Unowned Actor == Server-Owned Actor&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;lt; NetMulticast, Server, Client 키워드 &amp;gt;&lt;/p&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;`UFUNCTION()`&amp;nbsp;매크로와&amp;nbsp;함께&amp;nbsp;사용되는&amp;nbsp;키워드 &lt;/li&gt;
&lt;li&gt;키워드&amp;nbsp;:&amp;nbsp;해당&amp;nbsp;원격&amp;nbsp;PC에서&amp;nbsp;RPC를&amp;nbsp;실행시켜&amp;nbsp;달라는&amp;nbsp;요청&amp;nbsp;&lt;/li&gt;
&lt;li&gt;실제로&amp;nbsp;실행될지는&amp;nbsp;표를&amp;nbsp;통해&amp;nbsp;판단&lt;/li&gt;
&lt;/ul&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1027&quot; data-origin-height=&quot;688&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dp6MgP/dJMcafHCpAS/CY3OEPpvuKhgKQOCJG8JPK/img.webp&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dp6MgP/dJMcafHCpAS/CY3OEPpvuKhgKQOCJG8JPK/img.webp&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dp6MgP/dJMcafHCpAS/CY3OEPpvuKhgKQOCJG8JPK/img.webp&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fdp6MgP%2FdJMcafHCpAS%2FCY3OEPpvuKhgKQOCJG8JPK%2Fimg.webp&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1027&quot; height=&quot;688&quot; data-origin-width=&quot;1027&quot; data-origin-height=&quot;688&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;

&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;NetMulticast&amp;nbsp;:&amp;nbsp;서버를&amp;nbsp;포함한&amp;nbsp;모든&amp;nbsp;클라이언트에서&amp;nbsp;해당&amp;nbsp;RPC를&amp;nbsp;실행을&amp;nbsp;요청&lt;/li&gt;
&lt;li&gt;Server&amp;nbsp;:&amp;nbsp;서버에서&amp;nbsp;해당&amp;nbsp;RPC를&amp;nbsp;실행을&amp;nbsp;요청&lt;/li&gt;
&lt;li&gt;Client&amp;nbsp;:&amp;nbsp;클라이언트에서&amp;nbsp;해당&amp;nbsp;RPC를&amp;nbsp;실행을&amp;nbsp;요청&lt;/li&gt;
&lt;/ul&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2161&quot; data-origin-height=&quot;1206&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/0O4gN/dJMcaiEiug1/9qXjyVNVNeqqNT5rtsIQ90/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/0O4gN/dJMcaiEiug1/9qXjyVNVNeqqNT5rtsIQ90/img.png&quot; data-alt=&quot;NetMulticast RPC&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/0O4gN/dJMcaiEiug1/9qXjyVNVNeqqNT5rtsIQ90/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F0O4gN%2FdJMcaiEiug1%2F9qXjyVNVNeqqNT5rtsIQ90%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2161&quot; height=&quot;1206&quot; data-origin-width=&quot;2161&quot; data-origin-height=&quot;1206&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;NetMulticast RPC&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2160&quot; data-origin-height=&quot;1203&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ce4RdM/dJMcadv5eCj/l3KMsoDo9V6mmt5ytsjVj1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ce4RdM/dJMcadv5eCj/l3KMsoDo9V6mmt5ytsjVj1/img.png&quot; data-alt=&quot;Server RPC&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ce4RdM/dJMcadv5eCj/l3KMsoDo9V6mmt5ytsjVj1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fce4RdM%2FdJMcadv5eCj%2Fl3KMsoDo9V6mmt5ytsjVj1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2160&quot; height=&quot;1203&quot; data-origin-width=&quot;2160&quot; data-origin-height=&quot;1203&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Server RPC&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2164&quot; data-origin-height=&quot;1212&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/oXBol/dJMcaidil4j/agKKWfsWBhwjnsuPFs30z1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/oXBol/dJMcaidil4j/agKKWfsWBhwjnsuPFs30z1/img.png&quot; data-alt=&quot;Client RPC&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/oXBol/dJMcaidil4j/agKKWfsWBhwjnsuPFs30z1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FoXBol%2FdJMcaidil4j%2FagKKWfsWBhwjnsuPFs30z1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2164&quot; height=&quot;1212&quot; data-origin-width=&quot;2164&quot; data-origin-height=&quot;1212&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Client RPC&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;lt;자주 쓰이는 케이스 &amp;gt;&lt;/p&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&amp;lt; Server 키워드 &amp;gt;&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span data-token-index=&quot;0&quot;&gt;RPC가 클라이언트에서 호출되고 서버에서 실행되어야 하는 경우&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;ClientConnection이&amp;nbsp;소유하고&amp;nbsp;있는&amp;nbsp;액터(Client-Owned)에서&amp;nbsp;RPC가&amp;nbsp;호출&lt;/li&gt;
&lt;li&gt;_Validate()&amp;nbsp;함수에서&amp;nbsp;RPC를&amp;nbsp;실행할지&amp;nbsp;말지&amp;nbsp;결정&lt;/li&gt;
&lt;/ul&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2127&quot; data-origin-height=&quot;740&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/1txKr/dJMcacjIfqn/KyW0NNlEo9ImGzQz6tz6GK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/1txKr/dJMcacjIfqn/KyW0NNlEo9ImGzQz6tz6GK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/1txKr/dJMcacjIfqn/KyW0NNlEo9ImGzQz6tz6GK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F1txKr%2FdJMcacjIfqn%2FKyW0NNlEo9ImGzQz6tz6GK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2127&quot; height=&quot;740&quot; data-origin-width=&quot;2127&quot; data-origin-height=&quot;740&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;

&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&amp;lt; NetMulticast 키워드 &amp;gt;&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span data-token-index=&quot;0&quot;&gt;RPC가 서버와 모든 클라이언트에서 실행되어야 하는 경우&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;서버에서 호출&lt;/li&gt;
&lt;li&gt;부하가&amp;nbsp;심하므로,&amp;nbsp;빈번하게&amp;nbsp;호출되게끔&amp;nbsp;코드를&amp;nbsp;작성하는&amp;nbsp;건&amp;nbsp;권장&amp;nbsp;X &lt;br /&gt;&amp;nbsp;&amp;nbsp;ex) Tick() 함수 내에서 NetMulticast RPC 호출&lt;/li&gt;
&lt;/ul&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2122&quot; data-origin-height=&quot;598&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/vMYjW/dJMcabZwGoG/uStKOi9kKW4Py7F3PhDyjK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/vMYjW/dJMcabZwGoG/uStKOi9kKW4Py7F3PhDyjK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/vMYjW/dJMcabZwGoG/uStKOi9kKW4Py7F3PhDyjK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FvMYjW%2FdJMcabZwGoG%2FuStKOi9kKW4Py7F3PhDyjK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2122&quot; height=&quot;598&quot; data-origin-width=&quot;2122&quot; data-origin-height=&quot;598&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;

&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&amp;lt; Client&amp;nbsp;키워드 &amp;gt;&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span data-token-index=&quot;0&quot;&gt;RPC가 서버에서 호출되고 클라이언트에서 실행되어야 하는 경우&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;서버에서 호출&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;lt; &lt;span style=&quot;color: #000000;&quot; data-token-index=&quot;0&quot;&gt;WithValidation&lt;/span&gt; &amp;gt;&lt;/p&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;UFUNCTION()&amp;nbsp;매크로와&amp;nbsp;함께&amp;nbsp;사용되는&amp;nbsp;키워드&lt;/li&gt;
&lt;li&gt;서버에서&amp;nbsp;실행되는&amp;nbsp;RPC의&amp;nbsp;경우에&amp;nbsp;해당&amp;nbsp;키워드를&amp;nbsp;작성하는&amp;nbsp;것이&amp;nbsp;권장&lt;/li&gt;
&lt;li&gt;이 키워드가 붙은 RPC를 구현 할때는 _Implementation() 함수와 _Validate() 함수로 분류 &lt;/li&gt;
&lt;li&gt;_Validate() 함수는 해당 RPC가 서버에서 실제로 실행을 결정&lt;/li&gt;
&lt;li&gt;서버&amp;nbsp;실행&amp;nbsp;로직은&amp;nbsp;무조건적으로&amp;nbsp;신뢰되기&amp;nbsp;때문에,&amp;nbsp;위변조를&amp;nbsp;막기&amp;nbsp;위한&amp;nbsp;방어막&amp;nbsp;역할&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot; data-token-index=&quot;0&quot;&gt;&amp;lt; &lt;span style=&quot;color: #000000;&quot; data-token-index=&quot;0&quot;&gt;Unreliable VS Reliable&lt;/span&gt; &amp;gt;&lt;/span&gt;&lt;/p&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;UFUNCTION()&amp;nbsp;매크로와&amp;nbsp;함께&amp;nbsp;사용되는&amp;nbsp;키워드 &lt;/li&gt;
&lt;li&gt;RPC는 기본적으로 **Unreliable** 키워드를 기준으로 동작 &lt;br /&gt;&amp;nbsp;아무것도&amp;nbsp;안쓰면&amp;nbsp;Unreliable &lt;/li&gt;
&lt;li&gt;Unreliable은 원격 PC에서 무조건 실행되리라는 보장 X &lt;/li&gt;
&lt;li&gt;반면, 원격 PC에서 무조건 실행되어야 하는 로직이라면 Reliable 키워드를 작성 &lt;/li&gt;
&lt;li&gt;Unreliable 의 활용 예시 : 코스메틱(이펙트, 사운드 등)처럼 게임에 큰 영향이 없는 로직&lt;/li&gt;
&lt;li&gt;Reliable 의 활용 예시 : 충돌, 데미지, 스폰처럼 게임에 큰 영향을 끼치는 로직&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;[ 강의 2 - 2 : 멀티플레이 채팅 구현 ]&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;lt; 내가 작성한 채팅 메세지가 다른 PC의 화면에도 나오게 하는 방법 &amp;gt;&lt;/p&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&amp;lt; 데디케이티드 서버 구조 &amp;gt;&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;클라이언트와 클라이언트 간의 통신은 불가능 &lt;/li&gt;
&lt;li&gt;서버와 클라이언트 간의 통신만 가능 &lt;/li&gt;
&lt;li&gt;내가 작성한 채팅 메시지는 일단 서버로 전송 &lt;/li&gt;
&lt;li&gt;서버는 전송 받은 메세지를 다시 클라이언트들에게 전송&lt;/li&gt;
&lt;/ul&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2159&quot; data-origin-height=&quot;1205&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cB2sFp/dJMb99N1wIb/MJ5g0WrzGukUv315edXcw0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cB2sFp/dJMb99N1wIb/MJ5g0WrzGukUv315edXcw0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cB2sFp/dJMb99N1wIb/MJ5g0WrzGukUv315edXcw0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcB2sFp%2FdJMb99N1wIb%2FMJ5g0WrzGukUv315edXcw0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2159&quot; height=&quot;1205&quot; data-origin-width=&quot;2159&quot; data-origin-height=&quot;1205&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;lt; 멀티플레이 채팅 구현 &amp;gt;&lt;/p&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&amp;lt; CFMPlayerController.h &amp;gt;&lt;/b&gt;&lt;/p&gt;
&lt;pre class=&quot;arduino&quot;&gt;&lt;code&gt;//서버에서 출력한 채팅을 클라이언트에서 받는 함수 (Client RPC) , 채팅메세지가 날아가지 않게 끔 Reliable 키워드 추가
UFUNCTION(Client, Reliable)
void ClientRPCPrintChatMessageString(const FString&amp;amp; InChatMessageString);

// 채팅을 서버로 보내기 위한 함수 (Server RPC), 채팅메세지가 날아가지 않게 끔 Reliable 키워드 추가
UFUNCTION(Server, Reliable)
void ServerRPCPrintChatMessageString(const FString&amp;amp; InChatMessageString);
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&amp;lt; CFMPlayerController.cpp &amp;gt;&lt;/b&gt;&lt;/p&gt;
&lt;pre class=&quot;cpp&quot;&gt;&lt;code&gt;#include &quot;EngineUtils.h&quot; // TActorIterator 사용 용도&lt;/code&gt;&lt;/pre&gt;
&lt;pre class=&quot;reasonml&quot;&gt;&lt;code&gt;void ACFMPlayerController::SetChatMessageString(const FString&amp;amp; InChatMessageString)
{
    ChatMessageString = InChatMessageString;
    //PrintChatMessageString(ChatMessageString);
    // 기존 PrintChatMessageString 주석 처리 후 Controller가 할당 돼있다면 Server에 채팅 보내는 함수로 변경
    if (IsLocalController() == true)
    {
       ServerRPCPrintChatMessageString(InChatMessageString);     
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre class=&quot;reasonml&quot;&gt;&lt;code&gt;// RPC를 만들 때  Implementation 을 붙히는 게 국룰 문법
void ACFMPlayerController::ServerRPCPrintChatMessageString_Implementation(const FString&amp;amp; InChatMessageString)
{
    // 서버에서 플레이어 컨트롤러를 확인
    for (TActorIterator&amp;lt;ACFMPlayerController&amp;gt; It(GetWorld()); It; ++It)
    { 
       // 확인한 플레이어 컨트롤러들이 유효하면 실행
       ACFMPlayerController* CXPlayerController = *It;
       if (IsValid(CXPlayerController) == true)
       {
          // 해당 플레이어컨트롤러에 ClientRPCPrintChatMessageString함수를 통해 해당 PC에서 채팅 출력
          CXPlayerController-&amp;gt;ClientRPCPrintChatMessageString(InChatMessageString);
       }
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre class=&quot;cpp&quot;&gt;&lt;code&gt;void ACFMPlayerController::ClientRPCPrintChatMessageString_Implementation(const FString&amp;amp; InChatMessageString)
{
    // 출력 하는 함수만 추가 (ServerRPC 함수 쪽에서 호출용)
    PrintChatMessageString(InChatMessageString); 
}
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;lt; &lt;span data-token-index=&quot;0&quot;&gt;Client RPC 대신에 NetMulticast RPC를 사용한다면?&lt;/span&gt;&amp;nbsp;&amp;gt;&lt;/p&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;작동&amp;nbsp;X &lt;br /&gt;&lt;br /&gt;&lt;/li&gt;
&lt;li&gt;PlayerController는&amp;nbsp;내&amp;nbsp;PC와&amp;nbsp;서버&amp;nbsp;PC에만&amp;nbsp;존재하기&amp;nbsp;때문에&amp;nbsp;NetMulticast&amp;nbsp;RPC로&amp;nbsp;구현하면&amp;nbsp;내&amp;nbsp;PC와&amp;nbsp;서버&amp;nbsp;PC에만&amp;nbsp;채팅이&amp;nbsp;출력&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;lt; &lt;span data-token-index=&quot;0&quot;&gt;NetMulticast RPC를 통한 서버 접속 알리기&lt;/span&gt; &amp;gt;&lt;/p&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&amp;nbsp;새 C++ 클래스 &amp;gt; GameStateBase 부모 클래스 &amp;gt; &amp;ldquo;CXGameStateBase&amp;rdquo;&lt;br /&gt;&lt;br /&gt;&lt;/li&gt;
&lt;li&gt;&amp;nbsp;Path:&amp;nbsp;`Game` &lt;br /&gt;&amp;nbsp; &lt;/li&gt;
&lt;li&gt;Content Browser &amp;gt; Content &amp;gt; ChatX &amp;gt; Blueprint &amp;gt; Game &lt;br /&gt;새 Blueprint 클래스 &amp;gt; CXGameStateBase 부모 클래스 &amp;gt; &amp;ldquo;BP_GameStateBase&amp;rdquo;&lt;br /&gt;&lt;br /&gt;&lt;/li&gt;
&lt;li&gt;BP_GameModeBase &amp;gt; Details &amp;gt; Game State Class에 BP_GameStateBase 지정&lt;/li&gt;
&lt;/ul&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1351&quot; data-origin-height=&quot;39&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/kPqFT/dJMcab6ehBj/u2uZ4M83vNIrF43Aw2fgL1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/kPqFT/dJMcab6ehBj/u2uZ4M83vNIrF43Aw2fgL1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/kPqFT/dJMcab6ehBj/u2uZ4M83vNIrF43Aw2fgL1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FkPqFT%2FdJMcab6ehBj%2Fu2uZ4M83vNIrF43Aw2fgL1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1351&quot; height=&quot;39&quot; data-origin-width=&quot;1351&quot; data-origin-height=&quot;39&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;

&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;lt; CFMGameStateBase.h &amp;gt;&lt;/p&gt;
&lt;pre class=&quot;cpp&quot;&gt;&lt;code&gt;public:
    
    // MulticastRPC 를 사용해서 누군가 접속(로그인) 했을 때 알려주는(TEXT호출) 함수
    UFUNCTION(NetMulticast, Reliable)
    void MulticastRPCBroadcastLoginMessage(const FString&amp;amp; InNameString = FString(TEXT(&quot;XXXXXXX&quot;)));
    &lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #fafafa; color: #333333; text-align: start;&quot;&gt;&amp;lt; CFMGameStateBase.cpp &amp;gt;&lt;/span&gt;&lt;/p&gt;
&lt;pre class=&quot;reasonml&quot;&gt;&lt;code&gt;#include &quot;Game/CFMGameStateBase.h&quot;
#include &quot;Kismet/GameplayStatics.h&quot;
#include &quot;Player/CFMPlayerController.h&quot;



void ACFMGameStateBase::MulticastRPCBroadcastLoginMessage_Implementation(const FString&amp;amp; InNameString)
{
    // HasAuthority() == false = 서버가 아니라면 = 클라이언트라면
    if (HasAuthority() == false)
    {
       //  0번 컨트롤러 (LocalPlayerController)를 가져오고
       APlayerController* PC = UGameplayStatics::GetPlayerController(GetWorld(), 0);
       if (IsValid(PC) == true)
       {
          // 로컬플레이어가 유요 하다면 NotificationString 으로 게임에 접속했다는 문구 출력
          ACFMPlayerController* CFMPC = Cast&amp;lt;ACFMPlayerController&amp;gt;(PC);
          if (IsValid(CFMPC) == true)
          {
             FString NotificationString = InNameString + TEXT(&quot; has joined the game.&quot;);
             CFMPC-&amp;gt;PrintChatMessageString(NotificationString);
          }
       }
    &lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #fafafa; color: #333333; text-align: start;&quot;&gt;&amp;lt; CFMGameStateBase.h &amp;gt;&lt;/span&gt;&lt;/p&gt;
&lt;pre class=&quot;cs&quot;&gt;&lt;code&gt;public:

// 다른 플레이어 (NewPlayer)가 로그인 할 때 실행되는 함수
virtual void OnPostLogin(AController* NewPlayer) override;
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #fafafa; color: #333333; text-align: start;&quot;&gt;&amp;lt; CFMGameStateBase.h &amp;gt;&lt;/span&gt;&lt;/p&gt;
&lt;pre class=&quot;dts&quot;&gt;&lt;code&gt;#include &quot;Game/CFMGameModeBase.h&quot;
#include &quot;Game/CFMGameStateBase.h&quot;



void ACFMGameModeBase::OnPostLogin(AController* NewPlayer)
{
    // 누군가 접속 할 때 마다
    Super::OnPostLogin(NewPlayer);

    // 접속한 NewPlayer의 GameStateBase를 가져오고 
    ACFMGameStateBase* CFMGameStateBase =  GetGameState&amp;lt;ACFMGameStateBase&amp;gt;();
    // GameStateBase 가 유효하다면 MulticastRPC 로그인 메세지 출력 함수 호출
    if (IsValid(CFMGameStateBase) == true)
    {
       CFMGameStateBase-&amp;gt;MulticastRPCBroadcastLoginMessage(TEXT(&quot;XXXXXXX&quot;));
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <author>blog13869</author>
      <guid isPermaLink="true">https://blog13869.tistory.com/75</guid>
      <comments>https://blog13869.tistory.com/75#entry75comment</comments>
      <pubDate>Tue, 4 Aug 2026 20:24:52 +0900</pubDate>
    </item>
    <item>
      <title>7/31 TIL</title>
      <link>https://blog13869.tistory.com/74</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #222222; text-align: start;&quot;&gt;[ CH4_Unreal Engine 멀티플레이어 게임 개발 ]&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;[강의 1-3.&amp;nbsp;서버의&amp;nbsp;종류와&amp;nbsp;데디케이티드&amp;nbsp;서버]&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;lt; 서버의 종류와 구조 &amp;gt;&lt;/p&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;P2P Server(Peer to Peer Server) &lt;br /&gt;&amp;nbsp;ㄴ 각각의 컴퓨터가 클라이언트이자 서버인 구조&lt;/p&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2118&quot; data-origin-height=&quot;1185&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bmlawp/dJMcaafaKpb/rB1GWq21WNrY3mG0Pl85T0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bmlawp/dJMcaafaKpb/rB1GWq21WNrY3mG0Pl85T0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bmlawp/dJMcaafaKpb/rB1GWq21WNrY3mG0Pl85T0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbmlawp%2FdJMcaafaKpb%2FrB1GWq21WNrY3mG0Pl85T0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;677&quot; height=&quot;379&quot; data-origin-width=&quot;2118&quot; data-origin-height=&quot;1185&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;

&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Listen&amp;nbsp;Server &lt;br /&gt;&amp;nbsp; ㄴ 클라이언트이자 서버인 방장(Host)이 있고, 나머지 참가자(Guest)는 모두 클라이언트 역할만 맡는 형태&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;P2P의 일종&lt;/p&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2124&quot; data-origin-height=&quot;1191&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dIbLmG/dJMcagzDdmc/4fbpx0fuXY4bDQe9BsWSa0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dIbLmG/dJMcagzDdmc/4fbpx0fuXY4bDQe9BsWSa0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dIbLmG/dJMcagzDdmc/4fbpx0fuXY4bDQe9BsWSa0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdIbLmG%2FdJMcagzDdmc%2F4fbpx0fuXY4bDQe9BsWSa0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;669&quot; height=&quot;375&quot; data-origin-width=&quot;2124&quot; data-origin-height=&quot;1191&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;

&lt;p data-ke-size=&quot;size16&quot;&gt;Dedicated Server &lt;br /&gt;&amp;nbsp; ㄴ 서버를 담당하는 컴퓨터가 별도로 존재&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; 서버 - 클라이언트 구조&lt;/p&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2128&quot; data-origin-height=&quot;1186&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b9YgA6/dJMcag0D7lg/yXtnGUblGwlehQL0HUvk70/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b9YgA6/dJMcag0D7lg/yXtnGUblGwlehQL0HUvk70/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b9YgA6/dJMcag0D7lg/yXtnGUblGwlehQL0HUvk70/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb9YgA6%2FdJMcag0D7lg%2FyXtnGUblGwlehQL0HUvk70%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2128&quot; height=&quot;1186&quot; data-origin-width=&quot;2128&quot; data-origin-height=&quot;1186&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;lt; &lt;span data-token-index=&quot;0&quot;&gt;Dedicated Server의 실행 흐름도&lt;/span&gt; &amp;gt;&lt;/p&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;1. PIE를 했거나, Server.exe를 실행하는 식으로 서버 프로세스 실행.&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;실행할 때 Open {Level이름}?Listen 명령어가 인자로 전달&lt;/li&gt;
&lt;li&gt;해당 Level을 열고 Socket이 생성되며 다른 PC가 접속 가능하게끔 설정&lt;/li&gt;
&lt;li&gt;Listen 명령어가 없다면 싱글플레이&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;2. Level의 정보를 토대로 GameMode와 GameState 액터를 생성&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Level의 WorldSettings 속성이 존재하고 WorldSettings에는 GameMode와 GameState 정보가 할당&lt;/li&gt;
&lt;li&gt;이를 통해 GameMode와 GameState 액터를 생성&lt;/li&gt;
&lt;li&gt;중요한 것은 GameMode 액터는 전체 컴퓨터에서 딱 한 곳(Server)에만 존재&lt;/li&gt;
&lt;li&gt;그래서 GameMode와 Server를 동일시 설정해도 무방&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;3. 클라이언트에서 접속 요청&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;클라이언트는 서버의 IP주소와 포트번호로 접속 시도&lt;/li&gt;
&lt;li&gt;서버는 접속 시도하는 클라이언트에게 Level 정보를 전달&lt;/li&gt;
&lt;li&gt;클라이언트도 해당 Level을 열고, Level을 여는데 성공했다고 데디 서버에게 알림&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;4. 접속 성공 시 게임의 정보를 클라이언트에 복제/대입&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Level을 여는데 성공한 클라이언트 전용 PlayerState, PlayerController, PlayerCharacter가 서버에 생성&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;background-color: #fafafa; color: #333333; text-align: start;&quot;&gt;PlayerState, PlayerController, PlayerCharacter&lt;/span&gt; 등 이 다시 Client1에 복제&lt;/li&gt;
&lt;li&gt;GameState 도 복제&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;5. 또 다른 클라이언트도 접속 ( 3~4 단계 반복 )&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;마찬가지로 서버는 접속 시도하는 또 다른 클라이언트에게 Level 정보를 전달&lt;/li&gt;
&lt;li&gt;클라이언트도 해당 Level을 열고, Level을 여는데 성공했다고 데디 서버에게 알림&lt;/li&gt;
&lt;li&gt;Client2 전용 PlayerState, PlayerController, PlayerCharacter가 서버에 생성&lt;/li&gt;
&lt;li&gt;이것이 다시 Client2에 복제&lt;/li&gt;
&lt;li&gt;GameState도 복제&lt;/li&gt;
&lt;li&gt;이때, 클라이언트 간의 PlayerState와 PlayerCharacter도 복제되면서 서로 존재 확인 가능&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;※ 다른 클라이언트의 PlayerState와 PlayerCharacter의 정보는 저장하지만&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp;PlayerController는 본인의 클라이언트의 정보만 저장 ※&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&amp;lt; 이미지 설명 &amp;gt;&lt;/b&gt;&lt;/p&gt;
&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;1181&quot; data-origin-height=&quot;655&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/QAall/dJMcadQogpz/j1ArSxs6caKyDenb4iMAL1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/QAall/dJMcadQogpz/j1ArSxs6caKyDenb4iMAL1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/QAall/dJMcadQogpz/j1ArSxs6caKyDenb4iMAL1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FQAall%2FdJMcadQogpz%2Fj1ArSxs6caKyDenb4iMAL1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;260&quot; height=&quot;655&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;1181&quot; data-origin-height=&quot;655&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;

&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;&amp;darr;&lt;/p&gt;
&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;1166&quot; data-origin-height=&quot;634&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/q4rcK/dJMcagGuV7y/7dQjAs9o2pK07PGx2maiw0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/q4rcK/dJMcagGuV7y/7dQjAs9o2pK07PGx2maiw0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/q4rcK/dJMcagGuV7y/7dQjAs9o2pK07PGx2maiw0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fq4rcK%2FdJMcagGuV7y%2F7dQjAs9o2pK07PGx2maiw0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;256&quot; height=&quot;139&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;1166&quot; data-origin-height=&quot;634&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;

&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #fafafa; color: #333333; text-align: start;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;&amp;darr;&lt;/span&gt;&lt;/p&gt;
&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;1669&quot; data-origin-height=&quot;1184&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/F8KLB/dJMcaasH8gt/qKFWUK59967Gtnb73qowEK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/F8KLB/dJMcaasH8gt/qKFWUK59967Gtnb73qowEK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/F8KLB/dJMcaasH8gt/qKFWUK59967Gtnb73qowEK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FF8KLB%2FdJMcaasH8gt%2FqKFWUK59967Gtnb73qowEK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;345&quot; height=&quot;245&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;1669&quot; data-origin-height=&quot;1184&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;

&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #fafafa; color: #333333; text-align: start;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;&amp;darr;&lt;/span&gt;&lt;/p&gt;
&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;1669&quot; data-origin-height=&quot;1192&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Tm9jn/dJMcaftXvG9/FkaY8ig8krfsUNSGo6Yxek/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Tm9jn/dJMcaftXvG9/FkaY8ig8krfsUNSGo6Yxek/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Tm9jn/dJMcaftXvG9/FkaY8ig8krfsUNSGo6Yxek/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FTm9jn%2FdJMcaftXvG9%2FFkaY8ig8krfsUNSGo6Yxek%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;345&quot; height=&quot;1192&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;1669&quot; data-origin-height=&quot;1192&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;

&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #fafafa; color: #333333; text-align: start;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;&amp;darr;&lt;/span&gt;&lt;/p&gt;
&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;2120&quot; data-origin-height=&quot;1191&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/rHnZK/dJMcagGuV7z/nXs6BoLs7SMLKglmKuUOo1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/rHnZK/dJMcagGuV7z/nXs6BoLs7SMLKglmKuUOo1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/rHnZK/dJMcagGuV7z/nXs6BoLs7SMLKglmKuUOo1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FrHnZK%2FdJMcagGuV7z%2FnXs6BoLs7SMLKglmKuUOo1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;459&quot; height=&quot;258&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;2120&quot; data-origin-height=&quot;1191&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;

&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #fafafa; color: #333333; text-align: start;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;darr;&lt;/span&gt;&lt;/p&gt;
&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;2122&quot; data-origin-height=&quot;1185&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Gv6Mw/dJMcaftXvHa/Lh4O5NjbUhwgUlfGoQmPQk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Gv6Mw/dJMcaftXvHa/Lh4O5NjbUhwgUlfGoQmPQk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Gv6Mw/dJMcaftXvHa/Lh4O5NjbUhwgUlfGoQmPQk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FGv6Mw%2FdJMcaftXvHa%2FLh4O5NjbUhwgUlfGoQmPQk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;459&quot; height=&quot;1185&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;2122&quot; data-origin-height=&quot;1185&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;lt; 서버 - 클라이언트 구조의 중요한 특징 &amp;gt;&lt;/p&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;클라이언트와 클라이언트 간의 통신이 불가능&lt;/li&gt;
&lt;li&gt;오직 서버와 클라이언트 사이의 통신만 가능&lt;/li&gt;
&lt;li&gt;RPC나 프로퍼티 레플리케이션에도 영향&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;[ 강의 1-4. 실습 환경 설정 ]&lt;/p&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;899&quot; data-origin-height=&quot;769&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/c3MWss/dJMcaafaXcQ/AMlNCfYvgfiokwuWBW3o40/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/c3MWss/dJMcaafaXcQ/AMlNCfYvgfiokwuWBW3o40/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/c3MWss/dJMcaafaXcQ/AMlNCfYvgfiokwuWBW3o40/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fc3MWss%2FdJMcaafaXcQ%2FAMlNCfYvgfiokwuWBW3o40%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;899&quot; height=&quot;769&quot; data-origin-width=&quot;899&quot; data-origin-height=&quot;769&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2111&quot; data-origin-height=&quot;421&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bceGHn/dJMcai5d4eP/3qOnvxup1v8ma45FKMIXG1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bceGHn/dJMcai5d4eP/3qOnvxup1v8ma45FKMIXG1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bceGHn/dJMcai5d4eP/3qOnvxup1v8ma45FKMIXG1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbceGHn%2FdJMcai5d4eP%2F3qOnvxup1v8ma45FKMIXG1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2111&quot; height=&quot;421&quot; data-origin-width=&quot;2111&quot; data-origin-height=&quot;421&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;

&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;위젯 객체는 &lt;b&gt;Owning Client&lt;/b&gt;에서만 생성&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Owning Client&lt;/b&gt;를 판별하기 위해서 IsLocalController() 함수를 호출&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&amp;lt; PlayerController 의&amp;nbsp; BeginPlay 함수 안에 작성 &amp;gt;&lt;/b&gt;&lt;/p&gt;
&lt;pre class=&quot;kotlin&quot;&gt;&lt;code&gt;// Owning Client 인지 확인
if (IsLocalController() == false)
{
    return;
}&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;lt; 데디케이티드 서버 환경 설정 &amp;gt;&lt;/p&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt; &lt;span data-token-index=&quot;0&quot;&gt;&amp;lt; Allow Late Joining 체크 &amp;gt;&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;257&quot; data-origin-height=&quot;255&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/lbe8b/dJMcadQopt1/GuKaCBpkO4HBmiWlUGokGK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/lbe8b/dJMcadQopt1/GuKaCBpkO4HBmiWlUGokGK/img.png&quot; data-alt=&quot;Add another cliner 버튼으로 클라이언트 추가 가능&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/lbe8b/dJMcadQopt1/GuKaCBpkO4HBmiWlUGokGK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Flbe8b%2FdJMcadQopt1%2FGuKaCBpkO4HBmiWlUGokGK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;257&quot; height=&quot;255&quot; data-origin-width=&quot;257&quot; data-origin-height=&quot;255&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Add another cliner 버튼으로 클라이언트 추가 가능&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;span data-token-index=&quot;0&quot;&gt;&amp;lt; &lt;span data-token-index=&quot;0&quot;&gt;Always on top&lt;/span&gt; &amp;gt; (필수 X)&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;새 클라이언트 화면이 항상 위에 생성&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&amp;lt; &lt;span data-token-index=&quot;0&quot;&gt;Multiplayer Options&lt;/span&gt; - &lt;span data-token-index=&quot;0&quot;&gt;Launch Seperate Server&lt;/span&gt; &amp;gt;&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;체크 시 서버 따로 생성&lt;/li&gt;
&lt;li&gt;체크 해제 시 에디터 화면이 클라이언트이자 서버&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;span style=&quot;background-color: #fafafa; color: #333333; text-align: start;&quot;&gt;&amp;lt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;background-color: #fafafa; color: #333333; text-align: start;&quot; data-token-index=&quot;0&quot;&gt;Multiplayer Options&lt;/span&gt;&lt;span style=&quot;background-color: #fafafa; color: #333333; text-align: start;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;-&lt;span&gt; &lt;span data-token-index=&quot;0&quot;&gt;Run Under One Process&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;background-color: #fafafa; color: #333333; text-align: start;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&amp;gt;&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;체크하면 같은 프로세스 내라서 공유하는 에셋이 생성&lt;/li&gt;
&lt;li&gt;따라서 확실한 멀티플레이 환경이라고 보기엔 무리&lt;/li&gt;
&lt;li&gt;성능상의 이점 : 체크하지 않으면 서버와 각 클라이언트들이 다른 프로세스로 분기&lt;/li&gt;
&lt;li&gt;추후에 멀티플레이 디버깅 시 체크하고 로깅으로 확인 가능&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&amp;lt; &lt;span data-token-index=&quot;0&quot;&gt;&amp;ldquo;Net Mode&amp;rdquo;는 &amp;ldquo;Play as Client&amp;rdquo;로 설정&lt;/span&gt; &amp;gt;&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Net&amp;nbsp;Mode&amp;nbsp;-&amp;nbsp;Play&amp;nbsp;as&amp;nbsp;Client&amp;nbsp;:&amp;nbsp;Dedicated&amp;nbsp;환경&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;[ 강의 1-5. NetMode, NetConnection, NetDriver ]&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;lt;NetMode &amp;gt;&lt;/p&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&amp;lt; 정의 &amp;gt;&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;해당 게임 프로세스가 네트워크 상에서 어떤 역할을 하고 있는지를 의미&lt;/li&gt;
&lt;li&gt;싱글(NM_StandAlone) 서버(NM_Listen, NM_DedicatedServer), 클라이언트(NM_Client)&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&amp;lt; NetMode의 필요성 &amp;gt;&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;로직이 서버에서 돌고 있는지 클라이언트에서 돌고 있는지를 확인 할 때 사용&lt;/li&gt;
&lt;li&gt;멀티플레이 개발 시에 같은 코드가 여러 PC에서 동작 -&amp;gt; 서버 PC에서, 내 PC에서, 다른 사람의 PC에서 동작&lt;/li&gt;
&lt;/ul&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;914&quot; data-origin-height=&quot;509&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/6fjtE/dJMcagsSEbs/6LfU7DKQGHKx2CLrR3KCA0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/6fjtE/dJMcagsSEbs/6LfU7DKQGHKx2CLrR3KCA0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/6fjtE/dJMcagsSEbs/6LfU7DKQGHKx2CLrR3KCA0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F6fjtE%2FdJMcagsSEbs%2F6LfU7DKQGHKx2CLrR3KCA0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;914&quot; height=&quot;509&quot; data-origin-width=&quot;914&quot; data-origin-height=&quot;509&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;

&lt;p data-ke-size=&quot;size16&quot;&gt;위 사진처럼 구조가 짜여져 있다는 가정 하에&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;PlayerCharacter :: BeginPlay 함수에 UE_LOG를 작성했다면&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;ㄴ Server ,C1,C2에 해당돼있는 모든 PlayerCharacter의 UE_LOG가 호출된다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;빨간 PlayerController :: BeginPlay 함수에 UE_LOG를 작성했다면&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;ㄴ Server,C1 의 PlayerController에 UE_LOG가 호출된다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&amp;lt; NetMode 와 NetRole &amp;gt;&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;NetMode : 월드 단위의 속성값&lt;/li&gt;
&lt;li&gt;NetRole : 액터 단위의 속성값&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;lt; UWorld::InternalGetNetMode() 함수 살펴보기 &amp;gt;&lt;/p&gt;
&lt;pre class=&quot;zephir&quot;&gt;&lt;code&gt;ENetMode UWorld::InternalGetNetMode() const
{
    if ( NetDriver != NULL )                                      // 넷드라이버가 설정되어 있으면
    {
       const bool bIsClientOnly = IsRunningClientOnly();
       return bIsClientOnly ? NM_Client : NetDriver-&amp;gt;GetNetMode(); // 클라 아니면 서버 둘 중 하나다.
    }

    ...
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre class=&quot;php&quot;&gt;&lt;code&gt;// UNetDriver.cpp

ENetMode UNetDriver::GetNetMode() const
{
#if WITH_EDITOR
    if (World &amp;amp;&amp;amp; World-&amp;gt;WorldType == EWorldType::PIE &amp;amp;&amp;amp; IsServer())
    {
       FWorldContext* WorldContext = GEngine-&amp;gt;GetWorldContextFromWorld(World);
       if (WorldContext &amp;amp;&amp;amp; WorldContext-&amp;gt;RunAsDedicated) // 데디로 실행했다면
       {
          return NM_DedicatedServer;                      // 데디 서버 반환.
       }
    }
#endif

    return (IsServer() ? (GIsClient ? NM_ListenServer : NM_DedicatedServer) : NM_Client);
    // 서버인데, 클라이언트로 참여하고 있다? 리슨서버. 서버인데 참여하고 있지 않다? 데디서버.
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre class=&quot;cpp&quot;&gt;&lt;code&gt;bool UNetDriver::IsServer() const
{
    return ServerConnection == NULL;
    // 클라이언트는 무조건 서버 커넥션을 가지고 있다.
}&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;lt; NetConnection &amp;gt;&lt;/p&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&amp;lt; 정의 &amp;gt;&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;다른 PC와의 연결이 발생하면 그에 대응하는 UNetConnection 객체도 생성&lt;/li&gt;
&lt;li&gt;서버에 클라이언트가 접속하면 서버에는 ClientConnection 객체가 추가&lt;/li&gt;
&lt;li&gt;반대로 클라이언트에는 ServerConnection 객체가 생성&lt;/li&gt;
&lt;li&gt;두 PC는 UNetConnection 객체를 통해 통신&lt;/li&gt;
&lt;li&gt;UNetDriver는 생성된 UNetConnection 객체를 소유하고 관리&lt;/li&gt;
&lt;li&gt;서버 PC에 생성된 UNetDriver는 접속한 클라이언트의 수 만큼 UNetConnection을 관리&lt;/li&gt;
&lt;li&gt;클라이언트 PC에 생성된 UNetDriver는 ServerConnection 단 하나만을 관리&lt;br /&gt;&lt;b&gt;서버-클라이언트&lt;/b&gt; 구조의 템플릿&lt;/li&gt;
&lt;li&gt;NetDriver : 멀티플레이 실행 장치&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;cpp&quot;&gt;&lt;code&gt;// UNetDriver.h

...

UCLASS(...)
class UNetDriver : public UObject, public FExec
{
    ...
    
    // 클라이언트 전용 (단일 취급)
    UPROPERTY()
    TObjectPtr&amp;lt;class UNetConnection&amp;gt; ServerConnection;

    // 서버 전용 (복수 취급 (배열))
    UPROPERTY()
    TArray&amp;lt;TObjectPtr&amp;lt;UNetConnection&amp;gt;&amp;gt; ClientConnections;
    
    ...

}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre class=&quot;cpp&quot;&gt;&lt;code&gt;UNetConnection* AActor::GetNetConnection() const
{
    return Owner ? Owner-&amp;gt;GetNetConnection() : nullptr;
    // 여기서 오너는 액터/폰/컨트롤러 등등이 해당
    // Owner가 지정되어 있지 않으면 통신 불가
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;class UNetConnection* APawn::GetNetConnection() const
{
    if ( Controller ) // 컨트롤러가 있다면
    {
       return Controller-&amp;gt;GetNetConnection(); // 컨트롤러의 GetNetConnection()을 호출.
    }
    return Super::GetNetConnection(); // 없다면 AActor::GetNetConnection() 함수 호출.
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre class=&quot;cpp&quot;&gt;&lt;code&gt;UNetConnection* APlayerController::GetNetConnection() const
{
    return (Player != NULL) ? NetConnection : NULL;
    // 플레이어가 존재한다면 NetConnection 객체를 반환.
}&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;lt; 언리얼에서의 Ownership &amp;gt;&lt;/p&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;하나의 ClientConnection은 하나의 PlayerController를 소유 -&amp;gt; PlayerController의 Owning Connection은 ClientConnection 와 동일&lt;/li&gt;
&lt;li&gt;PlayerController가 빙의하는 폰의 Owner 속성은 해당 PlayerController로 설정&lt;/li&gt;
&lt;li&gt;폰에 무기 액터가 생성되고, 무기 액터의 Owner 속성에 해당 폰을 설정 가능&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;background-color: #fafafa; color: #333333; text-align: start;&quot;&gt;패밀리&lt;/span&gt; : ClientConnection에서부터 무기 액터에 이르는 소유 관계&lt;/li&gt;
&lt;li&gt;소유 관계 속에 있는 액터가 본인의 Owning Connection을 얻으려면&lt;br /&gt;AActor::GetNetConnection() 함수를 호출&lt;/li&gt;
&lt;li&gt;이 소유 관계가 후에 배울 RPC와 Property Replication과 관련&lt;/li&gt;
&lt;/ul&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2128&quot; data-origin-height=&quot;697&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bUB0rJ/dJMb99NZjxM/OHCBGedrkRvHWxhVTGn3v1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bUB0rJ/dJMb99NZjxM/OHCBGedrkRvHWxhVTGn3v1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bUB0rJ/dJMb99NZjxM/OHCBGedrkRvHWxhVTGn3v1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbUB0rJ%2FdJMb99NZjxM%2FOHCBGedrkRvHWxhVTGn3v1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2128&quot; height=&quot;697&quot; data-origin-width=&quot;2128&quot; data-origin-height=&quot;697&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;lt; NetDriver &amp;gt;&lt;/p&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;언리얼 네트워크 통신에서 로우레벨 동작들을 관리하는 클래스&lt;/li&gt;
&lt;li&gt;싱글플레이에서는 UNetDriver 객체가 생성 X&lt;/li&gt;
&lt;li&gt;멀티플레이에서만 UWorld::Listen() 함수를 통해 UNetDriver 객체가 생성&lt;/li&gt;
&lt;li&gt;멀티플레이에 참여하는 각 PC마다 UNetDriver 객체가 생성&lt;/li&gt;
&lt;li&gt;ex) 컴퓨터 포맷 후 Wifi Driver를 설치하는 것과 유사합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre class=&quot;reasonml&quot;&gt;&lt;code&gt;// UWorld.cpp

bool UWorld::Listen( FURL&amp;amp; InURL )
{
#if WITH_SERVER_CODE
    ...

    // Create net driver. 넷드라이버 만드는 부분이 있다~ 정도.
    if (GEngine-&amp;gt;CreateNamedNetDriver(this, NAME_GameNetDriver, NAME_GameNetDriver))
    {
       NetDriver = GEngine-&amp;gt;FindNamedNetDriver(this, NAME_GameNetDriver);

       ...
    }

    ...
}&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;[ NetMode에 대한 추가적인 설명 ]&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;&amp;lt; StandAlone &amp;gt;&lt;/p&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;게임이 원격 클라이언트의 연결을 허용하지 않는 서버로 실행&lt;/li&gt;
&lt;li&gt;커넥션이 없음. 게임에 참여하는 모든 플레이어는 로컬 플레이어&lt;/li&gt;
&lt;li&gt;싱글 플레이 및 로컬 플레이 게임에 사용&lt;/li&gt;
&lt;li&gt;로컬 플레이어에 맞게 서버 측 로직과 클라이언트측 로직을 모두 실행&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;lt;&amp;nbsp;Client&amp;nbsp;&amp;gt;&lt;/p&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;게임이 네트워크 멀티플레이 세션에서 서버에 연결된 클라이언트로 실행&lt;/li&gt;
&lt;li&gt;서버 측 로직을 실행하지 않습니다. 서버로부터 복제된 Proxy를 보여주는 역할&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;lt;&amp;nbsp;Listen&amp;nbsp;Server&amp;nbsp;&amp;gt;&lt;/p&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;게임이 네트워크 멀티플레이어 세션을 호스팅 하는 서버로 실행&lt;/li&gt;
&lt;li&gt;원격 클라이언트의 연결을 수락하고 로컬 플레이어를 서버에 직접 배치&lt;br /&gt;즉, 서버 자기 자신도 게임에 직접 참여&lt;/li&gt;
&lt;li&gt;이 모드는 캐주얼 협동 및 경쟁 멀티플레이어에 자주 사용&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;lt;&amp;nbsp;Dedicated&amp;nbsp;Server&amp;nbsp;&amp;gt;&lt;/p&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;게임이 네트워크 멀티플레이 세션을 호스팅 하는 서버로 실행&lt;/li&gt;
&lt;li&gt;원격 클라이언트의 연결을 허용하지만 로컬 플레이어는 존재 X&lt;br /&gt;따라서 그래픽, 사운드, 입력 및 기타 플레이어 중심 기능이 필요 없으니 삭제&lt;/li&gt;
&lt;li&gt;이 모드는 지속적이고 안전한 대규모 멀티플레이어가 필요한 게임에 주로 사용&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;[ NetMode에 따른 액터 위치 ]&lt;/p&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;서버에만 존재하는 액터&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;GameMode&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;서버와 모든 클라이언트에 존재하는 액터&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;배경 액터와 Pawn&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;서버와 클라이언트에만 존재하는 액터&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;PlayerController , ABP&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;클라이언트에만 존재하는 언리얼 오브젝트&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;UI&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;[ 강의 1-6. NetRole ]&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span data-token-index=&quot;0&quot;&gt;&amp;lt; &lt;span data-token-index=&quot;0&quot;&gt;Authority와 Proxy&lt;/span&gt; &amp;gt;&lt;/span&gt;&lt;/p&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;서버에 스폰 된 액터가 가진 NetRole 속성 값은 언제나 Authority&lt;br /&gt;&amp;nbsp;ㄴ 서버에 스폰 된 액터에서 수행될 로직은 &amp;ldquo;권한을 가지고 있다&amp;rdquo;를 의미&lt;br /&gt;&amp;nbsp; &amp;nbsp;ㄴ 게임에 중대한 영향을 끼치는 로직은 NetRole이 Authority일 때만 수행하게 설정&lt;/li&gt;
&lt;li&gt;NetRole이 Authority인 액터가 클라이언트로 복제되었을 때,&lt;br /&gt;클라이언트에 복제된 액터의 NetRole 속성 값은 Proxy : &amp;ldquo;허상&amp;rdquo;&lt;/li&gt;
&lt;li&gt;여기서는 중요한 로직을 수행 X&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;lt; &lt;span data-token-index=&quot;0&quot;&gt;로컬 롤과 리모트 롤&lt;/span&gt; &amp;gt;&lt;/p&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #fafafa; color: #333333; text-align: start;&quot; data-token-index=&quot;1&quot;&gt;&lt;b&gt;로컬 롤(Local Role)&lt;/b&gt; :&lt;/span&gt;&amp;nbsp;현재 동작하는 컴퓨터에서의 롤&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #fafafa; color: #333333; text-align: start;&quot;&gt;&lt;b&gt;리모트 롤(Remote Role)&lt;/b&gt;&lt;/span&gt; : 커넥션으로 연결된 반대편 컴퓨터에서의 롤&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;lt; &lt;span data-token-index=&quot;0&quot;&gt;NetRole의 종류&lt;/span&gt; &amp;gt;&lt;/p&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&amp;lt; &lt;span data-token-index=&quot;0&quot;&gt;None&lt;/span&gt; &amp;gt;&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;레플리케이션 되지 않는 액터&lt;/li&gt;
&lt;li&gt;다만, 무조건적이진 않음&lt;/li&gt;
&lt;li&gt;ex) LocalRole은 Authority이고 RemoteRole이 None이라면&lt;br /&gt;서버에서 스폰 되고, 클라쪽으로 레플리케이션 되지 않는 액터라고 취급&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;span data-token-index=&quot;0&quot;&gt;&amp;lt; &lt;span data-token-index=&quot;0&quot;&gt;Authority&lt;/span&gt; &amp;gt;&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;게임에 중대한 영향을 끼칠 수 있는 권한을 가진 액터&lt;/li&gt;
&lt;li&gt;서버에서 스폰된 액터가 LocalRole으로 Authority를 가질 수 있음&lt;/li&gt;
&lt;li&gt;ex) GameMode&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&amp;lt; &lt;span data-token-index=&quot;0&quot;&gt;Autonomous Proxy&lt;/span&gt; &amp;gt;&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Authority 액터의 복제본&lt;br /&gt;서버로부터 데이터를 수신 받아서 동기화도 되면서, 서버로 송신도 가능&lt;/li&gt;
&lt;li&gt;ex) PlayerController&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&amp;lt; &lt;span data-token-index=&quot;0&quot;&gt;Simulated Proxy&lt;/span&gt; &amp;gt;&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Authority 액터의 복제본&lt;br /&gt;서버로부터 수신 받아서 동기화만 가능&lt;/li&gt;
&lt;li&gt;ex) AnotherPlayer 의 PlayerCharacter&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <author>blog13869</author>
      <guid isPermaLink="true">https://blog13869.tistory.com/74</guid>
      <comments>https://blog13869.tistory.com/74#entry74comment</comments>
      <pubDate>Fri, 31 Jul 2026 21:03:04 +0900</pubDate>
    </item>
    <item>
      <title>7/30 TIL</title>
      <link>https://blog13869.tistory.com/73</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;[ CH4_Unreal Engine 멀티플레이어 게임 개발 ]&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;팀이 다시 짜여졌고&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처음은 개인 과제로 시작을 하게 되었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;녹화 강의를 통해 공부를 하는 시간이고&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;배운 내용을 적게 되었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #fafafa; color: #333333; text-align: start;&quot;&gt;프로젝트 이름(CFM : Chapter Four Multi)의 이름을 사용해서 &lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #fafafa; color: #333333; text-align: start;&quot;&gt;생성하는 모든 파일들의 말머리에 CFM을 붙히는 규칙으로 정하였다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;[ 채팅 프로젝트 생성 ]&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;lt; CFMGameModeBase &amp;gt;&lt;/p&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;pre class=&quot;cpp&quot;&gt;&lt;code&gt;#pragma once

#include &quot;CoreMinimal.h&quot;
#include &quot;GameFramework/GameModeBase.h&quot;
#include &quot;CFMGameModeBase.generated.h&quot;

/**
 * 
 */
UCLASS()
class CFM_API ACFMGameModeBase : public AGameModeBase
{
    GENERATED_BODY()
    
};
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;기본적으로 GameMode를 Custom을 할 수 있게 GameModeBase를 상속받아 C++ 파일을 생성하여 작업을 시작&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;lt; CFMPlayerController &amp;gt;&lt;/p&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;pre class=&quot;cpp&quot;&gt;&lt;code&gt;#pragma once

#include &quot;CoreMinimal.h&quot;
#include &quot;GameFramework/PlayerController.h&quot;
#include &quot;CFMPlayerController.generated.h&quot;

/**
 * 
 */

class UCFMChatInput;
// 전방 선언으로 헤더 파일에 해당 내용들을 선언해도 문제생기지 않게 작성

UCLASS()
class CFM_API ACFMPlayerController : public APlayerController
{
    GENERATED_BODY()
    
    public:
    
    virtual void BeginPlay() override;
    
    void SetChatMessageString(const FString&amp;amp; InChatMessageString);
    
    void PrintChatMessageString(const FString&amp;amp; InChatMessageString);

    
    
    protected:
    
    UPROPERTY(EditDefaultsOnly)
    TSubclassOf&amp;lt;UCFMChatInput&amp;gt; ChatInputWidgetClass;
    // WBP_ChatInput 과 연결
    
    UPROPERTY()
    TObjectPtr&amp;lt;UCFMChatInput&amp;gt; ChatInputWidgetInstance;
    
    FString ChatMessageString;
    // 채팅 내용
    
};&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre class=&quot;cpp&quot;&gt;&lt;code&gt;#include &quot;Player/CFMPlayerController.h&quot;
#include &quot;UI/CFMChatInput.h&quot;
#include &quot;Kismet/KismetSystemLibrary.h&quot;


void ACFMPlayerController::BeginPlay()
{
    Super::BeginPlay();
    
    // 입력 할당
    FInputModeUIOnly InputModeUIOnly;
    SetInputMode(InputModeUIOnly);

    // ChatInputWidgetClass가 변수로 선언 돼있을 때 WBP_ChatInput을 연결
    if (IsValid(ChatInputWidgetClass) == true)
    {
       ChatInputWidgetInstance = CreateWidget&amp;lt;UCFMChatInput&amp;gt;(this, ChatInputWidgetClass);
       if (IsValid(ChatInputWidgetInstance) == true)
       {
          // 입력 내용을 Viewport에 출력할 수 있게 설정
          ChatInputWidgetInstance-&amp;gt;AddToViewport();
       }
    }
}

// ChatMessageString을 함수의 인자값으로 대입 후 출력
void ACFMPlayerController::SetChatMessageString(const FString&amp;amp; InChatMessageString)
{
    ChatMessageString = InChatMessageString;
    PrintChatMessageString(ChatMessageString);
}

// 화면에 String을 출력
void ACFMPlayerController::PrintChatMessageString(const FString&amp;amp; InChatMessageString)
{
    UKismetSystemLibrary::PrintString(this, ChatMessageString, true, true, FLinearColor::Red, 5.0f);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;lt; CFMChatInput &amp;gt;&lt;/p&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;pre class=&quot;cpp&quot;&gt;&lt;code&gt;#pragma once

#include &quot;CoreMinimal.h&quot;
#include &quot;Blueprint/UserWidget.h&quot;
#include &quot;CFMChatInput.generated.h&quot;

class UEditableTextBox;

UCLASS()
class CFM_API UCFMChatInput : public UUserWidget
{
    GENERATED_BODY()
    
public:
    
    // WBP_ChatInput 의 팔레트 안에 있는 'EditableTextBox_ChatInput' 에 바인딩
    UPROPERTY(meta=(BindWidget))
    TObjectPtr&amp;lt;UEditableTextBox&amp;gt; EditableTextBox_ChatInput;
    
    virtual void NativeConstruct() override;

    virtual void NativeDestruct() override;
    
    
protected:
    
    UFUNCTION()
    void OnChatInputTextCommitted(const FText&amp;amp; Text, ETextCommit::Type CommitMethod);
    
};&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre class=&quot;reasonml&quot;&gt;&lt;code&gt;#include &quot;UI/CFMChatInput.h&quot;
#include &quot;Components/EditableTextBox.h&quot;
#include &quot;Player/CFMPlayerController.h&quot;




// WBP_ChatInput 의 OnTextCommitted 이벤트와 Delegate로 바인딩 확인/연결
void UCFMChatInput::NativeConstruct()
{
    Super::NativeConstruct();
    if (EditableTextBox_ChatInput-&amp;gt;OnTextCommitted.IsAlreadyBound(this, &amp;amp;ThisClass::OnChatInputTextCommitted) == false)
    {
       EditableTextBox_ChatInput-&amp;gt;OnTextCommitted.AddDynamic(this, &amp;amp;ThisClass::OnChatInputTextCommitted);    
    }  
}

// WBP_ChatInput 의 OnTextCommitted 이벤트 종료 시 바인딩 해제
void UCFMChatInput::NativeDestruct()
{
    Super::NativeDestruct();
    if (EditableTextBox_ChatInput-&amp;gt;OnTextCommitted.IsAlreadyBound(this, &amp;amp;ThisClass::OnChatInputTextCommitted) == true)
    {
       EditableTextBox_ChatInput-&amp;gt;OnTextCommitted.RemoveDynamic(this, &amp;amp;ThisClass::OnChatInputTextCommitted);
    }

}


// Enter와 같은 키로 텍스트를 제출 했을 때 GetOwningPlayer 함수를 통해서 제출을 요청한 컴퓨터의 PlayerController를 가져오고
// 유효 했을 때 Cast 후 ChatMessageString 내용을 EditableTextBox_ChatInput 여기에 출력시키는 함수
// 이 후 빈 텍스트(FText())로 내용 지우기
void UCFMChatInput::OnChatInputTextCommitted(const FText&amp;amp; Text, ETextCommit::Type CommitMethod)
{
    if (CommitMethod == ETextCommit::OnEnter)
    {
       APlayerController* OwningPlayerController = GetOwningPlayer();
       if (IsValid(OwningPlayerController) == true)
       {
          ACFMPlayerController* OwningCFMPlayerController = Cast&amp;lt;ACFMPlayerController&amp;gt;(OwningPlayerController);
          if (IsValid(OwningCFMPlayerController) == true)
          {
             OwningCFMPlayerController-&amp;gt;SetChatMessageString(Text.ToString());

             EditableTextBox_ChatInput-&amp;gt;SetText(FText());
          }
       }
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;[ 스크럼 ]&lt;/p&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;lt; 오전 스크럼 &amp;gt;&lt;/p&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;782&quot; data-origin-height=&quot;301&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/FrkRN/dJMcagsSad7/ESRw9Yodq7DZszZz6o68eK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/FrkRN/dJMcagsSad7/ESRw9Yodq7DZszZz6o68eK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/FrkRN/dJMcagsSad7/ESRw9Yodq7DZszZz6o68eK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FFrkRN%2FdJMcagsSad7%2FESRw9Yodq7DZszZz6o68eK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;782&quot; height=&quot;301&quot; data-origin-width=&quot;782&quot; data-origin-height=&quot;301&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;

&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;lt; 오후 스크럼 &amp;gt;&lt;/p&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;773&quot; data-origin-height=&quot;297&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cCQe10/dJMcaftXi3b/MpZFBvFAX7XSUp5Jwu4q6K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cCQe10/dJMcaftXi3b/MpZFBvFAX7XSUp5Jwu4q6K/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cCQe10/dJMcaftXi3b/MpZFBvFAX7XSUp5Jwu4q6K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcCQe10%2FdJMcaftXi3b%2FMpZFBvFAX7XSUp5Jwu4q6K%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;773&quot; height=&quot;297&quot; data-origin-width=&quot;773&quot; data-origin-height=&quot;297&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/div&gt;
&lt;/div&gt;</description>
      <author>blog13869</author>
      <guid isPermaLink="true">https://blog13869.tistory.com/73</guid>
      <comments>https://blog13869.tistory.com/73#entry73comment</comments>
      <pubDate>Thu, 30 Jul 2026 20:50:15 +0900</pubDate>
    </item>
    <item>
      <title>7/28 TIL</title>
      <link>https://blog13869.tistory.com/72</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;[ 커리어데이 ]&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;어제 브로셔 템플릿을 작성한 이 후 개인적으로 느낀점과 보완할 점 등 개인 회고를 작성하는 시간을 가졌다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;[ CH3 - 프로젝트 개인 회고 ]&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;lt; 내가 맡은&amp;nbsp;&lt;span data-token-index=&quot;1&quot;&gt;주요 작업 및 기능&lt;/span&gt; &amp;gt;&lt;/p&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;게임 시작부터 게임이 끝나는 로직을 구체화하고 레벨이 넘어가는 조건, 웨이브를 클리어하는 조건 등 게임의 기반을 다짐&lt;/li&gt;
&lt;li&gt;SpawnPortal을 만들어 몬스터를 스폰하는 함수를 작성하고 DT를 만들어 편리하고 간단하게 웨이브마다 스폰되는 몬스터의 종류와 수를 설정할 수 있게 구현&lt;/li&gt;
&lt;li&gt;아이템을 구현하여 레벨에 배치할 수 있게끔 설계하고 몬스터 처치 시 확률적으로 드랍하게 구현&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;lt; 내가 맡은 &lt;span data-token-index=&quot;1&quot;&gt;팀&lt;/span&gt;&amp;nbsp;&lt;span data-token-index=&quot;3&quot;&gt;내 역할&lt;/span&gt; &amp;gt;&lt;/p&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;게임 플레이의 흐름을 구현&lt;/li&gt;
&lt;li&gt;팀장으로서 팀원의 소통을 주도하고, 팀원간의 갈등을 조율하여 보다 성공적인 프로젝트의 마무리를 이끌어냄&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;lt; 이번 프로젝트를 진행하면서 느낀&amp;nbsp;&lt;span data-token-index=&quot;1&quot;&gt;나의 강점&lt;/span&gt; &amp;gt;&lt;/p&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;게임의 기본적인 틀을 구상하고 각 파트마다 요청 사항을 작성하여 기획 단계에 설계한대로 구현하려고 소통하려는 능력&lt;/li&gt;
&lt;li&gt;기본적인 구현에 끝나지 않고 추가 요소를 생각해내고 게임의 분위기를 고려하여 재미를 더할 수 있는 로직을 추천 및 요청하고 구현하는 능력&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;lt; 이번 프로젝트에서&amp;nbsp;&lt;span data-token-index=&quot;1&quot;&gt;가장 몰입했던 순간&lt;/span&gt; &amp;gt;&lt;/p&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;다른 파트의 팀원분께 요청 사항을 작성하고, 해당 내용이 내가 구현하고 싶었던 내용과 일치하게 작동했을 때 성취감을 느낌&lt;/li&gt;
&lt;li&gt;기본적인 테스트를 모두 마치고 오류 검출이 끝난 후 새로 작성한 내용들을 바탕으로 모든 파트의 코드를 조합해서 실제 게임과 같은 구현이 됐을 때&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;lt; 내가&amp;nbsp;&lt;span data-token-index=&quot;1&quot;&gt;팀에 기여했다고 생각하는 부분&lt;/span&gt; &amp;gt;&lt;/p&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;하드 스킬 : 파트 별 작업 내용이 Merge됐을 때 본인 브랜치에 최신화를 한 후 테스트를 해보고 수치 조정이나 이펙트, 모션 등 피드백 및 코드 구현에 대해 함께 고민하고 방향성 제공&lt;/li&gt;
&lt;li&gt;소프트 스킬 : 팀장의 역할로 현재 구현해야 할 내용들에 대한 순서도 및 중요도를 인지하고 팀원들에게 공유함으로써 합리적이고 신속한 개발에 이바지함&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;lt; 프로젝트를 진행하면서 &lt;span data-token-index=&quot;1&quot;&gt;아쉬웠던 것들 중 가장 성장한 부분 &amp;gt;&lt;/span&gt;&lt;/p&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;아쉬웠던 부분: 초반 팀원들과의 소통으로 인한 교류의 문제로 계획했던 진행에 차질이 생김&lt;/li&gt;
&lt;li&gt;성장 과정: 틀어진 계획 내에서 우선 순위를 정하여 기초 구현으로 뼈대를 잡은 뒤 후에 작업할 내용을 팀원들과 의논하고 설계함&lt;/li&gt;
&lt;li&gt;결과: 여러 문제가 생겼지만 높은 완성도를 갖춘 개발을 하여 기존 기획에서 크게 벗어나지 않은 개발을 이루어 냄&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;lt; 내가 맡았던&amp;nbsp;&lt;span data-token-index=&quot;1&quot;&gt;기술적 구현 중 가장 잘 되었다고 생각한 부분&lt;/span&gt; &amp;gt;&lt;/p&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;데이터 테이블로 웨이브마다 호출되는 정보를 설정하여 어떤 포탈에서 어떤몬스터를 몇마리 소환할지 간편하게 설정할 수 있게 설계함&lt;/li&gt;
&lt;li&gt;플레이어의 IA는 Character파트 안에 구현하게 설계하고, PFPlayerController에서 UI의 정보를 업데이트 해주는 함수들을 선언 및 정의하여 각 파트에서 UI의 출력 함수를 호출할 때 혼동이 없게 구현함&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;lt; 프로젝트 중&amp;nbsp;&lt;span data-token-index=&quot;1&quot;&gt;발생한 기술 문제를 내가 주도적으로 해결한 경험&lt;/span&gt; &amp;gt;&lt;/p&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;문제:&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;레벨 및 몬스터를 구현했지만 어떤 레벨의 어떤 웨이브에서 어떤 몬스터가 나오게 만들지 구체화 하지 못하는 상황이 발생&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;과정:&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;총 4개의 레벨에 각각 3웨이브 씩 갖추게 하였고, 총 12개의 웨이브에서 어떤 몬스터를 얼마나 스폰할 지 직접 DT_Wave를 수정해가며 어떻게 게임을 구상할 지 계속 테스트 및 적용&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;결과:&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;각 레벨이 이동 할 때 Character의 HP를 GameInstance에 저장하고, 총기의 총알 및 데미지는 초기화하여 게임의 긴장감을 부여하고, 밸런스를 갖추어 너무 쉽지도 어렵지도 않은 적당한 난이도로 개선&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;lt; 팀원들과의&amp;nbsp;&lt;span data-token-index=&quot;1&quot;&gt;협업에서 내가 잘했다고 느낀 부분&lt;/span&gt; &amp;gt;&lt;/p&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;문제 발생 시 회의를 이끌어 나가며 최선의 문제 해결 방안을 제안&lt;/li&gt;
&lt;li&gt;작성한 코드를 구현하기 위해 다른 파트의 코드 구현 요청 사항을 상세하게 기입하여 요청 사항의 이유를 확실하게 납득시킴&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;lt; 프로젝트 중 발생한&amp;nbsp;&lt;span data-token-index=&quot;1&quot;&gt;협업 및 커뮤니케이션 문제를 내가 주도적으로 해결한 경험&lt;/span&gt; &amp;gt;&lt;/p&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;문제:&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;각 파트마다 구현하는 것에 대한 생각이 각기 다름. 어느 분은 이 것 저 것 모두 구현을 하고 싶어하시고, 어떤 분은 필수 기본 구현만 갖추고 공부/휴식을 원하는 분도 계시는 상황&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;과정:&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;팀원간의 회의/소통을 주도하여 각 파트마다 기본 기능 구현을 모두 완료 하고, 특수 기능 구현 2가지 씩 추가하자고 제안하여 합의점을 이끌어 냄.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;결과:&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;캐릭터 파트에서는 대쉬/대검 모션을 구현하였고, 몬스터 파트에서는 보스몬스터 / 자폭몬스터를 구현하였고, UI에서는 시네마틱 연출/ Kill Log를 구현하였고, GameManager 파트에서는 LevelPortal의Activate()/ DropItem()로 몬스터 처치 시 아이템 확률 드랍 코드를 구현하였다.&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;lt; 같은 프로젝트를 다시 한다면, 나는&amp;nbsp;&lt;span data-token-index=&quot;1&quot;&gt;어떤 역할을 맡고 싶고&lt;/span&gt;,&lt;span data-token-index=&quot;3&quot;&gt; 그 이유&lt;/span&gt;&amp;nbsp;&amp;gt;&lt;/p&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;캐릭터 파트 역할을 맡고 싶다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;IA/IMC를 바인딩하여 캐릭터의 움직임을 대입시키고, 공격/이동/점프 등에 Animation을 적용하여 원하는 모션을 입혀 생동감있는 캐릭터 구현을 해보고 싶고, Game의 매커니즘/흐름에 맞게 알맞은 기능들을 구현해보고 싶다.(패링, 대쉬, 카운터, 로크라이크 기반 능력치 업그레이드 등)&amp;lt; 피드백 &amp;gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #fafafa; color: #333333; text-align: start;&quot;&gt;&amp;lt; 피드백 &amp;gt;&lt;/span&gt;&lt;/p&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;피드백 단계에서 본인이 질의응답에 참여하게 되었다.&lt;/li&gt;
&lt;li&gt;왜 BP를 쓰지 않고 C++로 로직 구현을 했냐는 질문에 쉽사리 대답을 하지 못하였다. 질문 자체를 많이 받아보지 않았기에 느꼈던 심리적 상태 때문이었던 것 같다. 위와 같은 상황을 여러번 마주함으로써 질문 자체에 대한 거부감을 줄이고, 처음 기획단계때 설계했던 내용을 바탕으로 대답하면 좋을것같다고 느낀다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <author>blog13869</author>
      <guid isPermaLink="true">https://blog13869.tistory.com/72</guid>
      <comments>https://blog13869.tistory.com/72#entry72comment</comments>
      <pubDate>Tue, 28 Jul 2026 21:04:55 +0900</pubDate>
    </item>
    <item>
      <title>7/27 TIL</title>
      <link>https://blog13869.tistory.com/71</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;[ 커리어데이 ]&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;오늘은 지난 프로젝트를 마친 후 회고, 브로셔 템플릿을 작성하는 활동을 하였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;[ 브로셔 템플릿 ]&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;lt; 게임 개요 &amp;gt;&lt;/p&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;592&quot; data-origin-height=&quot;412&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b8gFLo/dJMcaaMXlvA/YD82jyeqKGsfEkVp9k0Ga0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b8gFLo/dJMcaaMXlvA/YD82jyeqKGsfEkVp9k0Ga0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b8gFLo/dJMcaaMXlvA/YD82jyeqKGsfEkVp9k0Ga0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb8gFLo%2FdJMcaaMXlvA%2FYD82jyeqKGsfEkVp9k0Ga0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;592&quot; height=&quot;412&quot; data-origin-width=&quot;592&quot; data-origin-height=&quot;412&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;lt; 핵심 기능 &amp;gt;&lt;/p&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;lt; 캐릭터 대쉬 구현 &amp;gt;&lt;/p&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;658&quot; data-origin-height=&quot;1092&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/rVAre/dJMcagfi0Us/nsJKlGsC9ujjoyapuk9chK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/rVAre/dJMcagfi0Us/nsJKlGsC9ujjoyapuk9chK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/rVAre/dJMcagfi0Us/nsJKlGsC9ujjoyapuk9chK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FrVAre%2FdJMcagfi0Us%2FnsJKlGsC9ujjoyapuk9chK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;658&quot; height=&quot;1092&quot; data-origin-width=&quot;658&quot; data-origin-height=&quot;1092&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;

&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;lt; 보스몬스터 구현 &amp;gt;&lt;/p&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;658&quot; data-origin-height=&quot;790&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/oZBI0/dJMb998iYGq/ozahfBCdHM3il7IqFnvGSk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/oZBI0/dJMb998iYGq/ozahfBCdHM3il7IqFnvGSk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/oZBI0/dJMb998iYGq/ozahfBCdHM3il7IqFnvGSk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FoZBI0%2FdJMb998iYGq%2FozahfBCdHM3il7IqFnvGSk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;658&quot; height=&quot;790&quot; data-origin-width=&quot;658&quot; data-origin-height=&quot;790&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;lt; 핵심 기술 &amp;gt;&lt;/p&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;lt; Niagara System &amp;gt;&lt;/p&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;708&quot; data-origin-height=&quot;505&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/FBmPI/dJMcadiEnF0/1jMJgLpRiq7TxkviGSxG0k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/FBmPI/dJMcadiEnF0/1jMJgLpRiq7TxkviGSxG0k/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/FBmPI/dJMcadiEnF0/1jMJgLpRiq7TxkviGSxG0k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FFBmPI%2FdJMcadiEnF0%2F1jMJgLpRiq7TxkviGSxG0k%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;708&quot; height=&quot;505&quot; data-origin-width=&quot;708&quot; data-origin-height=&quot;505&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;687&quot; data-origin-height=&quot;855&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/brNoat/dJMcagfi0YH/0vSp0B4kDIIKcWDurQkAJk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/brNoat/dJMcagfi0YH/0vSp0B4kDIIKcWDurQkAJk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/brNoat/dJMcagfi0YH/0vSp0B4kDIIKcWDurQkAJk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbrNoat%2FdJMcagfi0YH%2F0vSp0B4kDIIKcWDurQkAJk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;687&quot; height=&quot;855&quot; data-origin-width=&quot;687&quot; data-origin-height=&quot;855&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;

&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;lt; 컴포넌트 기반 기능 구현 &amp;gt;&lt;/p&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;673&quot; data-origin-height=&quot;588&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dpnmHL/dJMcajbXD6c/GkkgyGWMc2MoqQAVGLzRek/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dpnmHL/dJMcajbXD6c/GkkgyGWMc2MoqQAVGLzRek/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dpnmHL/dJMcajbXD6c/GkkgyGWMc2MoqQAVGLzRek/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdpnmHL%2FdJMcajbXD6c%2FGkkgyGWMc2MoqQAVGLzRek%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;673&quot; height=&quot;588&quot; data-origin-width=&quot;673&quot; data-origin-height=&quot;588&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;[ 기술적 의사결정 사례 ]&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;lt; UpdateUI 할당&amp;gt;&amp;nbsp;&lt;/p&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;655&quot; data-origin-height=&quot;427&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dHdgO4/dJMcacjDoG8/Ho3xuGOBupcLFKxYma99i1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dHdgO4/dJMcacjDoG8/Ho3xuGOBupcLFKxYma99i1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dHdgO4/dJMcacjDoG8/Ho3xuGOBupcLFKxYma99i1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdHdgO4%2FdJMcacjDoG8%2FHo3xuGOBupcLFKxYma99i1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;655&quot; height=&quot;427&quot; data-origin-width=&quot;655&quot; data-origin-height=&quot;427&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;lt; SwitchWeapon - Sword &amp;gt;&lt;/p&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;652&quot; data-origin-height=&quot;1168&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b88spn/dJMcaaGfcFg/W05rKkJd3xEwBhywwJtJa0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b88spn/dJMcaaGfcFg/W05rKkJd3xEwBhywwJtJa0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b88spn/dJMcaaGfcFg/W05rKkJd3xEwBhywwJtJa0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb88spn%2FdJMcaaGfcFg%2FW05rKkJd3xEwBhywwJtJa0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;652&quot; height=&quot;1168&quot; data-origin-width=&quot;652&quot; data-origin-height=&quot;1168&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;lt; MonsterSpawnPortal &amp;gt;&lt;/p&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;639&quot; data-origin-height=&quot;496&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bS312a/dJMcaa0slBK/lbGfu6a9WsHDOi23V7BCMk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bS312a/dJMcaa0slBK/lbGfu6a9WsHDOi23V7BCMk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bS312a/dJMcaa0slBK/lbGfu6a9WsHDOi23V7BCMk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbS312a%2FdJMcaa0slBK%2FlbGfu6a9WsHDOi23V7BCMk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;639&quot; height=&quot;496&quot; data-origin-width=&quot;639&quot; data-origin-height=&quot;496&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;636&quot; data-origin-height=&quot;1438&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/EJAgT/dJMcafAIXJj/g1wgO2w0DLeA365ebm1Bk1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/EJAgT/dJMcafAIXJj/g1wgO2w0DLeA365ebm1Bk1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/EJAgT/dJMcafAIXJj/g1wgO2w0DLeA365ebm1Bk1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FEJAgT%2FdJMcafAIXJj%2Fg1wgO2w0DLeA365ebm1Bk1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;636&quot; height=&quot;1438&quot; data-origin-width=&quot;636&quot; data-origin-height=&quot;1438&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;[ 트러블 슈팅 사례 ]&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;lt; 롤백 &amp;gt;&lt;/p&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;633&quot; data-origin-height=&quot;432&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/qmcNU/dJMcaiRKJNA/ktCjsutyM2JLaMHzHEXEo0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/qmcNU/dJMcaiRKJNA/ktCjsutyM2JLaMHzHEXEo0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/qmcNU/dJMcaiRKJNA/ktCjsutyM2JLaMHzHEXEo0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FqmcNU%2FdJMcaiRKJNA%2FktCjsutyM2JLaMHzHEXEo0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;633&quot; height=&quot;432&quot; data-origin-width=&quot;633&quot; data-origin-height=&quot;432&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;lt; Build Failed &amp;gt;&lt;/p&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;610&quot; data-origin-height=&quot;513&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/49OpH/dJMcac4162p/n5DmeuVcnnjSy0hj4AxvbK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/49OpH/dJMcac4162p/n5DmeuVcnnjSy0hj4AxvbK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/49OpH/dJMcac4162p/n5DmeuVcnnjSy0hj4AxvbK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F49OpH%2FdJMcac4162p%2Fn5DmeuVcnnjSy0hj4AxvbK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;610&quot; height=&quot;513&quot; data-origin-width=&quot;610&quot; data-origin-height=&quot;513&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;606&quot; data-origin-height=&quot;475&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/vAnyB/dJMcabdUYiR/hlxVDaMJbwliVCChBEXzt0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/vAnyB/dJMcabdUYiR/hlxVDaMJbwliVCChBEXzt0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/vAnyB/dJMcabdUYiR/hlxVDaMJbwliVCChBEXzt0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FvAnyB%2FdJMcabdUYiR%2FhlxVDaMJbwliVCChBEXzt0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;606&quot; height=&quot;475&quot; data-origin-width=&quot;606&quot; data-origin-height=&quot;475&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;lt; 인원 감소로 인한 작업 분배 &amp;gt;&lt;/p&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;658&quot; data-origin-height=&quot;430&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/zYBO5/dJMcafAIXMA/BXehv8lY3ndQYcWH5eaaEk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/zYBO5/dJMcafAIXMA/BXehv8lY3ndQYcWH5eaaEk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/zYBO5/dJMcafAIXMA/BXehv8lY3ndQYcWH5eaaEk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FzYBO5%2FdJMcafAIXMA%2FBXehv8lY3ndQYcWH5eaaEk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;658&quot; height=&quot;430&quot; data-origin-width=&quot;658&quot; data-origin-height=&quot;430&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;[ 팀 구성 ]&lt;/p&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;466&quot; data-origin-height=&quot;396&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bbJcBp/dJMcaaGfcKD/zdJMjNgRJ3WBpfPhySukqK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bbJcBp/dJMcaaGfcKD/zdJMjNgRJ3WBpfPhySukqK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bbJcBp/dJMcaaGfcKD/zdJMjNgRJ3WBpfPhySukqK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbbJcBp%2FdJMcaaGfcKD%2FzdJMjNgRJ3WBpfPhySukqK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;466&quot; height=&quot;396&quot; data-origin-width=&quot;466&quot; data-origin-height=&quot;396&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/div&gt;
&lt;/div&gt;</description>
      <author>blog13869</author>
      <guid isPermaLink="true">https://blog13869.tistory.com/71</guid>
      <comments>https://blog13869.tistory.com/71#entry71comment</comments>
      <pubDate>Mon, 27 Jul 2026 20:32:21 +0900</pubDate>
    </item>
    <item>
      <title>7/24 TIL</title>
      <link>https://blog13869.tistory.com/70</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;[ PROJECT F ]&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;오늘 PPT 발표를 마치고 정상적으로 프로젝트를 완료 하였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1746&quot; data-origin-height=&quot;981&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dnRGbT/dJMcadvZkW0/9ah9g3xhv0pEbg9Dy8qXsK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dnRGbT/dJMcadvZkW0/9ah9g3xhv0pEbg9Dy8qXsK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dnRGbT/dJMcadvZkW0/9ah9g3xhv0pEbg9Dy8qXsK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdnRGbT%2FdJMcadvZkW0%2F9ah9g3xhv0pEbg9Dy8qXsK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1746&quot; height=&quot;981&quot; data-origin-width=&quot;1746&quot; data-origin-height=&quot;981&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1704&quot; data-origin-height=&quot;919&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/FJQ3D/dJMcafOiZBC/D2kjxwrteMF83ns3aqaL4K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/FJQ3D/dJMcafOiZBC/D2kjxwrteMF83ns3aqaL4K/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/FJQ3D/dJMcafOiZBC/D2kjxwrteMF83ns3aqaL4K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FFJQ3D%2FdJMcafOiZBC%2FD2kjxwrteMF83ns3aqaL4K%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1704&quot; height=&quot;919&quot; data-origin-width=&quot;1704&quot; data-origin-height=&quot;919&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1705&quot; data-origin-height=&quot;946&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/c6V438/dJMcaae62sw/QkxgahhgE9d1l2cN53S721/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/c6V438/dJMcaae62sw/QkxgahhgE9d1l2cN53S721/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/c6V438/dJMcaae62sw/QkxgahhgE9d1l2cN53S721/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fc6V438%2FdJMcaae62sw%2FQkxgahhgE9d1l2cN53S721%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1705&quot; height=&quot;946&quot; data-origin-width=&quot;1705&quot; data-origin-height=&quot;946&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1723&quot; data-origin-height=&quot;949&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bfqfu2/dJMcabLUUny/92acD484sZ8SVJka71khsK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bfqfu2/dJMcabLUUny/92acD484sZ8SVJka71khsK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bfqfu2/dJMcabLUUny/92acD484sZ8SVJka71khsK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbfqfu2%2FdJMcabLUUny%2F92acD484sZ8SVJka71khsK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1723&quot; height=&quot;949&quot; data-origin-width=&quot;1723&quot; data-origin-height=&quot;949&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1714&quot; data-origin-height=&quot;954&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dDBbY4/dJMcag7ooqy/5VjhvRBJROHgHGu4ZPrBGK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dDBbY4/dJMcag7ooqy/5VjhvRBJROHgHGu4ZPrBGK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dDBbY4/dJMcag7ooqy/5VjhvRBJROHgHGu4ZPrBGK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdDBbY4%2FdJMcag7ooqy%2F5VjhvRBJROHgHGu4ZPrBGK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1714&quot; height=&quot;954&quot; data-origin-width=&quot;1714&quot; data-origin-height=&quot;954&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1716&quot; data-origin-height=&quot;943&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/EZrnU/dJMcag7ooqL/IU8uwwpR8cxyjJszTl2K61/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/EZrnU/dJMcag7ooqL/IU8uwwpR8cxyjJszTl2K61/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/EZrnU/dJMcag7ooqL/IU8uwwpR8cxyjJszTl2K61/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FEZrnU%2FdJMcag7ooqL%2FIU8uwwpR8cxyjJszTl2K61%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1716&quot; height=&quot;943&quot; data-origin-width=&quot;1716&quot; data-origin-height=&quot;943&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1734&quot; data-origin-height=&quot;964&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/9Otob/dJMcagGq3cl/a6xwpknYPdUga44lsVElkk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/9Otob/dJMcagGq3cl/a6xwpknYPdUga44lsVElkk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/9Otob/dJMcagGq3cl/a6xwpknYPdUga44lsVElkk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F9Otob%2FdJMcagGq3cl%2Fa6xwpknYPdUga44lsVElkk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1734&quot; height=&quot;964&quot; data-origin-width=&quot;1734&quot; data-origin-height=&quot;964&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1729&quot; data-origin-height=&quot;960&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b3bACi/dJMcajpq8Ng/6jak5VkmC4EKtxf2PamV31/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b3bACi/dJMcajpq8Ng/6jak5VkmC4EKtxf2PamV31/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b3bACi/dJMcajpq8Ng/6jak5VkmC4EKtxf2PamV31/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb3bACi%2FdJMcajpq8Ng%2F6jak5VkmC4EKtxf2PamV31%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1729&quot; height=&quot;960&quot; data-origin-width=&quot;1729&quot; data-origin-height=&quot;960&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1729&quot; data-origin-height=&quot;970&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cxAnqh/dJMcajpq8NL/KKRKHS2mtpi9LUZpgV7vD0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cxAnqh/dJMcajpq8NL/KKRKHS2mtpi9LUZpgV7vD0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cxAnqh/dJMcajpq8NL/KKRKHS2mtpi9LUZpgV7vD0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcxAnqh%2FdJMcajpq8NL%2FKKRKHS2mtpi9LUZpgV7vD0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1729&quot; height=&quot;970&quot; data-origin-width=&quot;1729&quot; data-origin-height=&quot;970&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1725&quot; data-origin-height=&quot;963&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/eHu3HT/dJMcafHvUxP/lMHrEAETLueiqWvx4kAKb0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/eHu3HT/dJMcafHvUxP/lMHrEAETLueiqWvx4kAKb0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/eHu3HT/dJMcafHvUxP/lMHrEAETLueiqWvx4kAKb0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FeHu3HT%2FdJMcafHvUxP%2FlMHrEAETLueiqWvx4kAKb0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1725&quot; height=&quot;963&quot; data-origin-width=&quot;1725&quot; data-origin-height=&quot;963&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1734&quot; data-origin-height=&quot;972&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Tehrp/dJMcafHvUxV/eEKFtrk0RMzcmU5EgMaIZK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Tehrp/dJMcafHvUxV/eEKFtrk0RMzcmU5EgMaIZK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Tehrp/dJMcafHvUxV/eEKFtrk0RMzcmU5EgMaIZK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FTehrp%2FdJMcafHvUxV%2FeEKFtrk0RMzcmU5EgMaIZK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1734&quot; height=&quot;972&quot; data-origin-width=&quot;1734&quot; data-origin-height=&quot;972&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1705&quot; data-origin-height=&quot;961&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/oWSCv/dJMcaiEcaBe/yZm7MOKkCsLwzXikR9L7b0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/oWSCv/dJMcaiEcaBe/yZm7MOKkCsLwzXikR9L7b0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/oWSCv/dJMcaiEcaBe/yZm7MOKkCsLwzXikR9L7b0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FoWSCv%2FdJMcaiEcaBe%2FyZm7MOKkCsLwzXikR9L7b0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1705&quot; height=&quot;961&quot; data-origin-width=&quot;1705&quot; data-origin-height=&quot;961&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <author>blog13869</author>
      <guid isPermaLink="true">https://blog13869.tistory.com/70</guid>
      <comments>https://blog13869.tistory.com/70#entry70comment</comments>
      <pubDate>Fri, 24 Jul 2026 21:02:07 +0900</pubDate>
    </item>
    <item>
      <title>7/23 TIL</title>
      <link>https://blog13869.tistory.com/69</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;[ PROJECT F ]&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이게 코드 구현 마감을 마치고 제출용 영상 편집, PPT 작성에 이르렀다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;PPT&lt;/p&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://www.miricanvas.com/v2/ko/design2/f0b3ab78-c1d8-4a38-9654-ede99a16dbdb?location=design&amp;amp;type=copy_link&amp;amp;access=link&amp;amp;permission=editor&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://www.miricanvas.com/v2/ko/design2/f0b3ab78-c1d8-4a38-9654-ede99a16dbdb?location=design&amp;amp;type=copy_link&amp;amp;access=link&amp;amp;permission=editor&lt;/a&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;영상은 길이도 길고 아직 완성 전이라 내일 오전중으로 제출 예정이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;나는 노션 브로셔 템플릿을 작성하였다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1108&quot; data-origin-height=&quot;1246&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bfDEKX/dJMcai49cQh/XFVbdZ9cbVTPs7wSp5rzt1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bfDEKX/dJMcai49cQh/XFVbdZ9cbVTPs7wSp5rzt1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bfDEKX/dJMcai49cQh/XFVbdZ9cbVTPs7wSp5rzt1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbfDEKX%2FdJMcai49cQh%2FXFVbdZ9cbVTPs7wSp5rzt1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1108&quot; height=&quot;1246&quot; data-origin-width=&quot;1108&quot; data-origin-height=&quot;1246&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;</description>
      <author>blog13869</author>
      <guid isPermaLink="true">https://blog13869.tistory.com/69</guid>
      <comments>https://blog13869.tistory.com/69#entry69comment</comments>
      <pubDate>Thu, 23 Jul 2026 21:03:26 +0900</pubDate>
    </item>
    <item>
      <title>7/22 TIL</title>
      <link>https://blog13869.tistory.com/68</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;[ PROJECT F ]&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;오늘로 모든 구현 작업을 끝내고 패키징을 완료했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;각자 구현하기로 한 내용들을 모두 구현을 완료했고 성공적으로 마쳤다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2026-07-22 210057.png&quot; data-origin-width=&quot;921&quot; data-origin-height=&quot;900&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/EUS2B/dJMcaf8pJn5/0WnObvfimWWeIf0HkjwwC0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/EUS2B/dJMcaf8pJn5/0WnObvfimWWeIf0HkjwwC0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/EUS2B/dJMcaf8pJn5/0WnObvfimWWeIf0HkjwwC0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FEUS2B%2FdJMcaf8pJn5%2F0WnObvfimWWeIf0HkjwwC0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;921&quot; height=&quot;900&quot; data-filename=&quot;스크린샷 2026-07-22 210057.png&quot; data-origin-width=&quot;921&quot; data-origin-height=&quot;900&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1971&quot; data-origin-height=&quot;1279&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bTKdGx/dJMcaaMTuHD/Wg5VMhFfJQXrNXs6RU7zEk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bTKdGx/dJMcaaMTuHD/Wg5VMhFfJQXrNXs6RU7zEk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bTKdGx/dJMcaaMTuHD/Wg5VMhFfJQXrNXs6RU7zEk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbTKdGx%2FdJMcaaMTuHD%2FWg5VMhFfJQXrNXs6RU7zEk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1971&quot; height=&quot;1279&quot; data-origin-width=&quot;1971&quot; data-origin-height=&quot;1279&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;</description>
      <author>blog13869</author>
      <guid isPermaLink="true">https://blog13869.tistory.com/68</guid>
      <comments>https://blog13869.tistory.com/68#entry68comment</comments>
      <pubDate>Wed, 22 Jul 2026 21:02:10 +0900</pubDate>
    </item>
    <item>
      <title>7/20 TIL</title>
      <link>https://blog13869.tistory.com/67</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;[ PROJECT F ]&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;오늘의 구현 목록&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Grenade&amp;nbsp;쿨타임&amp;nbsp;호출용&amp;nbsp;함수&amp;nbsp;추가&lt;/b&gt;&lt;/p&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;[ PFPlayerController ]&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&amp;lt; 헤더 파일 선언 &amp;gt;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre class=&quot;cpp&quot;&gt;&lt;code&gt;void UpdateGrenadeCooldownUI(float GrenadeCooldown);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&amp;lt; 소스 파일 구현 &amp;gt;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre class=&quot;cpp&quot;&gt;&lt;code&gt;void APFPlayerController::UpdateGrenadeCooldownUI(float GrenadeCooldown)
{
    if (HUDWidgetInstance == nullptr) return;
    FOutputDeviceNull Ar;
    FString Command = FString::Printf(TEXT(&quot;UpdateGrenadeCooldownUI %f&quot;), GrenadeCooldown);
    HUDWidgetInstance-&amp;gt;CallFunctionByNameWithArguments(*Command, Ar, nullptr, true);
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;[ PFCombatComponent ] // 작성 요쳥&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&amp;lt; 헤더 파일 선언 &amp;gt;&lt;/b&gt;&lt;/p&gt;
&lt;pre class=&quot;cpp&quot;&gt;&lt;code&gt;protected:
    
    UPROPERTY(EditAnywhere, Category = &quot;Combat&quot;)
    float GrenadeCooldown = 5.0f;

  
    bool bIsGrenadeOnCooldown = false;

 
    FTimerHandle GrenadeCooldownTimerHandle;

    
    void ResetGrenadeCooldown() { bIsGrenadeOnCooldown = false; };&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&amp;lt; 소스 파일 구현 &amp;gt;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre class=&quot;livescript&quot;&gt;&lt;code&gt;void UPFCombatComponent::ExecuteThrow()
{
    AActor* Owner = GetOwner();
    ACharacter* Character = Cast&amp;lt;ACharacter&amp;gt;(Owner);
    
    if (!Character || !GrenadeClass || GrenadeCount &amp;lt;= 0 || bIsGrenadeOnCooldown) return;

    if (HandGrenadeMesh)
    {
        HandGrenadeMesh-&amp;gt;SetVisibility(false);
    }

    GrenadeCount--;

    FVector SpawnLocation = Character-&amp;gt;GetMesh()-&amp;gt;GetSocketLocation(HandSocketName); 
    FRotator SpawnRotation = Character-&amp;gt;GetControlRotation();

    FActorSpawnParameters SpawnParams;
    SpawnParams.Owner = Owner;
    SpawnParams.Instigator = Character;

    GetWorld()-&amp;gt;SpawnActor&amp;lt;APFGrenade&amp;gt;(GrenadeClass, SpawnLocation, SpawnRotation, SpawnParams);

    bIsGrenadeOnCooldown = true;

    APFPlayerController* PlayerController = Cast&amp;lt;APFPlayerController&amp;gt;(Character-&amp;gt;GetController());
    if (PlayerController)
    {
        PlayerController-&amp;gt;UpdateGrenadeCooldownUI(GrenadeCooldown); 
    }

    GetWorld()-&amp;gt;GetTimerManager().SetTimer(
        GrenadeCooldownTimerHandle, 
        this, 
        &amp;amp;UPFCombatComponent::ResetGrenadeCooldown, 
        GrenadeCooldown, 
        false
    );
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;GameClear 함수 작성 및 최종 구현 완료&amp;nbsp;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&amp;lt; PFGameMode.cpp &amp;gt;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre class=&quot;xl&quot;&gt;&lt;code&gt;void APFGameMode::OnMonsterKilled()
{
    if (!PFGameState) return;
    PFGameState-&amp;gt;OnMonsterKilled();
    UE_LOG(LogTemp, Log, TEXT(&quot;몬스터 처치! 남은 몬스터: %d&quot;), PFGameState-&amp;gt;GetRemainingMonsters());

    if (PFGameState-&amp;gt;GetRemainingMonsters() &amp;lt;= 0)
    {
       //   4레벨 3웨이브(최종 보스)를 잡았는지 확인
       if (PFGameState-&amp;gt;GetCurrentLevel() == 4 &amp;amp;&amp;amp; PFGameState-&amp;gt;GetCurrentWave() == 3)
       {
          APFPlayerController* PlayerController = Cast&amp;lt;APFPlayerController&amp;gt;(GetWorld()-&amp;gt;GetFirstPlayerController());
          if (PlayerController)
          {
             PlayerController-&amp;gt;ShowGameOver(true); // true = 게임 클리어!
          } // true = 승리/게임클리어!
       }
       else
       {
          WaveClear(); // 다음 웨이브 진행
       }
    }
    
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre class=&quot;livescript&quot;&gt;&lt;code&gt;void APFGameMode::GameOver()
{
    GetWorldTimerManager().ClearTimer(PFTimerHandle);
    UE_LOG(LogTemp, Warning, TEXT(&quot;Game Over!&quot;));

    if (APFPlayerController* PlayerController = Cast&amp;lt;APFPlayerController&amp;gt;(GetWorld()-&amp;gt;GetFirstPlayerController()))
    {
       PlayerController-&amp;gt;ShowGameOver(false);
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&amp;lt; PFPlayerController.h &amp;gt;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre class=&quot;lisp&quot;&gt;&lt;code&gt;UPROPERTY(EditDefaultsOnly, Category = &quot;UI&quot;)
TSubclassOf&amp;lt;UUserWidget&amp;gt; GameClearWidgetClass;
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&amp;nbsp;&amp;lt; PFPlayerController.cpp &amp;gt;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre class=&quot;reasonml&quot;&gt;&lt;code&gt;void APFPlayerController::ShowGameOver(bool bIsClear)
{
    if (HUDWidgetInstance) 
    {
     HUDWidgetInstance-&amp;gt;RemoveFromParent(); 
     HUDWidgetInstance = nullptr; 
    }
     
    // bIsClear 조건에 따라 띄울 위젯 클래스 결정 
    TSubclassOf&amp;lt;UUserWidget&amp;gt; TargetWidgetClass = bIsClear ? GameClearWidgetClass : GameOverWidgetClass;
    
    // 위젯 생성 및 화면 표시
    if (TargetWidgetClass)
        {
            GameOverWidgetInstance = CreateWidget&amp;lt;UUserWidget&amp;gt;(this, TargetWidgetClass);
            if (GameOverWidgetInstance)
            {
                GameOverWidgetInstance-&amp;gt;AddToViewport(); 

                // 마우스 커서 활성화 및 UI 입력 모드로 전환
                bShowMouseCursor = true; 
                FInputModeUIOnly InputModeData;
                InputModeData.SetWidgetToFocus(GameOverWidgetInstance-&amp;gt;TakeWidget());
                SetInputMode(InputModeData);
            }
        }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&amp;lt; BP_PFPlayerController &amp;gt;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1582&quot; data-origin-height=&quot;300&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b1Zwrv/dJMcaaF9wGJ/LVuH1UKKCExkiw0dn2BOf1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b1Zwrv/dJMcaaF9wGJ/LVuH1UKKCExkiw0dn2BOf1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b1Zwrv/dJMcaaF9wGJ/LVuH1UKKCExkiw0dn2BOf1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb1Zwrv%2FdJMcaaF9wGJ%2FLVuH1UKKCExkiw0dn2BOf1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1582&quot; height=&quot;300&quot; data-origin-width=&quot;1582&quot; data-origin-height=&quot;300&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/div&gt;
&lt;/div&gt;</description>
      <author>blog13869</author>
      <guid isPermaLink="true">https://blog13869.tistory.com/67</guid>
      <comments>https://blog13869.tistory.com/67#entry67comment</comments>
      <pubDate>Mon, 20 Jul 2026 19:44:34 +0900</pubDate>
    </item>
    <item>
      <title>7/15 TIL</title>
      <link>https://blog13869.tistory.com/66</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;[ PROJECT F ]&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;오늘의 구현 목록&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;[ 수류탄 코드 구현 ]&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;lt; PFGrenade &amp;gt;&lt;/p&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;lt; 헤더 파일 &amp;gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre class=&quot;cpp&quot;&gt;&lt;code&gt;// Fill out your copyright notice in the Description page of Project Settings.

#pragma once

#include &quot;CoreMinimal.h&quot;
#include &quot;GameFramework/GameModeBase.h&quot;
#include &quot;PFGameMode.generated.h&quot;

/**
 * 
 */
class APFGameState;
class APFLevelPortal;
class APFBaseItem;

UCLASS()
class PROJECTF_API APFGameMode : public AGameModeBase
{
    GENERATED_BODY()
    
public:
    
    APFGameMode();
    
    void OnMonsterKilled();
    
    void ProceedToNextLevel();
    
    // 게임오버 이동
    void GameOver();
    
    UFUNCTION(BlueprintCallable, Category = &quot;Item|Drop&quot;)
    void HandleMonsterDeath(const FVector&amp;amp; SpawnLocation);
    
    UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = &quot;UI&quot;)
    TSubclassOf&amp;lt;class UUserWidget&amp;gt; LoadingWidgetClass;
    
    UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = &quot;Item|Classes&quot;)
    TSubclassOf&amp;lt;APFBaseItem&amp;gt; HealingItemClass;

    UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = &quot;Item|Classes&quot;)
    TSubclassOf&amp;lt;APFBaseItem&amp;gt; PistolAmmoClass;

    UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = &quot;Item|Classes&quot;)
    TSubclassOf&amp;lt;APFBaseItem&amp;gt; RifleAmmoClass;

    UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = &quot;Item|Classes&quot;)
    TSubclassOf&amp;lt;APFBaseItem&amp;gt; GrenadeItemClass;

    // 기본 드랍 확률 (20%)
    UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = &quot;Item|DropSettings&quot;)
    float OverallDropChance = 0.2f;
    
    void RegisterLevelPortal(class APFLevelPortal* Portal) { LevelPortalInstance = Portal; }
    
protected:
    
    virtual void BeginPlay() override;
    
    void TimerCountDown();
    void StartWave();
    void WaveClear();
    void LevelClear();
    
    
    
private:
    
    void SpawnRandomItem(const FVector&amp;amp; SpawnLocation, int32 CurrentLevel);
    
    UPROPERTY(VisibleAnywhere, Category = &quot;Game Data&quot;)
    APFGameState* PFGameState;
    
    FTimerHandle PFTimerHandle;
    
    UPROPERTY()
    class APFWaveManager* PFWaveManager;
    UPROPERTY()
    class APFSpawnManager* PFSpawnManager;
    
    UPROPERTY(EditAnywhere, Category = &quot;Wave Data&quot;)
    UDataTable* WaveDataTableAsset;
    
    class APFLevelPortal* LevelPortalInstance;
};&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;lt; 소스 파일 &amp;gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre class=&quot;xl&quot;&gt;&lt;code&gt;#include &quot;Weapon/PFGrenade.h&quot;
#include &quot;Components/SphereComponent.h&quot;
#include &quot;Components/StaticMeshComponent.h&quot;
#include &quot;GameFramework/ProjectileMovementComponent.h&quot;
#include &quot;Kismet/GameplayStatics.h&quot; 
#include &quot;NiagaraComponent.h&quot; 
#include &quot;DrawDebugHelpers.h&quot;
#include &quot;Monster/PFMonsterCharacter.h&quot;


APFGrenade::APFGrenade()
{
    PrimaryActorTick.bCanEverTick = false;

    CollisionComponent = CreateDefaultSubobject&amp;lt;USphereComponent&amp;gt;(TEXT(&quot;SphereComponent&quot;));
    CollisionComponent-&amp;gt;InitSphereRadius(10.0f);
    CollisionComponent-&amp;gt;SetCollisionProfileName(TEXT(&quot;BlockAll&quot;));
    RootComponent = CollisionComponent;

    GrenadeMesh = CreateDefaultSubobject&amp;lt;UStaticMeshComponent&amp;gt;(TEXT(&quot;GrenadeMesh&quot;));
    GrenadeMesh-&amp;gt;SetupAttachment(RootComponent);
    
    ExplosionSphereComponent = CreateDefaultSubobject&amp;lt;USphereComponent&amp;gt;(TEXT(&quot;ExplosionSphereComponent&quot;));
    ExplosionSphereComponent-&amp;gt;SetupAttachment(RootComponent);
    ExplosionSphereComponent-&amp;gt;InitSphereRadius(400.0f);
    ExplosionSphereComponent-&amp;gt;SetCollisionEnabled(ECollisionEnabled::QueryOnly);
    ExplosionSphereComponent-&amp;gt;SetCollisionProfileName(TEXT(&quot;OverlapAllDynamic&quot;));

    ExplosionEffect = CreateDefaultSubobject&amp;lt;UNiagaraComponent&amp;gt;(TEXT(&quot;ExplosionEffect&quot;));
    ExplosionEffect-&amp;gt;SetupAttachment(RootComponent);
    ExplosionEffect-&amp;gt;bAutoActivate = false;
    
    ProjectileMovement = CreateDefaultSubobject&amp;lt;UProjectileMovementComponent&amp;gt;(TEXT(&quot;ProjectileMovement&quot;));
    ProjectileMovement-&amp;gt;UpdatedComponent = CollisionComponent;
    ProjectileMovement-&amp;gt;InitialSpeed = 1500.f;
    ProjectileMovement-&amp;gt;MaxSpeed = 1500.f;
    ProjectileMovement-&amp;gt;bRotationFollowsVelocity = true;
    ProjectileMovement-&amp;gt;bShouldBounce = true;
    ProjectileMovement-&amp;gt;Bounciness = 0.3f;
}


void APFGrenade::BeginPlay()
{
    Super::BeginPlay();
    // 타이머 설정
    GetWorldTimerManager().SetTimer(ExplodeTimerHandle, this, &amp;amp;APFGrenade::Explode, FuseTime, false);
}



void APFGrenade::Explode()
{
    FVector ExplodeLocation = GetActorLocation();

    // 나이아가라 이펙트 on(활성화)
    if (ExplosionEffect)
    {
       ExplosionEffect-&amp;gt;DetachFromComponent(FDetachmentTransformRules::KeepWorldTransform);
       ExplosionEffect-&amp;gt;Activate(true);
    }

    // 청각 효과 (사운드 재생)
    if (ExplosionSound)
    {
       UGameplayStatics::PlaySoundAtLocation(GetWorld(), ExplosionSound, ExplodeLocation);
    }

    if (ExplosionSphereComponent)
    {
       TArray&amp;lt;AActor*&amp;gt; OverlappingActors;
        
       //  ExplosionSphereComponent 이랑 overlap된 액터 탐색
       ExplosionSphereComponent-&amp;gt;GetOverlappingActors(OverlappingActors);

       for (AActor* HitActor : OverlappingActors)
       {
          if (HitActor)
          {
             // 몬스터만 피격
             APFMonsterCharacter* Monster = Cast&amp;lt;APFMonsterCharacter&amp;gt;(HitActor);
             if (Monster)
             {
                Monster-&amp;gt;TakeDamageFromMonster(ExplosionDamage, GetOwner());
                    
                UE_LOG(LogTemp, Log, TEXT(&quot;[Grenade] %s 몬스터에게 수류탄 데미지 %f 적용 완료!&quot;), 
                   *Monster-&amp;gt;GetName(), ExplosionDamage);
             }
          }
       }
    }

    // 이후 처리
    if (GrenadeMesh)
    {
       GrenadeMesh-&amp;gt;SetHiddenInGame(true);
    }
    
    if (CollisionComponent)
    {
       CollisionComponent-&amp;gt;SetCollisionEnabled(ECollisionEnabled::NoCollision);
    }
    
    if (ProjectileMovement)
    {
       ProjectileMovement-&amp;gt;StopMovementImmediately();
    }

    FTimerHandle DestroyTimerHandle;
    GetWorldTimerManager().SetTimer(DestroyTimerHandle, [this]()
       {this-&amp;gt;Destroy();}, 5.0f, false);
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;lt; 이 후 BP 래핑 &amp;gt;&lt;/p&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;192&quot; data-origin-height=&quot;304&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/mG6jq/dJMcafm5T2c/K6FJ1iiav5KPpSm9AQxuU1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/mG6jq/dJMcafm5T2c/K6FJ1iiav5KPpSm9AQxuU1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/mG6jq/dJMcafm5T2c/K6FJ1iiav5KPpSm9AQxuU1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FmG6jq%2FdJMcafm5T2c%2FK6FJ1iiav5KPpSm9AQxuU1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;192&quot; height=&quot;304&quot; data-origin-width=&quot;192&quot; data-origin-height=&quot;304&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;lt; 수류탄 효과 (TakeDamageFromMonster)는 적용되는데 나이아가라 이펙트가 나오지않는 이슈 발생 &amp;gt;&lt;/p&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;해결 방법&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2026-07-15 192344.png&quot; data-origin-width=&quot;649&quot; data-origin-height=&quot;376&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/tydh8/dJMcag0t5JK/vKX8aCe1L1ySkiEhsB75R0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/tydh8/dJMcag0t5JK/vKX8aCe1L1ySkiEhsB75R0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/tydh8/dJMcag0t5JK/vKX8aCe1L1ySkiEhsB75R0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Ftydh8%2FdJMcag0t5JK%2FvKX8aCe1L1ySkiEhsB75R0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;649&quot; height=&quot;376&quot; data-filename=&quot;스크린샷 2026-07-15 192344.png&quot; data-origin-width=&quot;649&quot; data-origin-height=&quot;376&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;

&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;CombatComponent 에서의 Grenade Class 를 작성한 BP_PFGrenade_N으로 교체&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;lt; 테스트 화면 &amp;gt;&lt;/p&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2026-07-15 192235.png&quot; data-origin-width=&quot;586&quot; data-origin-height=&quot;397&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/6uWcJ/dJMcaaeZN84/zSs57UhVQscuOWyJolv59K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/6uWcJ/dJMcaaeZN84/zSs57UhVQscuOWyJolv59K/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/6uWcJ/dJMcaaeZN84/zSs57UhVQscuOWyJolv59K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F6uWcJ%2FdJMcaaeZN84%2FzSs57UhVQscuOWyJolv59K%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;586&quot; height=&quot;397&quot; data-filename=&quot;스크린샷 2026-07-15 192235.png&quot; data-origin-width=&quot;586&quot; data-origin-height=&quot;397&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;[ 몬스터 스폰 포탈, Level포탈/보스 포탈 나이아가라 이펙트 수정 ]&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2026-07-15 193047.png&quot; data-origin-width=&quot;988&quot; data-origin-height=&quot;745&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bEsfga/dJMcagsHOmG/MHcI4MWNOZHVXO5yC5wIOk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bEsfga/dJMcagsHOmG/MHcI4MWNOZHVXO5yC5wIOk/img.png&quot; data-alt=&quot;몬스터 포탈&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bEsfga/dJMcagsHOmG/MHcI4MWNOZHVXO5yC5wIOk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbEsfga%2FdJMcagsHOmG%2FMHcI4MWNOZHVXO5yC5wIOk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;662&quot; height=&quot;499&quot; data-filename=&quot;스크린샷 2026-07-15 193047.png&quot; data-origin-width=&quot;988&quot; data-origin-height=&quot;745&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;몬스터 포탈&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2026-07-15 193116.png&quot; data-origin-width=&quot;658&quot; data-origin-height=&quot;547&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/VW6Mg/dJMcafObx1C/UkTgPtmk00UK0KBz1ogkPK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/VW6Mg/dJMcafObx1C/UkTgPtmk00UK0KBz1ogkPK/img.png&quot; data-alt=&quot;보스/레벨 포탈&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/VW6Mg/dJMcafObx1C/UkTgPtmk00UK0KBz1ogkPK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FVW6Mg%2FdJMcafObx1C%2FUkTgPtmk00UK0KBz1ogkPK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;658&quot; height=&quot;547&quot; data-filename=&quot;스크린샷 2026-07-15 193116.png&quot; data-origin-width=&quot;658&quot; data-origin-height=&quot;547&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;보스/레벨 포탈&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <author>blog13869</author>
      <guid isPermaLink="true">https://blog13869.tistory.com/66</guid>
      <comments>https://blog13869.tistory.com/66#entry66comment</comments>
      <pubDate>Wed, 15 Jul 2026 19:39:16 +0900</pubDate>
    </item>
  </channel>
</rss>