<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>kim_yw.log</title>
        <link>https://velog.io/</link>
        <description>게임 프로그래머</description>
        <lastBuildDate>Fri, 09 May 2025 12:40:48 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <image>
            <title>kim_yw.log</title>
            <url>https://velog.velcdn.com/images/kim_yw/profile/5bedfb84-b5bd-4e30-9307-255faeea9da8/image.jpg</url>
            <link>https://velog.io/</link>
        </image>
        <copyright>Copyright (C) 2019. kim_yw.log. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/kim_yw" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[타일맵 생성]]></title>
            <link>https://velog.io/@kim_yw/%ED%83%80%EC%9D%BC%EB%A7%B5-%EC%83%9D%EC%84%B1</link>
            <guid>https://velog.io/@kim_yw/%ED%83%80%EC%9D%BC%EB%A7%B5-%EC%83%9D%EC%84%B1</guid>
            <pubDate>Fri, 09 May 2025 12:40:48 GMT</pubDate>
            <description><![CDATA[<p>프로젝트에서 타일맵을 만들 일이 있어서 정리를 할 겸 ChildActorComponent로 만들려고했다.
처음에는 AddComponentByClass를 사용하려고 했지만 문제가 많아서 사용하지 않기로 했다.</p>
<p>그 이유는 나는 UFUNCTION의 CallInEditor를 이용해 버튼으로 만들어 생성하려고 했는데, 이 버튼이 Blueprint에서는 정상 작동 하지 않았다.
그래서 Level에 배치해서 사용하는 수 밖에 없었는데, 이렇게 했더니 처음엔 추가가 됬지만 오브젝트가 움직인다던가 설정값이 바뀌면 모두 초기화되어 사라졌다.</p>
<p>이런 문제들 때문에 AI와 인터넷의 도움을 받아 찾아본 결과 NewObject를 이용하면 된다고 했다.
NewObject를 이용해 생성했지만, 이번엔 아웃라이너에선 생성되었다고 뜨는데 디테일에선 안뜨고, 아웃라이너든 디테일이든 선택할 수 없는 문제가 생겼다.
인터넷 서핑을 해보니 <code>NewChildActorComp-&gt;CreationMethod = EComponentCreationMethod::Instance;</code>를 붙이면 된다고 해서 붙였더니 해결되었다.</p>
<pre><code class="language-cpp">void ARSTileMap::CreateTilesWithChildActorComponent()
{
    RS_LOG_F(&quot;%d x %d Tile 생성&quot;, Width, Height)

    check(DefaultTileType)

    if (TileParent-&gt;GetNumChildrenComponents() &gt; 0)
    {
        auto&amp; ChildCompArray = TileParent-&gt;GetAttachChildren();
        int Num = ChildCompArray.Num();

        for (int32 i = 0; i &lt; Num; i++)
        {
            auto&amp; Child = ChildCompArray[0];
            if (Child &amp;&amp; !Child-&gt;IsBeingDestroyed())
            {
                Child-&gt;DestroyComponent();
            }
        }
    }

    FVector TileSize = DefaultTileType.GetDefaultObject()-&gt;GetTileSize();

    for (int32 i = 0; i &lt; Height; i++)
    {
        for (int32 j = 0; j &lt; Width; j++)
        {
            FName TileName = FName(FString::Printf(TEXT(&quot;Tile %d x %d&quot;), i, j));

            //새로운 UChildActorComponent를 생성
            UChildActorComponent* NewChildActorComp = NewObject&lt;UChildActorComponent&gt;(this, TileName);
            NewChildActorComp-&gt;SetChildActorClass(DefaultTileType);

            //부모를 설정 후 컴포넌트를 등록
            NewChildActorComp-&gt;SetupAttachment(TileParent);
            NewChildActorComp-&gt;CreationMethod = EComponentCreationMethod::Instance;
            NewChildActorComp-&gt;RegisterComponent();

            FVector Location = FVector::ZeroVector;
            Location.Y += TileSize.Y * j;
            Location.X += TileSize.X * i;

            NewChildActorComp-&gt;SetRelativeLocation(Location);
        }
    }
}</code></pre>
<p>그런데 여기서 심각한 문제가 발견된다.</p>
<p>Level의 프로퍼티를 수정해서 그런건지 모르겠지만 가끔 생성되었던 ChildActor들이 모두 날아갔다
그리고 블루프린트를 컴파일을 누르는 순간 ChildActor들이 모두 날아갔다</p>
<p>맵을 다시 로딩하면 생기기는 했지만 너무나도 불안한 형태인게 보여서 튜터분께 질문했다.</p>
<p>튜터분이 보기에도 확실히 너무나도 불안한 형태이고, ChildActorComponent는 패키징 문제 등 문제가 많은 Component라고 한다.
차라리 에디터에서 생성하는걸 포기하고 플레이 때 동적으로 생성하는게 더 좋아보인다고 해셔서 확실히 그런 것 같아 동적으로 바꿨다.</p>
<pre><code class="language-cpp">void ARSTileMap::CreateTilesWithSpawnActor()
{
    RS_LOG_F(&quot;%d x %d Tile 생성&quot;, Width, Height)

    check(DefaultTileType)

    //임시, 타일 전체 삭제
    if (Tiles.Num() &gt; 0)
    {
        for (auto&amp; Tile : Tiles)
        {
            if (Tile.Get())
            {
                Tile-&gt;Destroy();
            }
        }

        Tiles.Empty();
    }

    FVector TileSize = DefaultTileType.GetDefaultObject()-&gt;GetTileSize();
    FVector StartLocation = GetActorLocation();

    for (int32 i = 0; i &lt; Height; i++)
    {
        for (int32 j = 0; j &lt; Width; j++)
        {
            ARSBaseTile* TileActor = GetWorld()-&gt;SpawnActor&lt;ARSBaseTile&gt;(DefaultTileType);
            TileActor-&gt;SetActorLabel(FString::Printf(TEXT(&quot;Tile %d x %d&quot;), i, j));

            FVector Location = StartLocation;
            Location.X += TileSize.X * j;
            Location.Y += TileSize.Y * i;

            TileActor-&gt;SetActorLocation(Location);
            Tiles.Add(TileActor);
        }
    }
}</code></pre>
<p>확실히 구조가 간단한 만큼 문제가 생기지는 않을 것 같다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[프로세스와 스레드 - 1]]></title>
            <link>https://velog.io/@kim_yw/%ED%94%84%EB%A1%9C%EC%84%B8%EC%8A%A4%EC%99%80-%EC%8A%A4%EB%A0%88%EB%93%9C</link>
            <guid>https://velog.io/@kim_yw/%ED%94%84%EB%A1%9C%EC%84%B8%EC%8A%A4%EC%99%80-%EC%8A%A4%EB%A0%88%EB%93%9C</guid>
            <pubDate>Tue, 29 Apr 2025 12:09:13 GMT</pubDate>
            <description><![CDATA[<p>모의 면접을 준비해야되는 상황에서 주제와 맞는 매우 좋은 강의를 해서 하나씩 보면서 익혔다.</p>
<br>

<h1 id="프로세스">프로세스</h1>
<p>프로세스는 실행 중인 프로그램을 의미한다
하나의 프로세스는 실행을 위해 독립된 메모리 공간을 할당 받는다.
각 프로세스는 운영체제에 의해 독립적으로 관리되며, 하나의 프로그램이 여러 프로세스를 생성할 수 있다.</p>
<h2 id="프로세스의-구조">프로세스의 구조</h2>
<p>프로세스는 운영체제에서 실행되는 동안, 특정한 메모리 구조를 가진다.
프로세스의 메모리 공간은 다음과 같이 나눌 수 있다.</p>
<br>

<h3 id="텍스트-세그먼트코드-세그먼트">텍스트 세그먼트(코드 세그먼트)</h3>
<p>프로그램의 실행 코드를 저장한다.
텍스트 세그먼트는 읽기 전용이다.
C++로 작성된 코드는 컴파일 과정을 거쳐 기계어로 변환되어, 이 기계어 코드가 텍스트 세그먼트에 저장한다.
CPU는 텍스트 세그먼트에 저장된 명령을 가져와 실행한다.</p>
<h3 id="데이터-세그먼트">데이터 세그먼트</h3>
<p>데이터 세그먼트는 크게 2가지로 나누어진다.
초기화된 데이터와 초기화되지 않은 데이터이다.</p>
<ul>
<li><p>초기화된 데이터 세그먼트 (Initialized Data Segment)
초기화된 전역 변수나 정적 변수를 저장한다.
C++에서는 static 키워드를 사용한 변수들이 컴파일 시 초기값과 함께 저장되며, 프로그램 시작 시 메모리에 올라간다.</p>
</li>
<li><p>초기화되지 않은 테이터 세그먼트 (BSS, Block Started by Symbol)
초기화되지 않은 전역 변수나 정적 변수를 저장한다.
초기값이 명시되지 않은 전역 변수나 정적 변수는 프로그램 실행 시 자동으로 0으로 초기화되며, 이들은 BSS영역에 저장된다.</p>
</li>
</ul>
<h3 id="스택-세그먼트">스택 세그먼트</h3>
<p>함수 호출 시 생성되는 지역 변수와 매개 변수를 저장한다.
함수가 호출될 때마다 스택 프레임이 만들어지고 함수가 반환되면 스택에서 해당 프레임이 제거된다.
이러한 스택은 함수 호출이 끝나면 메모리가 자동으로 해제된다.</p>
<p>스택 오버플로우는 함수가 너무 많이 중첩 호출 되었을 때 발생할 수 있다.</p>
<h3 id="힙-세그먼트">힙 세그먼트</h3>
<p>동적 메모리 할당이 이루어지는 공간이다.
C++에선 주로 new로 생성된 객체들이 힙에 저장된다.
힙은 스택과 달리 수명이 관리되지 않기 때문에 메모리 누수가 발생할 수 있다.
언리얼에서는 가비지 컬렉션을 통해 일부 객체의 관리를 해줌</p>
<h3 id="가비지-컬렉션">가비지 컬렉션</h3>
<p>가비지 컬렉션(Garbage Collection)이란 프로그램에서 더 이상 필요 없는 메모리를 자동으로 정리(해제) 하는 시스템이다.</p>
<p>언리얼 엔진은 <code>UObject</code> 기반 클래스에 대해 Garbage Collection 시스템을 제공한다.
언리얼의 GC는 <code>UObject</code>를 상속한 객체들을 추적하여, 더 이상 참조되지 않는 객체를 메모리에서 안전하게 해제한다.</p>
<ul>
<li><p>언리얼의 GC가 관리하는 것</p>
<ul>
<li><code>UObject</code>를 상속한 객체</li>
<li>예시: <code>AActor</code>, <code>UActorComponent</code>, <code>UUserWidget</code>, <code>UTexture</code>, <code>UMaterial</code>, 등</li>
</ul>
</li>
<li><p>GC가 관리하지 않는 것</p>
<ul>
<li>일반 <code>C++ class</code> (<code>class MyClass {}</code> 처럼 <code>UCLASS()</code> 안 붙은 것)</li>
<li><code>new</code>, <code>malloc</code> 등으로 직접 할당한 메모리 (<code>int*</code>, <code>char*</code>, <code>std::vector</code> 등)</li>
<li><code>FVector</code>, <code>FString</code>, <code>TArray</code>, <code>TMap</code> (이런건 힙 기반이고 직접 관리해야 함)</li>
</ul>
</li>
</ul>
<p>언리얼의 GC는 객체를 참조하는 다른 객체가 없을 때 해당 객체를 메모리에서 해제한다.
루트 객체(Root Set)부터 시작해서, 참조하고 있는 모든 객체를 추적(Mark)하고, 추적되지 않은(Unreachable) 객체를 정리(Sweep) 하는데 이 과정을 Mark-and-Sweep이라고 부른다.</p>
<p>GC는 게임 틱마다 돌아가는 게 아니라, 메모리 상황이나 GC 트리거 이벤트에 따라 주기적으로 실행된다. (예: 레벨 전환, 특정 조건 충족 시 등)</p>
<h3 id="추가적인-프로세스의-특징">추가적인 프로세스의 특징</h3>
<p>각 프로세스는 독립적인 주소 공간을 갖는다.
이는 하나의 프로세스가 다른 프로세스의 메모리에 직접 접근할 수 없다는 의미이다.</p>
<p>프로세스 간 데이터를 주고받기 위해서는 운영체제의 도움을 받아야 하며, 이를 프로세스 간 통신(Inter-process Communication, IPC)이라고 한다.
IPC 방식에는 파이프, 메시지 큐, 공유 메모리 등이 있다.</p>
<p>운영체제는 멀티태스킹을 지원하기 위해 여러 프로세스를 빠르게 전환하는데, 이를 컨텍스트 스위칭이라고 한다.
각 프로세스의 상태를 저장하고 복원하는 과정에서 오버헤드가 발생할 수 있다.</p>
<hr>
<h1 id="스레드">스레드</h1>
<p>스레드는 프로세스 내에서 실행의 흐름을 담당하는 단위이다.
하나의 프로세스에는 하나 이상의 스레드가 존재할 수 있으며, 이들은 같은 메모리 공간을 공유한다.</p>
<h3 id="스레드의-구조">스레드의 구조</h3>
<p>스레드는 같은 프로세스의 메모리 공간을 공유하지만, 각각 독립적인 스택을 가진다.
스레드 간에는 다음 메모리 공간을 공유한다.</p>
<ol>
<li>텍스트 세그먼트 : 스레드는 프로세스의 코드를 공유한다.</li>
<li>데이터 세그먼트 : 전역 변수와 정적 변수를 공유한다.</li>
<li>힙 : 동적으로 할당된 메모리를 공유한다.</li>
</ol>
<p>스레드는 같은 프로세스 내에서 자원을 공유하기 때문에 스레드간 전환은 프로세스 간 전환보다 오버헤드가 적다.
즉, 컨텍스트 스위칭이 더 빠르게 이루어진다.</p>
<p>하지만 스레드는 같은 메모리 공간을 공유하기 때문에 동시성 문제가 발생할 수 있다.
두 스레드가 동시에 같은 메모리 위치에 접근하면 경쟁 상태(Race condition)가 발생할 수 있다.
이를 방지하기 위해 락(Lock)이나 세마포어(Semaphore) 같은 동기화 기법이 필요하다.</p>
<h3 id="언리얼에서의-스레드">언리얼에서의 스레드</h3>
<p>기본적으로 메인 스레드(Main Thread)가 존재하며, 필요에 따라 추가적인 백그라운드 스레드(Worker Thread)를 생성하여 작업을 분산시킬 수 있다</p>
<table>
<thead>
<tr>
<th>스레드</th>
<th>역할</th>
<th>특징</th>
</tr>
</thead>
<tbody><tr>
<td>메인 스레드 (Game Thread)</td>
<td>게임 로직, 액터 갱신, 블루프린트 실행 등</td>
<td>대부분의 게임 코드가 실행</td>
</tr>
<tr>
<td>렌더 스레드 (Render Thread)</td>
<td>렌더링 명령 생성 및 최적화</td>
<td>메인 스레드와 분리되어 렌더링 처리</td>
</tr>
<tr>
<td>RHI 스레드 (RHI Thread)</td>
<td>GPU 드라이버 통신</td>
<td>GPU 명령 최적화 처리</td>
</tr>
<tr>
<td>오디오 스레드 (Audio Thread)</td>
<td>사운드 재생 및 처리</td>
<td>별도 스레드에서 실행</td>
</tr>
<tr>
<td>작업 스레드 (Worker Thread)</td>
<td>비동기 연산, 파일 로딩, 계산 등</td>
<td>필요할 때 생성하여 작업 수행</td>
</tr>
</tbody></table>
<p>언리얼 엔진은 멀티스레드로 동작한다.
메인 스레드 외에도 렌더 스레드, 오디오 스레드, RHI 스레드 등이 기본적으로 동작하고 있다
단, 게임 로직(액터 이동, 충돌 처리 등)은 주로 메인 스레드에서 실행된다.</p>
<br>

<h3 id="언리얼에서-스레드를-사용하는-방법">언리얼에서 스레드를 사용하는 방법</h3>
<ol>
<li><code>Async()</code> 함수 사용 (가장 간단한 방법)</li>
</ol>
<p>간단한 비동기 작업은 <code>Async()</code> 함수를 통해 쉽게 처리할 수 있다.</p>
<pre><code class="language-cpp">#include &quot;Async/Async.h&quot;

void StartAsyncTask()
{
    Async(EAsyncExecution::Thread, []()
    {
        UE_LOG(LogTemp, Log, TEXT(&quot;비동기 작업 실행 중입니다.&quot;));
    });
}</code></pre>
<p>EAsyncExecution::Thread 옵션을 통해 새로운 스레드에서 작업을 실행한다.
짧고 가벼운 비동기 작업에 적합하다.</p>
<br>

<ol start="2">
<li><code>FAsyncTask</code> 클래스 사용 (구조화된 비동기 작업)</li>
</ol>
<p>조금 더 복잡한 비동기 작업이 필요할 때는 <code>FAsyncTask</code>를 사용할 수 있다.</p>
<pre><code class="language-cpp">#include &quot;Async/AsyncWork.h&quot;

class FMyAsyncTask : public FNonAbandonableTask
{
public:
    void DoWork()
    {
        UE_LOG(LogTemp, Log, TEXT(&quot;FAsyncTask 작업 중입니다.&quot;));
    }

    FORCEINLINE TStatId GetStatId() const
    {
        RETURN_QUICK_DECLARE_CYCLE_STAT(FMyAsyncTask, STATGROUP_ThreadPoolAsyncTasks);
    }
};

void StartFAsyncTask()
{
    (new FAutoDeleteAsyncTask&lt;FMyAsyncTask&gt;())-&gt;StartBackgroundTask();
}</code></pre>
<p>작업 클래스(<code>FNonAbandonableTask</code>)를 상속하여 작업을 정의한다.
비동기 실행이 완료되면 자동으로 메모리가 정리된다</p>
<br>

<ol start="3">
<li><code>FRunnable</code>과 <code>FRunnableThread</code>를 사용한 직접 스레드 제어</li>
</ol>
<p>스레드를 직접 제어하고 싶을 경우, <code>FRunnable</code> 인터페이스를 구현하여 사용할 수 있다.</p>
<pre><code class="language-cpp">class FMyRunnable : public FRunnable
{
public:
    virtual uint32 Run() override
    {
        while (bRunning)
        {
            FPlatformProcess::Sleep(0.1f);
        }
        return 0;
    }

    void StopTask()
    {
        bRunning = false;
    }

private:
    bool bRunning = true;
};

FMyRunnable* MyRunnable = nullptr;
FRunnableThread* MyThread = nullptr;

void StartRunnable()
{
    MyRunnable = new FMyRunnable();
    MyThread = FRunnableThread::Create(MyRunnable, TEXT(&quot;MyRunnableThread&quot;));
}

void StopRunnable()
{
    if (MyRunnable)
    {
        MyRunnable-&gt;StopTask();
        MyThread-&gt;WaitForCompletion();
        delete MyRunnable;
        delete MyThread;
    }
}</code></pre>
<p>직접 스레드를 시작, 정지, 삭제까지 관리해야 한다.
복잡한 커스텀 스레드 작업이 필요한 경우 사용한다.</p>
<hr>
]]></description>
        </item>
        <item>
            <title><![CDATA[언리얼 AI - 1]]></title>
            <link>https://velog.io/@kim_yw/%EC%96%B8%EB%A6%AC%EC%96%BC-AI-1</link>
            <guid>https://velog.io/@kim_yw/%EC%96%B8%EB%A6%AC%EC%96%BC-AI-1</guid>
            <pubDate>Fri, 25 Apr 2025 12:40:20 GMT</pubDate>
            <description><![CDATA[<h2 id="navigation-invoker">Navigation Invoker</h2>
<p>언리얼의 NavMeshBoundsVolume은 Scale이 아니라 Brush 크기를 활용해 레벨 내 적용되는 지오메트리를 결정한다.
그런데 언리얼 엔진의 아키텍쳐 특성상 Brush는 런타임 중 동적 재생성이 불가능하다.
무슨 말이냐면 설계 상의 이유로 게임을 플레이하는 도중에 NavMeshBoundsVolume을 새로 생성하여 레벨 내 네비메시 적용범위를 늘리거나 줄이는 방식의 구현은 어렵다는 것이다.</p>
<p>이러면 AI를 맵 전체에 놔야하는 레벨 디자인이면 계속 네비메시를 연산해야할까?
성능을 엄청나게 잡아 먹을 것이다.</p>
<p>다행히 이러한 문제점을 고려해 언리얼 엔진은 Navigation Invoker라는 기능을 제공한다.
Navigation Invoker는 Navigation Invoker라는 컴포넌트를 가진 액터의 주변 영역만 연산해서 리소스를 아끼고, 게임 플레이는 문제없이 가능하게 하는 기능이다.</p>
<p><img src="https://velog.velcdn.com/images/kim_yw/post/7d5942f5-2c19-48b4-9452-f4dfd1448b39/image.png" alt=""></p>
<p>우선 엔진 -&gt; 네비게이션 메시를 들어가서 런타임 생성을 Dynamic으로 바꿔주고 </p>
<p><img src="https://velog.velcdn.com/images/kim_yw/post/a80fe86c-c04a-4c30-be99-5b9cce3c1f82/image.png" alt=""></p>
<p>엔진 -&gt; 네비게이션 시스템에 들어가서 <code>네비게이션 인보커 주변에만 네비게이션 생성(Generate Navigation Only Around Navigation Invokers)</code>를 켜준다.</p>
<p><img src="https://velog.velcdn.com/images/kim_yw/post/b2fc4f38-ee37-423d-83cb-5be41e85d054/image.png" alt=""></p>
<p>AI를 쓸 액터에 NavigationInvokerComponent를 추가해주면</p>
<p><img src="https://velog.velcdn.com/images/kim_yw/post/1fc13cde-e866-4b21-87af-bd84241a320a/image.png" alt=""></p>
<p>이렇게 한다면 위 사진과 같이 NavBounds는 범위가 크지만 Invoker가 있는 주변만 네비게이션을 연산한다.</p>
<hr>
<h2 id="경로-탐색">경로 탐색</h2>
<p>보통의 경로를 찾을 때 다익스트라 알고리즘 등과 같이 특정 경로로 이동하기 위한 비용 최적화 경로 탐색 공식을 바탕으로 경로를 탐색한다.
즉 언리얼의 경로 탐색은 “낮은 가격순”으로 선택하는 원리이다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[최적화, 해시 테이블]]></title>
            <link>https://velog.io/@kim_yw/%EC%B5%9C%EC%A0%81%ED%99%94-%ED%95%B4%EC%8B%9C-%ED%85%8C%EC%9D%B4%EB%B8%94</link>
            <guid>https://velog.io/@kim_yw/%EC%B5%9C%EC%A0%81%ED%99%94-%ED%95%B4%EC%8B%9C-%ED%85%8C%EC%9D%B4%EB%B8%94</guid>
            <pubDate>Wed, 23 Apr 2025 08:35:30 GMT</pubDate>
            <description><![CDATA[<p>Q. 배열이 인덱스 기반으로 접근 가능한데 왜 가능하냐
A. 배열은 동일한 데이터 타입의 값을 메모리에 연속으로 저장하기 때문에 입력한 인덱스 * 데이터 타입의 크기를 해 해당하는 메모리 주소에 접근하면 된다.</p>
<p>CPU는 한번에 일정 크기의 메모리(ex. 64byte)를 읽어오므로 순차 접근 시 캐시 미스가 줄어들어 성능이 향상됨</p>
<hr>
<h1 id="해시-테이블">해시 테이블</h1>
<p>주어진 키에 해시 함수를 적용해서 계산된 인덱스 위치에 값을 저장한다.
인덱스로 접근하므로 O(1)의 시간 복잡도를 가지게 된다</p>
<p>해시 함수는 같은 Key를 넣었을 때 반드시 같은 값이 반환되어야 한다는 조건이 있기 때문에 가능한 일이다.</p>
<p>우선 입력된 key를 해쉬 함수를 거쳐 고정 크기의 정수, 즉 해시 코드로 반환하고 이 값을 내부 배열(버킷)의 인덱스로 사용해 데이터를 저장한다.
간단한 예시를 들자면 index = hash(key) % table_size 이런 식으로 인덱스를 만들어 사용하게 된다.</p>
<p>이렇게 인덱스를 만들어서 저장하면 O(1)의 시간 복잡도를 가지게 되지만 현실적으로 해시 충돌(Collision)이 일어날 수 밖에 없다.
모든 해시 테이블은 반드시 충돌 해결 기법을 포함해야 한다.</p>
<p>해시 충돌의 해결 방법은 크게 2가지로 나눌 수 있다.</p>
<ul>
<li>체이닝(Chaining) 방식</li>
<li>오픈 어드레싱(Open Addressing) 방식</li>
</ul>
<h2 id="체이닝-방식">체이닝 방식</h2>
<p>이 방식에서는 동일한 해시 값을 가진 여러 키-값 쌍을 하나의 버킷(내부 배열)에 연결 리스트나 동적 배열과 같이 별도의 자료구조를 통해 저장한다.</p>
<p>체이닝 방식은 각 버킷에 여러 요소를 저장할 수 있어 테이블 전체의 크기와 상관없이 유연하게 데이터를 관리할 수 있는 장점이 있지만, 만약 충돌이 빈번해지면 해당 버킷 내에서 순차 탐색이 필요해지는 단점이 있다.</p>
<pre><code class="language-cpp">#include &lt;iostream&gt;
#include &lt;vector&gt;
#include &lt;string&gt;
#include &lt;cmath&gt;
#include &lt;fstream&gt;
#include &lt;Windows.h&gt;
#include &lt;algorithm&gt;
#include &lt;set&gt;
#include &lt;numeric&gt;
#include &lt;map&gt;
#include &lt;queue&gt;
#include &lt;unordered_map&gt;
#include &lt;stack&gt;

using namespace std;

const int TABLE_SIZE = 10;

class HashTable {
    vector&lt;list&lt;string&gt;&gt; table;

public:
    HashTable() : table(TABLE_SIZE) {}

    void Insert(const string&amp; key) 
    {
        int index = Hash(key);
        table[index].push_back(key);
    }

    void Display() 
    {
        for (int i = 0; i &lt; TABLE_SIZE; ++i) 
        {
            cout &lt;&lt; i &lt;&lt; &quot; : &quot;;

            for (const auto&amp; item : table[i]) 
            {
                cout &lt;&lt; item &lt;&lt; &quot; → &quot;;
            }

            cout &lt;&lt; &quot;nullptr\n&quot;;
        }
    }

private:
    int Hash(const string&amp; key) {
        int h = 0;
        for (char ch : key)
            h += ch;

        return h % TABLE_SIZE;
    }

};

int main()
{
    HashTable ht;
    ht.Insert(&quot;apple&quot;);
    ht.Insert(&quot;banana&quot;);
    ht.Insert(&quot;grape&quot;);
    ht.Insert(&quot;orange&quot;); // 일부는 같은 버킷으로 충돌할 수 있음
    ht.Display();
}</code></pre>
<h2 id="오픈-어드레싱-방식">오픈 어드레싱 방식</h2>
<p>이 방식은 충돌이 발생할 경우, 충돌이 발생한 버킷 외의 다른 빈 버킷을 찾아 데이터를 저장한다.
오픈 어드레싱에는 여러 기법이 존재하는데, 가장 단순한 선형 탐사(Linear Probing)는 충돌이 발생하면 다음 인덱스를 순차적으로 검사하여 빈 버킷을 찾는다.</p>
<p>그러나 이 방식은 동일한 해시 값 근처에 데이터가 몰려 발생하는 1차 군집(Primary Clustering) 문제를 일으킬 수 있다.</p>
<p>이를 보완하기 위해 제곱 탐사(Quadratic Probing) 방식이 사용되며, 이 방식은 충돌 발생 시 탐사 간격을 제곱수로 증가시켜 빈 버킷을 찾는다.</p>
<p>또 다른 기법인 이중 해싱(Double Hashing)은 두 개의 해시 함수를 사용하여 첫 번째 해시 함수로 기본 인덱스를 결정하고, 두 번째 해시 함수를 통해 탐사 간격을 정함으로써 보다 균등한 분포와 뛰어난 충돌 회피 성능을 제공한다.</p>
<p>아래는 선형 탐사 방식이다.</p>
<pre><code class="language-cpp">#include &lt;iostream&gt;
#include &lt;vector&gt;
#include &lt;string&gt;
#include &lt;cmath&gt;
#include &lt;fstream&gt;
#include &lt;Windows.h&gt;
#include &lt;algorithm&gt;
#include &lt;set&gt;
#include &lt;numeric&gt;
#include &lt;map&gt;
#include &lt;queue&gt;
#include &lt;unordered_map&gt;
#include &lt;stack&gt;

using namespace std;

const int TABLE_SIZE = 10;

class OpenAddressingHashTable
{
    vector&lt;string&gt; table;

public:
    OpenAddressingHashTable() : table(TABLE_SIZE, &quot;&quot;) {}

    void Insert(const string&amp; key) 
    {
        int index = Hash(key);
        int originalIndex = index;

        while (!table[index].empty()) 
        {
            index = (index + 1) % TABLE_SIZE;
            if (index == originalIndex) 
            {
                cout &lt;&lt; &quot;Table is full!\n&quot;;
                return;
            }
        }

        table[index] = key;
    }

    void Display() 
    {
        for (int i = 0; i &lt; TABLE_SIZE; ++i) 
        {
            cout &lt;&lt; i &lt;&lt; &quot; : &quot; &lt;&lt; (table[i].empty() ? &quot;empty&quot; : table[i]) &lt;&lt; &quot;\n&quot;;
        }
    }

private:
    int Hash(const std::string&amp; key)
    {
        int h = 0;
        for (char ch : key)
            h += ch;

        return h % TABLE_SIZE;
    }

};

int main()
{
    OpenAddressingHashTable ht;
    ht.Insert(&quot;apple&quot;);
    ht.Insert(&quot;banana&quot;);
    ht.Insert(&quot;grape&quot;);
    ht.Insert(&quot;orange&quot;);
    ht.Display();
    return 0;
}</code></pre>
<br>
<br>
<br>

<p>해시 테이블은 키 기반 데이터 접근을 매우 빠르게 수행할 수 있다는 점에서 큰 장점을 가진다.
평균적으로 검색, 삽입, 삭제 연산이 O(1)의 시간 복잡도를 보장하므로, 객체 ID 매핑, 캐싱, 설정 값 저장 등 빠른 데이터 접근이 필요한 응용 분야에서 특히 유용하다.</p>
<p>하지만 해시 함수의 품질에 따라서 전체 성능이 크게 좌우될 수 있으며, 키의 분포가 고르지 않다면 속도가 매우 느려질 수 있다.</p>
<p>그리고 해시 테이블은 내부적으로 해시 버킷과 충돌 해결을 위한 추가 자료구조(예를 들어 체이닝 방식의 연결 리스트)를 사용하므로, 메모리 오버헤드가 발생한다.</p>
<p>데이터가 많아지면 해시 테이블의 크기를 동적으로 조절하기 위해 재해싱(Rehashing)이 필요해지는데, 이 과정에서는 모든 데이터를 새로운 버킷 배열로 옮기는 작업이 포함되어 일시적인 성능 저하가 발생할 수 있다.</p>
<p>메모리 관리 측면에서도 해시 테이블은 추가적인 버킷 공간과 충돌 해결 구조를 유지해야 하므로 전체 메모리 사용량이 증가하는 단점이 있다.</p>
<p>따라서, 해시 테이블을 설계할 때는 적절한 초기 크기와 부하 계수(Load Factor)를 설정하여 재해싱 빈도를 최소화하고 메모리 효율성을 높이는 전략이 필요하다.</p>
<hr>
]]></description>
        </item>
        <item>
            <title><![CDATA[숫자 야구 게임 제작한거 피드백]]></title>
            <link>https://velog.io/@kim_yw/%EC%88%AB%EC%9E%90-%EC%95%BC%EA%B5%AC-%EA%B2%8C%EC%9E%84-%EC%A0%9C%EC%9E%91%ED%95%9C%EA%B1%B0-%ED%94%BC%EB%93%9C%EB%B0%B1</link>
            <guid>https://velog.io/@kim_yw/%EC%88%AB%EC%9E%90-%EC%95%BC%EA%B5%AC-%EA%B2%8C%EC%9E%84-%EC%A0%9C%EC%9E%91%ED%95%9C%EA%B1%B0-%ED%94%BC%EB%93%9C%EB%B0%B1</guid>
            <pubDate>Mon, 21 Apr 2025 11:51:16 GMT</pubDate>
            <description><![CDATA[<p>언리얼로 간단하게 제작한 숫자 야구 게임이다.
<a href="https://github.com/balamwind/LeanProject9">https://github.com/balamwind/LeanProject9</a></p>
<hr>
<h1 id="피드백">피드백</h1>
<p>함수 줄바꿈 무조건 해라</p>
<p>WhiteSpace 보는 옵션이 있는데 그거 키면 불필요한 띄어쓰기 있으니까 지워라
한방에 지워주는 단축키 있다.
저장시 WhiteSpace를 싹 다 지워주는 옵션이 있을거다</p>
<p>UObject 변수인데 UPROPERTY 없으면 노란줄 뜸
잘못하면 GC가 가져감 꼭 붙이셈</p>
<p>생성해서 넣는거면 꼭 필요한거 까진 않음
그래도 꼭 붙여주는게 좋음
그게 싫으면 TWeakObjectPtr을 쓰셈
UPROPERTY(Transient)를 해서 런타임에서 생성하는 거라 삭제 신경 덜써도 된다 표시를 해주면 된다.</p>
<p>한줄짜리 if 쓰지 않는걸 추천한다.</p>
<p>문자로 Widget을 구해오는 등할 때 TEXT() 반드시 붙여줘야한다
윈도우는 문제 없어도 리눅스나 다른 OS에서 실행할 때 문제가 될 수 있다.</p>
<p>함수 좀 길어진다 싶으면 함수 분리해라
람다로 만들던가</p>
<p>const는 항상 붙여주자</p>
<p>왠만하면 초기값을 .h에서 초기화해줘라
특히 블루프린트 노출 변수는 기본값 설정을 해줘야한다
안하면 경고 메세지 뜸</p>
<p>check() 같은거 붙여서 nullptr 체크 하는게 좋다.</p>
<p>GetWidgetFromName()은 무거우니까 왠만하면 쓰지 말아라</p>
<p>GameMode에서 PostLogin으로 처리하는게 더 좋았을거다
튜터 쌤이 알기론 PlayerController의 Login 에서 실행될 텐데 자기 Pawn까지 생성하고 보내는걸로 안다</p>
<p>FString보다 FName이 비교가 더 빠르다
기술 면접에서도 많이 물어보는데 FText, FString, FName이 어떻게 다른지 공부해두는게 좋다.</p>
<p>다른 사람이 봤을 때 딱 알아볼 수 있게
check() 같은거를 붙여주는게 좋다.
예를 들어 Problem이 길이가 3으로 고정되는건 나만 알고 있으니까
앞에 check(Problem.Len() == 3) 이런걸 붙여주면 좋다.</p>
<p>증감 연산자는 전위가 아주 살짝 더 빠르니까 앞에 붙이자 ( ex) ++ProblemCount )</p>
<p>아예 조건이 비슷하지 않는건 그냥 if문으로 분리하고 return;을 붙여주는게 낫다</p>
<p>아예 if문으로 조건을 붙이고 return을 해서 밑으로 코드를 내리는게 대부분 더 낫다
if문으로 정리하는게 더 깔끔할 때도 있는데 그건 하다보면 느낄꺼다</p>
<p>그냥 상수로 쓸꺼면 const만 붙이지말고
constexpr을 쓰거나 inline static constexpr를 붙이는게 나을꺼다</p>
<p>RPC는 최소한으로 쓰는게 좋다
같은 함수에 여러번의 RPC를 호출하는 것 보단 한번의 RPC로 줄여라
멀티 게임 제작할 땐 RPC의 호출을 최소화 해주는게 좋다.</p>
<p>함수 내부에 식을 넣지 마라. 헷갈린다</p>
<p>값을 나는 알고있지만 다른 사람이 볼 때는 모르니까 한방에 알 수 있게 check문 같은걸 넣어야한다.</p>
<p>RandomIndex를 선택하고 걔꺼를 뽑고 그거를 맨 뒤에 있는애랑 swap하고 RnadomIndex를 뽑을 때 최대 값을 1씩 줄이는 방식으로 원하는 랜덤을 뽑으면서 메모리도 쓰는 시간도 안쓸 수 있다.
튜터님이 알기론 ?? 피셔 알고리즘 이라고 한다.</p>
<p>람다 쓸 때 [&amp;]만 띡 쓰지 말고 실제로 쓰는 변수를 직접 넣어라
[Temp = Variable] 이런식으로 자동으로 Variable에 맞는 타입으로 캡쳐해서 쓸 수 있으니까 써라
UObject라면 MakeWeakObjectPtr()을 써서 캡쳐를 해라</p>
<p>그냥 깡 숫자 말고 static constexpr 같은걸 붙여서 어떤 의미를 가지고있는 상수인지 표현해주는게 좋다.</p>
<p>그냥 최대한 RPC를 안보내는게 더 중요하다
최대한 한 함수로 모아서 그 안에서 분기로 분리되게 만드는게 더 좋다</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[힙(우선순위 큐), 다익스트라 알고리즘, 언리얼 조금]]></title>
            <link>https://velog.io/@kim_yw/%ED%9E%99%EC%9A%B0%EC%84%A0%EC%88%9C%EC%9C%84-%ED%81%90-%EB%8B%A4%EC%9D%B5%EC%8A%A4%ED%8A%B8%EB%9D%BC-%EC%95%8C%EA%B3%A0%EB%A6%AC%EC%A6%98-%EC%96%B8%EB%A6%AC%EC%96%BC-%EC%A1%B0%EA%B8%88</link>
            <guid>https://velog.io/@kim_yw/%ED%9E%99%EC%9A%B0%EC%84%A0%EC%88%9C%EC%9C%84-%ED%81%90-%EB%8B%A4%EC%9D%B5%EC%8A%A4%ED%8A%B8%EB%9D%BC-%EC%95%8C%EA%B3%A0%EB%A6%AC%EC%A6%98-%EC%96%B8%EB%A6%AC%EC%96%BC-%EC%A1%B0%EA%B8%88</guid>
            <pubDate>Mon, 07 Apr 2025 14:35:06 GMT</pubDate>
            <description><![CDATA[<h1 id="힙우선순위-큐">힙(우선순위 큐)</h1>
<p><img src="https://velog.velcdn.com/images/kim_yw/post/eb3e043c-31a0-49d0-ad29-fced8ec4d412/image.png" alt=""></p>
<p><em><code>출처 : 나무위키</code></em></p>
<p>힙(우선순위 큐)은 우선 순위 관련 문제나 K번째 최대값 찾기 같은 문제에서 쓸 수 있다.</p>
<p>데이터를 삽입 시 아래와 같은 과정을 반복해 규칙에 맞게 만든다</p>
<ol>
<li>가장 끝의 자리에 노드를 삽입한다.</li>
<li>그 노드와 부모 노드를 서로 비교한다.</li>
<li>규칙에 맞으면 그대로 두고, 그렇지 않으면 부모와 교환한다. 
(부모 노드는 삽입된 위치의 인덱스 번호에서 /2를 하면 쉽게 구할 수 있다.)</li>
</ol>
<p><img src="https://velog.velcdn.com/images/kim_yw/post/25710c86-15c4-4668-b477-66963554468f/image.png" alt=""></p>
<p><em><code>출처 : 나무위키</code></em></p>
<p>힙은 배열을 이용해 만들기 때문에 만약 노드의 인덱스가 i라면, 
<code>왼쪽 자식은 인덱스 2 * i + 1, 오른쪽 자식은 인덱스 2 * i + 2, 부모 노드는 인덱스 (i - 1) / 2 (정수 나눗셈)</code>
로 쉽게 구할 수 있다.</p>
<p>C++에선 make_heap 과 sort_heap으로 간단하게 구현할 수 있다.
추가로 push_heap도 있다.</p>
<h1 id="다익스트라-알고리즘">다익스트라 알고리즘</h1>
<p>다익스트라라는 과학자가 고안해서 다익스트라 알고리즘이다.
최소 비용의 정점을 빠르게 선택하기 위한 알고리즘이고 힙을 사용한다.</p>
<p>가장 비용이 적은 경로부터 탐색하고 탐색 과정에서 경로 비용을 업데이트하면서 최단 경로를 찾는다.</p>
<p>힙을 사용하지 않으면 O(n) 이지만
힙을 사용하면 O(E log V)정도로 끝낼 수 있다.
여기서 E는 간선(Edge) 이고 V는 정점(Vertex) 이다.</p>
<blockquote>
<p>다익스트라 알고리즘
<br>
<a href="https://www.youtube.com/watch?v=tZu4x5825LI"><img src="https://img.youtube.com/vi/tZu4x5825LI/0.jpg" alt="다익스트라 알고리즘"></a></p>
</blockquote>
<p>다익스트라 알고리즘에 대해 쉽고 자세히 설명한 영상이다.</p>
<hr>
<h1 id="언리얼">언리얼</h1>
<p>UCLASS(Config = Game)로 환경설정에서 변수를 설정하게 만들 수 있다.
<a href="%22https://dev.epicgames.com/documentation/ko-kr/unreal-engine/configuration-files-in-unreal-engine%22">언리얼 - 환경설정 파일</a></p>
<p>Subsystem의 기본값 설정 같은 것들은 UDeveloperSetting을 상속받아서 하면 된다.
UCLASS(Config=Game)이랑 UPROPERTY(Config, EditAnywhere)를 이용해서 프로젝트 세팅에 이 클래스를 설정할 수 있다.
GetDefault&lt;&gt;()를 써서 클래스의 CDO를 이용해 값을 가져올 수 있다.</p>
<p>저장은 Default{Config=에 쓴 곳}.ini 파일에 저장되게 된다.</p>
<p>UI나 이런 언리얼 Editor 설정에 관한건
<a href="%22https://unreal-garden.com/%22">benui</a> &lt;&lt;&lt; 이분이 기가막히게 정리해놨으니까 가서 보기</p>
<p>PostEditChangeProperty로 값이 바뀌면 함수 호출된다.
meta에 EditCondition = &quot;MyValue &gt; 0&quot;
이런식으로 활성화 비활성화 조건도 설정할 수 있다.</p>
<p>ServerTravel에서 매개변수로 URL을 받는데 진짜 WebURL처럼 ?뒤에 정보들을 넣을 수 있다.
GameMode에 있는 PreLogin에서 URL에 저장되있는 데이터를 알 수 있다. (ProcessServerTravel도 알 수 있는 듯?)</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[리팩터링 5가지 핵심 결정 원칙]]></title>
            <link>https://velog.io/@kim_yw/%EB%A6%AC%ED%8C%A9%ED%84%B0%EB%A7%81-5%EA%B0%80%EC%A7%80-%ED%95%B5%EC%8B%AC-%EA%B2%B0%EC%A0%95-%EC%9B%90%EC%B9%99</link>
            <guid>https://velog.io/@kim_yw/%EB%A6%AC%ED%8C%A9%ED%84%B0%EB%A7%81-5%EA%B0%80%EC%A7%80-%ED%95%B5%EC%8B%AC-%EA%B2%B0%EC%A0%95-%EC%9B%90%EC%B9%99</guid>
            <pubDate>Thu, 03 Apr 2025 12:13:01 GMT</pubDate>
            <description><![CDATA[<h1 id="코드-세분화-vs-코드-통합">코드 세분화 vs 코드 통합</h1>
<h2 id="코드-세분화">코드 세분화</h2>
<ul>
<li><p>함수 추출하기
하나의 함수가 여러개의 일을 할 때 분리 해주기</p>
</li>
<li><p>변수 추출하기
간단한 계산식이어도 이게 다른 일을 할 때 분리해 어떤 계산을 하는건지 표현하기</p>
</li>
<li><p>클래스 추출하기
하나의 클래스가 여러개의 일을 하지 않게 분리해 관리하기</p>
</li>
</ul>
<h2 id="코드-통합">코드 통합</h2>
<p>위와는 또 정반대의 법칙이다;;</p>
<ul>
<li><p>함수 인라인하기
함수로 만들어봤자 사실상 별 작업을 안하면 인라인하기</p>
</li>
<li><p>변수 인라인하기
굳이 한번만 쓰는 변수는 그냥 인라인 해버리기</p>
</li>
<li><p>클래스 인라인하기
클래스가 너무 의미없이 나눠져있으면 인라인하기</p>
</li>
</ul>
<h2 id="결정-기준">결정 기준</h2>
<table>
<thead>
<tr>
<th>세분화가 적합한 상황</th>
<th>통합이 적합한 상황</th>
</tr>
</thead>
<tbody><tr>
<td>코드가 복잡하고 이해하기 어려울 때</td>
<td>추상화가 오히려 복잡성을 증가시킬 때</td>
</tr>
<tr>
<td>재사용 가능성이 있을 때</td>
<td>간단한 로직이 불필요하게 분리되어 있을 때</td>
</tr>
<tr>
<td>변경 가능성이 높은 부분</td>
<td>사용되는 곳이 한 곳뿐인 단순한 코드</td>
</tr>
<tr>
<td>의도와 구현을 분리할 필요가 있을 때</td>
<td>함수/변수 이름이 실제 로직에 가치를 더하지 않을 때</td>
</tr>
</tbody></table>
<br>
<br>

<p>결국 리랙터링이란 균형이 중요하다
너무 나눠도 문제고 너무 통합해도 문제다</p>
<hr>
<h1 id="객체-세분화-vs-객체-통합">객체 세분화 vs 객체 통합</h1>
<h2 id="객체-세분화">객체 세분화</h2>
<p>단일책임의 원칙을 지키는 방식인거 같다</p>
<ul>
<li><p>매개변수 객체 만들기
어떤 함수를 호출하는 등의 일에 매개변수가 너무 많아지면 가독성이 떨어지므로 struct 같은걸로 하나로 모아 전달하기</p>
</li>
<li><p>단계 쪼개기
한개의 함수에서 많은 단계를 거칠 때 단계별로 함수를 쪼갠다</p>
</li>
<li><p>반복문 쪼개기
반복문에서 여러 작업을 할 때 쪼개기</p>
</li>
</ul>
<h2 id="객체-통합">객체 통합</h2>
<ul>
<li><p>여러 함수를 클래스로 묶기
관련된 여러 함수를 묶어서 클래스로 만들어 버리기
ex) Timer를 각 함수로 관리하지 말고 class로 만들어서 관리해버리기</p>
</li>
<li><p>여러 함수를 변환 함수로 묶기
여러 함수로 분할되어 있으며 오히려 알아보기 힘드니까 차라리 하나로 묶기
ex) 쓸데없이 나눠져있는 데미지 계산</p>
</li>
</ul>
<h2 id="결정-기준-1">결정 기준</h2>
<table>
<thead>
<tr>
<th>세분화가 적합한 상황</th>
<th>통합이 적합한 상황</th>
</tr>
</thead>
<tbody><tr>
<td>책임이 명확히 분리될 때</td>
<td>강한 응집력이 필요할 때</td>
</tr>
<tr>
<td>다른 용도로 재사용 가능할 때</td>
<td>관련 데이터와 동작이 함께 있는 것이 자연스러울 때</td>
</tr>
<tr>
<td>독립적으로 테스트하고 싶을 때</td>
<td>공유 상태에 대한 관리가 필요할 때</td>
</tr>
<tr>
<td>복잡한 단계를 나누어 명확히 하고 싶을 때</td>
<td>여러 작은 함수들이 항상 함께 사용될 때</td>
</tr>
</tbody></table>
<hr>
<h1 id="간접-접근-vs-직접-접근">간접 접근 vs 직접 접근</h1>
<h2 id="간접-접근">간접 접근</h2>
<ul>
<li><p>변수 캡슐화하기
변수에 직접 값을 대입하게 되면 부가 처리같은 것이 불가능하고 추적이 힘들다
Setter로 접근하게 만들기</p>
</li>
<li><p>레코드 캡슐화하기
데이터 접근을 항상 함수로만 가능하게 만들기</p>
</li>
<li><p>컬렉션 캡슐화하기
Array 같은 컬렉션은 직접 접근이 가능하면 여러 다른 일들이 가능해져 위험하다.
필요한 기능만 함수로 노출하기
ex) Inventory컬렉션에 직접 접근하게 만드는게 아닌 AddItem() 을 추가하기</p>
</li>
</ul>
<h2 id="직접-접근">직접 접근</h2>
<ul>
<li><p>중개자 제거하기
함수를 계속 타면서 함수를 호출하면 복잡하고 알아보기 어려울 수 있다. (World-&gt;GameState-&gt;PlayerState-&gt;PlayerController 처럼)
이런걸 함수로 줄여 중간에 타게되는 중개자를 제거해 보기 편하게 만드는거다</p>
</li>
<li><p>위임 숨기기
객체 내부 구조가 전부 보이는걸 함수로 감춰 간단하게 만들기
ex) player.contactInfo.Address.zipCode; 이걸 player.GetZipCode(); 로 끝낼 수 있게 정리하기</p>
</li>
</ul>
<h2 id="결정-기준-2">결정 기준</h2>
<table>
<thead>
<tr>
<th>간접 접근이 적합한 상황</th>
<th>직접 접근이 적합한 상황</th>
</tr>
</thead>
<tbody><tr>
<td>데이터 검증이나 부가 처리가 필요할 때</td>
<td>과도한 래퍼가 복잡성만 증가시킬 때</td>
</tr>
<tr>
<td>변경 추적이 필요할 때</td>
<td>성능이 중요한 핫스팟일 때</td>
</tr>
<tr>
<td>향후 구현 변경 가능성이 있을 때</td>
<td>단순한 데이터 구조에서</td>
</tr>
<tr>
<td>중복된 접근 로직이 여러 곳에 있을 때</td>
<td>위임 체인이 너무 길어질 때</td>
</tr>
</tbody></table>
<br>
<br>

<p>데이터를 보호해야 되냐 아니냐로 판단하는게 좋다고 함</p>
<hr>
<h1 id="조건문-vs-다형성">조건문 vs 다형성</h1>
<h2 id="조건문">조건문</h2>
<ul>
<li><p>조건문 분해하기
조건문의 조건이 여러개 있어 뭐하는건지 모르겠을 때 그냥 함수로 분리해서 한눈에 이해하기 쉽게 만들기</p>
</li>
<li><p>조건식 통합하기
어차피 같은 행동을 하는 조건문이라면 조건식을 통합해 보기 쉽게 만들기</p>
</li>
</ul>
<h2 id="다형성">다형성</h2>
<ul>
<li><p>조건부 로직을 다형성으로 바꾸기
새로운 뭔가 추가됬을 때 계속 수정해줘야 된다면 다형성으로 만들어 중복을 줄이고 명확하게 만들기</p>
</li>
<li><p>특이 케이스 추가하기
null 체크 등을 할 떄 if문으로 구별하는데
(예를들어 player가 null이라면 &quot;Guest&quot;를 띄어주기)
아싸리 해당 케이스의 class를 추가해 나올 결과를 설정해두기
(nullplayer 같은걸 만들고 GetName을 &quot;Guest&quot;로 반환해두는 방식으로)</p>
</li>
</ul>
<h2 id="결정-기준-3">결정 기준</h2>
<table>
<thead>
<tr>
<th>조건문이 적합한 상황</th>
<th>다형성이 적합한 상황</th>
</tr>
</thead>
<tbody><tr>
<td>단순한 분기 로직일 때</td>
<td>타입별 동작 차이가 뚜렷할 때</td>
</tr>
<tr>
<td>일회성이거나 지역적인 결정일 때</td>
<td>타입 추가가 자주 발생할 때</td>
</tr>
<tr>
<td>성능이 매우 중요한 곳일 때</td>
<td>타입별 코드가 반복적으로 나타날 때</td>
</tr>
<tr>
<td>타입 배열이 고정적일 때</td>
<td>동작이 확장될 가능성이 높을 때</td>
</tr>
</tbody></table>
<hr>
<h1 id="상속-vs-위임">상속 vs 위임</h1>
<h2 id="상속">상속</h2>
<ul>
<li><p>메서드 올리기
중복된 함수는 부모로 올리기</p>
</li>
<li><p>필드 올리기
중복되는 변수는 부모로 올리기</p>
</li>
<li><p>슈퍼클래스 추출하기
같은 일을 하는 클래스 같으면 부모로 따서 그걸 상속받게 만들기</p>
</li>
</ul>
<h2 id="위임">위임</h2>
<ul>
<li><p>서브클래스를 위임으로 바꾸기
소리, AI 등 같은 것을 재사용할만한 것들이 있을 때 전략 패턴같이 공통되는 것들을 분리해 그곳에서 관리하게 만들기</p>
<ul>
<li>슈퍼클래스를 위임으로 바꾸기
Component마냥 상속만 쓰지 말고 가지고 잇는 객체 등으로 만들어 거기서 실행하는 식으로 분리하기</li>
</ul>
</li>
</ul>
<h2 id="결정-기준-4">결정 기준</h2>
<table>
<thead>
<tr>
<th>상속이 적합한 상황</th>
<th>위임이 적합한 상황</th>
</tr>
</thead>
<tbody><tr>
<td>명확한 &quot;is-a&quot; 관계가 있을 때</td>
<td>&quot;has-a&quot; 관계나 행동 공유일 때</td>
</tr>
<tr>
<td>공통 기능이 많고 타입 계층이 필요할 때</td>
<td>런타임에 동작을 변경해야 할 때</td>
</tr>
<tr>
<td>다형성을 활용한 확장이 자연스러울 때</td>
<td>다중 상속과 같은 효과가 필요할 때</td>
</tr>
<tr>
<td>코드 재사용이 수직적일 때</td>
<td>기존 클래스 변경 없이 기능 확장이 필요할 때</td>
</tr>
</tbody></table>
]]></description>
        </item>
        <item>
            <title><![CDATA[최단 거리 구하기, 단어 변환]]></title>
            <link>https://velog.io/@kim_yw/%EC%B5%9C%EB%8B%A8-%EA%B1%B0%EB%A6%AC-%EA%B5%AC%ED%95%98%EA%B8%B0-%EB%8B%A8%EC%96%B4-%EB%B3%80%ED%99%98</link>
            <guid>https://velog.io/@kim_yw/%EC%B5%9C%EB%8B%A8-%EA%B1%B0%EB%A6%AC-%EA%B5%AC%ED%95%98%EA%B8%B0-%EB%8B%A8%EC%96%B4-%EB%B3%80%ED%99%98</guid>
            <pubDate>Wed, 02 Apr 2025 12:36:56 GMT</pubDate>
            <description><![CDATA[<h1 id="맵-최단-거리-구하기">맵 최단 거리 구하기</h1>
<p><a href="https://school.programmers.co.kr/learn/courses/30/lessons/1844">https://school.programmers.co.kr/learn/courses/30/lessons/1844</a></p>
<p>해당 문제를 푸는데, BFS로 접근하면 될거 같은데 BFS를 잘 모르겠어서 일단 생각이 나는 DFS로 풀어보았다.
** 틀렸다. 효율도 안좋고, 틀리기까지 했다. **</p>
<pre><code class="language-cpp">#include &lt;iostream&gt;
#include &lt;vector&gt;
#include &lt;queue&gt;
#include &lt;algorithm&gt;

using namespace std;

vector&lt;int&gt; successVisitCounts;
void DFS(vector&lt;vector&lt;int&gt;&gt;&amp; maps, vector&lt;vector&lt;bool&gt;&gt;&amp; visits, const pair&lt;int, int&gt;&amp; targetPos, pair&lt;int, int&gt; pos, int visitCount)
{
    visitCount++;
    visits[pos.first][pos.second] = true;

    if (pos == targetPos)
        successVisitCounts.push_back(visitCount);

    //방문 위치
    int yPos[] = { -1, +1, 0, 0 };
    int xPos[] = { 0, 0, -1, +1 };

    for (int i = 0; i &lt; 4; i++)
    {
        pair&lt;int, int&gt; visitPos = pos;
        visitPos.first += yPos[i];
        visitPos.second += xPos[i];

        if (visitPos.first &gt;= 0 &amp;&amp; visitPos.first &lt; maps.size() &amp;&amp;
            visitPos.second &gt;= 0 &amp;&amp; visitPos.second &lt; maps[0].size() &amp;&amp;
            maps[visitPos.first][visitPos.second] &amp;&amp; visits[visitPos.first][visitPos.second] == false)
            DFS(maps, visits, targetPos, visitPos, visitCount);
    }

    visits[pos.first][pos.second] = false;
    visitCount--;
}

int solution(vector&lt;vector&lt;int&gt;&gt; maps)
{
    pair&lt;int, int&gt; startPos = make_pair(0, 0);
    pair&lt;int, int&gt; targetPos = make_pair(maps.size() - 1, maps.size() - 1);

    vector&lt;vector&lt;bool&gt;&gt; visits(maps.size());
    for (vector&lt;bool&gt;&amp; vec : visits)
        vec.resize(maps[0].size());

    int visitCount = 0;
    DFS(maps, visits, targetPos, startPos, visitCount);

    auto minIter = min_element(successVisitCounts.begin(), successVisitCounts.end());

    return minIter == successVisitCounts.end() ? -1 : *minIter;
}

int main()
{
    cout &lt;&lt; solution({ {1, 0, 1, 1, 1}, {1, 0, 1, 0, 1}, {1, 0, 1, 1, 1}, {1, 1, 1, 0, 1}, {0, 0, 0, 0, 1} }) &lt;&lt; endl;
}</code></pre>
<p>예시는 성공해서 될 줄 알았는데 안됬다.
역시 최단거리이므로 BFS로 푸는게 맞는 것 같다.</p>
<hr>
<h1 id="단어-변환">단어 변환</h1>
<p><a href="https://school.programmers.co.kr/learn/courses/30/lessons/43163">https://school.programmers.co.kr/learn/courses/30/lessons/43163</a></p>
<p>최단 횟수라 BFS 맞는거 같긴한데..
사람이 하루 아침에 익숙해 질 수는 없으므로 알고있는 DFS로 풀었다.
이번엔 통과도 되서 정답이긴하다.</p>
<pre><code class="language-cpp">#include &lt;iostream&gt;
#include &lt;string&gt;
#include &lt;vector&gt;
#include &lt;algorithm&gt;

using namespace std;

vector&lt;int&gt; endCounts;
void DFS(vector&lt;string&gt;&amp; words, vector&lt;bool&gt;&amp; visit, const string&amp; target, string str, int count)
{
    if (str == target)
    {
        endCounts.push_back(count);
        return;
    }

    for (int i = 0; i &lt; words.size(); i++)
    {
        const string&amp; nowStr = words[i];
        int diff = 0;

        if (nowStr == str || visit[i])
            continue;

        //차이점이 1인지 검사
        for (int j = 0; j &lt; str.size(); j++)
            if (str[j] != nowStr[j])
                diff++;

        if (diff == 1)
        {
            visit[i] = true;
            DFS(words, visit, target, nowStr, count + 1);
            visit[i] = false;
        }
    }
}

int solution(string begin, string target, vector&lt;string&gt; words)
{
    auto targetIter = find(words.begin(), words.end(), target);
    if (targetIter == words.end())
        return 0;

    vector&lt;bool&gt; visit(words.size());
    DFS(words, visit, target, begin, 0);

    auto minIter = min_element(endCounts.begin(), endCounts.end());
    return endCounts.size() ? *minIter : 0;
}

int main()
{
    cout &lt;&lt; solution(&quot;hit&quot;, &quot;cog&quot;, { &quot;hot&quot;, &quot;dot&quot;, &quot;dog&quot;, &quot;lot&quot;, &quot;log&quot;, &quot;cog&quot; }) &lt;&lt; endl;
}</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[언리얼의 assert]]></title>
            <link>https://velog.io/@kim_yw/%EC%96%B8%EB%A6%AC%EC%96%BC%EC%9D%98-assert</link>
            <guid>https://velog.io/@kim_yw/%EC%96%B8%EB%A6%AC%EC%96%BC%EC%9D%98-assert</guid>
            <pubDate>Tue, 01 Apr 2025 12:05:16 GMT</pubDate>
            <description><![CDATA[<p>assert는 C에서 생겨 개발 중 예상치 못한 런타임 조건을 탐지하고 진단하는 데 유용하다.
예를 들어, 포인터가 null이 아닌지, 제수가 0이 아닌지, 함수가 반복적으로 실행되지 않는지 등을 확인하여 중요한 가정이 맞는지 점검한다.</p>
<p>언리얼 엔진에서는 assert와 유사한 3가지 방법을 제공한다.</p>
<ul>
<li>check : 절대 일어나면 안 되는 매우 기본적인 조건</li>
<li>verify : 대체 가능한 리소스나 상태 검사</li>
<li>ensure : 복잡한 조건과 자동 복구가 필요한 경우</li>
</ul>
<p>이들은 모두 개발 중에 조건을 확인하고, 조건이 맞지 않으면 오류를 발생시키는 역할을 한다.
이 셋은 모두 기본적으론 Release Packege에선 작동을 안함.</p>
<hr>
<h2 id="check">check</h2>
<p>** <code>check</code>는 표현식이 <code>false</code>로 평가되면 실행을 멈춘다. **</p>
<p><code>check</code> 매크로는 디버그, 개발, 테스트, 릴리즈 에디터 빌드에서 작동하며, <code>slow</code>로 끝나는 매크로는 디버그 빌드에서만 작동한다.
<code>USE_CHECKS_IN_SHIPPING</code>을 <code>true</code>로 설정하면 모든 빌드에서 작동한다.
<code>check</code>는 코드 내에서 값 변경이나 추적이 어려운 릴리즈 전용 버그를 확인할 때 유용하다.</p>
<hr>
<h2 id="ensure">ensure</h2>
<p>** <code>ensure</code>의 표현식이 <code>false</code>로 평가되면 크래시 리포터에 알리지만 실행은 계속된다. **</p>
<p><code>ensure</code> 계열은 <code>verify</code>와 비슷하지만, 치명적이지 않은 오류를 처리하는 데 사용된다.
크래시 리포터 플러드를 방지하기 위해 한 세션당 한 번만 보고된다.
<code>ensure</code>는 모든 빌드에서 표현식을 평가하지만, 디버그, 개발, 테스트, 릴리즈 에디터 빌드에서만 크래시 리포터에 알린다.
<code>always</code> 버전을 사용하면 매번 리포트를 받을 수 있다.</p>
<p>기본적으론 1회용 로그만 남기는 <code>check</code>라 보면 될 듯
<code>ensureAlways</code>를 쓰면 오류가 터질 때 마다 로그를 남겨줄 수 있음</p>
<hr>
<h2 id="verify">verify</h2>
<p>** <code>verify</code>는 항상 표현식을 평가한다. 표현식이 항상 실행되어야 할 경우에 사용해야한다. **</p>
<p><code>verify</code>는 <code>check</code>와 비슷하지만, <code>check</code>가 비활성화된 빌드에서도 표현식을 평가한다.
<code>verify</code>는 디버그, 개발, 테스트, 릴리즈 에디터 빌드에서 작동하며, <code>slow</code> 매크로는 디버그 빌드에서만 작동한다.
<code>USE_CHECKS_IN_SHIPPING</code>을 <code>true</code>로 설정하면 모든 빌드에서 작동한다.
<code>verify</code>는 표현식을 평가하지만 실행을 멈추거나 로그를 출력하지 않는다.</p>
<p>많이 안쓴다.</p>
<p>항상 표현식을 평가한다는게 이해가 잘 안될 수 있는데 아래와 같은 상황에 쓰인다.</p>
<p>ex)</p>
<pre><code class="language-cpp">// 메시 값을 설정하고 그 값이 null이 아닐 것으로 예상합니다. 
// 그 후 메시가 null이면 프로그램을 정지합니다.
// 표현식에 추가 이펙트(메시 세팅)가 있으므로 Check 대신 Verify를 사용합니다.
verify((Mesh = GetRenderMesh()) != nullptr);

//이게 Shipping 빌드에서는 아래처럼 바뀐다.
Mesh = GetRenderMesh();</code></pre>
<hr>
<p>더 자세한건 아래의 언리얼 어서트 설명을 보는게 좋다.</p>
<p><a href="https://dev.epicgames.com/documentation/ko-kr/unreal-engine/asserts-in-unreal-engine">https://dev.epicgames.com/documentation/ko-kr/unreal-engine/asserts-in-unreal-engine</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[타겟 넘버, 웜 바이러스, 옵저버 패턴]]></title>
            <link>https://velog.io/@kim_yw/%ED%83%80%EA%B2%9F-%EB%84%98%EB%B2%84-%EC%9B%9C-%EB%B0%94%EC%9D%B4%EB%9F%AC%EC%8A%A4-%EC%98%B5%EC%A0%80%EB%B2%84-%ED%8C%A8%ED%84%B4</link>
            <guid>https://velog.io/@kim_yw/%ED%83%80%EA%B2%9F-%EB%84%98%EB%B2%84-%EC%9B%9C-%EB%B0%94%EC%9D%B4%EB%9F%AC%EC%8A%A4-%EC%98%B5%EC%A0%80%EB%B2%84-%ED%8C%A8%ED%84%B4</guid>
            <pubDate>Mon, 31 Mar 2025 11:48:06 GMT</pubDate>
            <description><![CDATA[<h2 id="타겟-넘버">타겟 넘버</h2>
<p><a href="https://school.programmers.co.kr/learn/courses/30/lessons/43165">https://school.programmers.co.kr/learn/courses/30/lessons/43165</a></p>
<pre><code class="language-cpp">#include &lt;string&gt;
#include &lt;vector&gt;

using namespace std;

void DFS(const vector&lt;int&gt;&amp; numbers, const int&amp; targetNumber, int index, int calculate, int&amp; outResult)
{
    //끝
    if (index == numbers.size())
    {
        if (calculate == targetNumber)
            outResult++;

        return;
    }

    DFS(numbers, targetNumber, index + 1, calculate + numbers[index], outResult);
    DFS(numbers, targetNumber, index + 1, calculate - numbers[index], outResult);
}

int solution(vector&lt;int&gt; numbers, int targetNumber)
{
    int result = 0;
    DFS(numbers, targetNumber, 0, 0, result);

    return result;
}</code></pre>
<hr>
<h2 id="웜-바이러스">웜 바이러스</h2>
<p><a href="https://www.acmicpc.net/problem/2606">https://www.acmicpc.net/problem/2606</a></p>
<p>문제만 보고 풀었는데 실제로 들어가서 적용해보니 메모리 초과라고 함;
그리고 양방향 문제도 있는거 같은 일단 예시로 나오는 입력에는 완벽히 대응함</p>
<pre><code class="language-cpp">void DFS(map&lt;int, vector&lt;int&gt;&gt;&amp; graph, vector&lt;bool&gt;&amp; visits, int vertex)
{
    visits[vertex - 1] = true;

    for (int i = 0; i &lt; graph[vertex].size(); i++)
        DFS(graph, visits, graph[vertex][i]);
}

int main()
{
    map&lt;int, vector&lt;int&gt;&gt; graph;

    int computerCount;
    cin &gt;&gt; computerCount;

    int lineCount;
    cin &gt;&gt; lineCount;

    for (int i = 0; i &lt; lineCount; i++)
    {
        int temp1, temp2;
        cin &gt;&gt; temp1 &gt;&gt; temp2;

        graph[temp1].push_back(temp2);
    }

    vector&lt;bool&gt; visits(computerCount, false);
    DFS(graph, visits, 1);

    int count = 0;
    for (int i = 0; i &lt; visits.size(); i++)
        if (visits[i])
            count++;

    //1번 컴퓨터는 제외
    count--;

    cout &lt;&lt; count &lt;&lt; endl;
}</code></pre>
<hr>
<h1 id="옵저버-패턴">옵저버 패턴</h1>
<p>보통은 클래스가 객체를 가지고 있고 그 객체의 함수를 직접 호출한다.
그렇게하면 간단하고 직관적이지만 신기능을 추가하려고 할 때마다 수정을 해야한다.
결과적으로 시간이 지나면 코드가 복잡해지고 유지보수가 어려워질 수 있다.</p>
<p>이런 현상을 방지하기 위해 옵저버 패턴을 도입하는데, 옵저버 패턴은 한 객체(주체, Subject)가 변경되었을 때, 그 변화를 감시하는 객체들(옵저버, Observer)에게 자동으로 알림을 보내는 구조이다.
요즘은 여러 언어들에서 EventHandler나 Delegate로 이 옵저버 패턴을 간단하게 사용하게 구현해놓았다.</p>
<p>옵저버 패턴은 보통 아래 네 가지 구성 요소로 설명된다.</p>
<ol>
<li><p>주체(Subject)</p>
<ul>
<li>관찰(Observer) 대상 객체</li>
<li>상태가 바뀌면 옵저버들에게 알림을 보낸다.</li>
</ul>
</li>
<li><p>옵저버(Observer)</p>
<ul>
<li>주체 객체의 상태 변경을 관찰하는 객체</li>
<li>주체가 알림을 보내면, 그에 맞춰 필요한 동작을 실행한다.</li>
</ul>
</li>
<li><p>구독(Subscribe)</p>
<ul>
<li>옵저버가 주체에 “나도 관찰하겠다” 하고 등록하는 과정이다.</li>
</ul>
</li>
<li><p>알림(Notify)</p>
<ul>
<li>주체가 자신의 상태가 변했음을 등록된 옵저버들에게 전달하는 과정이다.</li>
</ul>
</li>
</ol>
<br>

<h3 id="장점">장점</h3>
<ol>
<li>느슨한 결합(Loose Coupling) : 주체와 옵저버가 서로 구체적인 정보를 몰라도 기능 동작 가능.</li>
<li>확장성 : 새로운 옵저버를 손쉽게 추가하거나 제거할 수 있음.</li>
<li>가독성 : 각 기능(업적, 사운드, 시각효과 등)이 서로 독립적인 클래스로 구성되어 이해하기 쉬워짐.</li>
</ol>
<h3 id="단점">단점</h3>
<ol>
<li>등록 관리 : 옵저버가 많아지면 주체가 옵저버 리스트를 관리하는 비용이 늘어날 수 있음.</li>
<li>디버깅 : 이벤트가 많아질수록 “누가 누구에게서 알림을 받아서 어떤 일이 일어나는지” 추적이 어려워질 수 있음.</li>
</ol>
]]></description>
        </item>
        <item>
            <title><![CDATA[클린 코드]]></title>
            <link>https://velog.io/@kim_yw/%ED%81%B4%EB%A6%B0-%EC%BD%94%EB%93%9C</link>
            <guid>https://velog.io/@kim_yw/%ED%81%B4%EB%A6%B0-%EC%BD%94%EB%93%9C</guid>
            <pubDate>Thu, 27 Mar 2025 11:38:08 GMT</pubDate>
            <description><![CDATA[<h1 id="클린-코드">클린 코드</h1>
<p>코드를 쓸 때 하지 말아야 할 것들을 정리했다.</p>
<ul>
<li><p>상수를 잘 써라</p>
</li>
<li><p>한 모듈이 여러 이유로 자주 수정되어야하면 복잡해진다.
예를들어 GameManager가 있을 때 그 GameManager가 PlayerData의 저장과 Gameplay의 흐름을 담당하고 있다고 생각해보자
이 경우 PlayerData를 수정하기 위해서 GameManager를 알아야 하며 자칫 잘못했다 버그가 터져서 수정중에 다른 사람이 같은 파일을 수정해서 충돌이 나는 등 문제가 많아진다.
이런 경우 자체를 제거를 위해 분리를 하면 애초에 이런 문제가 생기지 않는다.</p>
</li>
<li><p>샷건 수술
작은 변경을 위해 여러 곳을 동시에 수정해야하면 골치 아픔
예를들어 데미지 계산 시스템을 생각해볼 때 ApplyDamage, CalculateDamage, UpdateDamageLeaderboard 같은 함수를 여러 곳에 분리하면 하나씩 가서 수정해야 되는 문제가 생긴다
중간중간 어딨는지 까먹을 수도 있고 어딨는지 찾기 힘들 수도 있다.
차라리 아래와 같이 분리를 해서 한곳에서 정리하자는 거다.</p>
<pre><code class="language-cpp">class UDamageSystem : public UObject 
{
  ApplyDamage...
  CalculateDamage...
  UpdateDamageLeaderboard...
};</code></pre>
</li>
<li><p>기능 편애
어떤 함수가 자기 객체보다 남의 객체 기능이나 데이터와 더 많이 소통한다면
그 함수를 데이터가 있는 곳으로 옮겨 의존성을 줄이자</p>
</li>
<li><p>데이터 뭉치
자주 함께 쓰이는 데이터는 하나로 묶으면 의미가 명확해짐
중복되는 필드나 매개변수 그룹은 별도 구조로 분리하자.
비슷한 데이터끼리 모아놓자</p>
<p>예를 들어 아래와 같이 매개변수가 반복된다 생각해보자</p>
<pre><code class="language-cpp">FireWeapon(float Damage, float Range, float Accuracy)
ShowWeaponInfo(float Damage, float Range, float Accuracy)</code></pre>
<p>&emsp;&emsp; 이걸 아래와 같이 struct로 정리 해버리면 훨씬 하나의 뜻으로써 정리가 된다는 거다.</p>
<pre><code class="language-cpp">struct FWeaponInfo
{
public:
  float Damage;
  float Range;
  float Accuracy;
};
</code></pre>
</li>
</ul>
<p>FireWeapon(const FWeaponInfo&amp; data)
ShowWeaponInfo(const FWeaponInfo&amp; data)</p>
<pre><code>
* 기본형 집착
복잡한 데이터를 단순한 기본형에 과도하게 의존하는 경향
복잡한 개념은 기본형 대신 차라리 클래스나 구조체를 사용해서 해결하자
타입 설계를 통해 복잡한 로직과 버그 발생의 가능성을 줄여줄 수 있다.

* 반복되는 스위치문
새로운 분기가 생길 때마다 여러 switch문을 전부 수정해야 한다면 비효율적이다.
다형성 구조를 적용해 중복되는 분기 로직을 없애자. 되도록이면 switch 쓰지 말고

* 반복문
루프 안에 비즈니스 로직을 다 넣지 말자
중첩 반복문은 왠만하면 피해야한다.

* 성의 없는 요소
코드 흐름상 실제로 필요 없는 구조는 과감히 없애자. 지우기 귀찮아도 삭제하자

* 추측성 일반화
현재 필요한 기능에 집중해 불필요한 추상화를 걷어내자
미래 대비보다 현재 문제 해결이 우선
나중에 필요할 수도 있다는 생각으로 만든 코드는 대부분 짐이 된다.

* 임시 필드
목적이 분명치 않은 필드는 코드 복잡도를 높이는 원인이다
특정 상황에서만 쓰이는 필드는 다른 상황에선 쓸데없는 혼란만 부를 뿐이다.

 예를들어
```cpp
class AEnemy : public UActor
{
public:
    float Health;

    //원거리 적만 씀
    float ProjectileSpeed;

    //특정 몬스터만 씀
    float TeleportCooldown;
};</code></pre><p>&emsp;&emsp; 이런식으로 있으면 특정 객체에만 사용하는데 변수가 쓰지 않는 변수가 있어 더 헷갈리기만 한다.
&emsp;&emsp; 차라리 아래처럼 Component로 분리하는게 훨씬 좋다.</p>
<pre><code class="language-cpp">class UProjectileComponent : public UActorComponent
{
public:
    float ProjectileSpeed;

    void Fire();
};

class UTeleportComponent : public UActorComponent
{
public:
    float TeleportCooldown;

    void Teleport();
};

class AEnemy : public UActor
{
public:
    float Health;
    UPorjectileComponent* ProjectileComp;
    UTeleportComponent* TeleportComp;
};</code></pre>
<ul>
<li><p>메시지 체인
객체를 줄줄이 호출하면 내부 구조가 노출돼 결합도가 커진다.
필요하다면 최종 로직을 호출부 가까이로 옮겨 의존을 줄이자</p>
</li>
<li><p>중재자
너무 많은 디자인 패턴들을 도입하다 보면 해당 클래스가 연결만 해주고 아무것도 안하는 일이 생길 수 있다.
실질적 로직 없이 위임만 하는 클래스는 존재할 필요가 없다.
ㅁㄴㅇ
늘 직관적 구조로 수정하자</p>
</li>
<li><p>내부자 거래
모듈 간에 비공개 데이터가 과하게 오가면 결합도가 높아짐
필요한 정보만 교환할 수 있게 인터페이스 범위를 명확히 정의하자
모듈 간 벽을 두껍게 유지해 각자의 책임을 분리하자</p>
</li>
<li><p>거대한 클래스
너무 많은 책임을 지는 클래스는 필드와 메서드가 폭발적으로 늘어난다.
중복이 생기고 관리가 어려워지니 분리하자</p>
</li>
<li><p>서로 다른 인터페이스의 대안 클래스들
클래스를 교체하려면 인터페이스가 호환되어야한다
유사 기능 클래스끼리 일관된 형식을 갖추는 것이 좋음
메서드 시그니처를 통일해 교체 가능성을 올리자</p>
</li>
<li><p>데이터 클래스
필드와 getter/setter만 있는 클래스는 다른 곳에서 함부로 조작되기 쉬움
변경될 필요가 없는 필드는 세터를 제거해 안전성을 높이자
필요 기능이 있다면 이 클래스 안에 직접 구현해 응집도를 높이자</p>
</li>
<li><p>상속 포기
서브클래스가 부모의 기능 중 일부만 필요하거나 인터페이스가 맞지 않는다면 꼭 상속하지 않고, 필요한 부분만 다른 방식으로 얻을 수 있다.
위임 등으로 불필요한 유산을 거부해 구조를 단순화하자.</p>
</li>
<li><p>주석의 남용
올바른 주석은 아주 좋지만, 코드만으로 명확하게 이해되는게 더 좋다.
주석이 필요한 상황일 경우, 주석이 필요없는 코드로 먼저 바꾸는게 우선이다.</p>
</li>
</ul>
<br>

<p>계속 짠 코드를 생각하며 어떤게 더 나은 구조가 없었을까? 생각하며 짜는게 좋다.
근데 경험상 일단 돌아가는게 먼저더라 항상 코드를 짜며 생각해되, 거기에 매몰되지는 말자</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[그래프, DFS, BFS, 커맨드 패턴]]></title>
            <link>https://velog.io/@kim_yw/%EA%B7%B8%EB%9E%98%ED%94%84-DFS-BFS-%EC%BB%A4%EB%A7%A8%EB%93%9C-%ED%8C%A8%ED%84%B4</link>
            <guid>https://velog.io/@kim_yw/%EA%B7%B8%EB%9E%98%ED%94%84-DFS-BFS-%EC%BB%A4%EB%A7%A8%EB%93%9C-%ED%8C%A8%ED%84%B4</guid>
            <pubDate>Wed, 26 Mar 2025 07:16:30 GMT</pubDate>
            <description><![CDATA[<h1 id="그래프">그래프</h1>
<h3 id="인접-리스트">인접 리스트</h3>
<p>인접 리스트는 연결된 노드만 리스트에 저장하는 방식이다.
아래와 같이 표현할 수 있다.</p>
<pre><code>A -&gt; B,D
B -&gt; A,C,D
C -&gt; B,D
D -&gt; A,B,C</code></pre><ul>
<li>장점<ul>
<li>필요한 정보만 저장하므로 메모리를 절약할 수 있다.</li>
<li>그래프 탐색 시 인접 노드를 빠르게 찾을 수 있다.</li>
</ul>
</li>
<li>단점<ul>
<li>특정 노드가 다른 노드와 연결되어 있는지 확인하는데 O(V)의 시간이 걸릴 수 있음</li>
</ul>
</li>
</ul>
<br>

<p>코딩 테스트에선 보통 인접 리스트 방식이 더 사용된다.
이유는</p>
<ul>
<li>메모리 효율성이 뛰어남<ul>
<li>인접 행렬은 <code>O(V^2)</code>의 공간을 차지하기 때문에, 노드 개수(V)가 크면 메모리를 과도하게 사용한다.
반면, 인접 리스트는 <code>O(V + E)</code>의 공간만 필요하므로, 노드가 많고 간선이 적은 경우(희소 그래프, Sparse Graph)에 훨씬 적은 메모리를 사용하게 된다.</li>
</ul>
</li>
<li>탐색 속도가 빠름<ul>
<li>인접 리스트는 O(E)에 가깝지만, 인접 행렬은 O(V^2)가 될 수 있다.</li>
</ul>
</li>
<li>실제 입력 데이터가 인접 리스트 형태인 경우가 많음<ul>
<li>대부분의 코딩 테스트 문제에서 그래프 입력은 a → b (weight) 형태로 주어지는데, 이를 인접 행렬로 변환하는 것보다 인접 리스트로 저장하는 것이 더 자연스럽다.</li>
</ul>
</li>
</ul>
<hr>
<h1 id="dfs깊이-우선-탐색">DFS(깊이 우선 탐색)</h1>
<p>DFS(Depth First Search)는 깊이 우선 탐색이라는 의미로 트리나 그래프를 최대한 깊게 들어가서 탐색하는 방법이다.</p>
<p>DFS는 재귀나 스택을 이용해서 구현할 수 있다.</p>
<ul>
<li><p>장점</p>
<ul>
<li>깊이 우선 탐색이 유리한 문제에 적합하다.<ul>
<li>미로 풀이 같은 데에 사용하기 좋다.</li>
<li>모든 경우의 수(혹은 모든 경로)를 찾거나, 퍼즐을 푸는 문제들이 나오는 경우가 있는데 이럴 때 DFS로 모든 가능한 선택지를 일단 끝까지 가보는 식으로 푼다.</li>
</ul>
</li>
</ul>
</li>
<li><p>단점</p>
<ul>
<li>재귀로 구현할 시 트리의 깊이가 매우 깊으면 스택 오버플로우가 발생할 수 있다.</li>
</ul>
</li>
</ul>
<hr>
<h1 id="bfs너비-우선-탐색">BFS(너비 우선 탐색)</h1>
<p>BFS(Breadth First Search)는 너비 우선 탐색이라는 의미로 트리의 같은 레벨에 있는 노드들을 먼저 방문 후에 다음 레벨로 이동해 탐색하는 방식이다.</p>
<p>BFS는 큐 자료구조를 이용해 구현할 수 있다.
예를 들어, 아래와 같은 트리를 BFS로 탐색한다고 가정해보면</p>
<pre><code>      A
     / \
    B   C
   / \   \
  D   E   F</code></pre><p>BFS 탐색 순서는 A → B → C → D → E → F 순서이다.
이 순서를 어떻게 큐를 사용해 구현하면</p>
<p><strong>BFS 탐색 과정</strong></p>
<ol>
<li>처음에는 루트 노드 A를 큐에 넣는다.<ul>
<li>큐 상태: <code>[A]</code></li>
</ul>
</li>
<li>큐에서 A를 꺼내고, A의 자식들 B와 C를 큐에 넣는다.<ul>
<li>큐 상태: <code>[B, C]</code> → 탐색: <code>[A]</code></li>
</ul>
</li>
<li>큐에서 B를 꺼내고, B의 자식들 D와 E를 큐에 넣는다.<ul>
<li>큐 상태: <code>[C, D, E]</code> → 탐색: <code>[A, B]</code></li>
</ul>
</li>
<li>큐에서 C를 꺼내고, C의 자식 F를 큐에 넣는다.<ul>
<li>큐 상태: <code>[D, E, F]</code> → 탐색: <code>[A, B, C]</code></li>
</ul>
</li>
<li>큐에서 D를 꺼내고, D는 자식이 없으므로 아무것도 하지 않는다.<ul>
<li>큐 상태: <code>[E, F]</code> → 탐색: <code>[A, B, C, D]</code></li>
</ul>
</li>
<li>큐에서 E를 꺼내고, E는 자식이 없으므로 아무것도 하지 않는다.<ul>
<li>큐 상태: <code>[F]</code> → 탐색: <code>[A, B, C, D, E]</code></li>
</ul>
</li>
<li>큐에서 F를 꺼내고, F는 자식이 없으므로 아무것도 하지 않는다.<ul>
<li>큐 상태: <code>[]</code> → 탐색: <code>[A, B, C, D, E, F]</code> → <strong>탐색 종료</strong></li>
</ul>
</li>
</ol>
<p>이렇게 큐를 이용하면 너비 기반 탐색과 동일하게 탐색을 할 수 있다.
FIFO(선입선출)이기 때문에 먼저 들어온 노드들이 먼저 처리되기 때문에, 같은 레벨에 있는 노드들(B와 C)가 먼저 탐색되고, 그다음 레벨인 D, E, F가 탐색된다.</p>
<br>

<ul>
<li><p>장점</p>
<ul>
<li>최단 경로 탐색에 유리하다.</li>
<li>BFS는 루트에서 가장 가까운 노드부터 탐색하는 방법이기 때문에 루트에서부터 노드까지의 레벨이 낮을수록 경로가 짧아진다.</li>
<li>다시 말해, <strong>루트에서 해당 노드까지 가는 길의 깊이가 짧을수록 그 경로가 더 짧은 경로</strong>이다.</li>
<li>위의 트리 기준으로 루트에서 <strong>레벨 1</strong>에 있는 노드들(<code>B</code>, <code>C</code>)이 <strong>가장 가까운 경로</strong>를 가지며, <strong>레벨 2</strong>에 있는 노드들(<code>D</code>, <code>E</code>, <code>F</code>)는 그보다 더 먼 경로를 가지고 있다</li>
</ul>
</li>
<li><p>단점</p>
<ul>
<li>큐에 노드를 계속 저장해야 하므로, 메모리 사용량이 많아질 수 있다.</li>
</ul>
</li>
</ul>
<hr>
<h1 id="커맨드-패턴">커맨드 패턴</h1>
<p>커맨드 패턴은 자신의 행동을 실행하고 Undo로 돌아갈 수 있는(혹은 그와 같은 효과를 내는) 패턴이다.
포토샵 같은 프로그램 문서 툴 등등에 쓰이는 대표적인 패턴 중 하나다.</p>
<p>내가 알기론 행동을 class같은 데이터로 만들어 일정량을 Stack 같은 곳에 저장하고 하나씩 빼면서 Undo할 수 있는 걸로 안다.</p>
<pre><code class="language-cpp">class ICommand
{
public:
    virtual void Execute() = 0;
    virtual void Undo() = 0;
    virtual ~ICommand() {}
};

class Light
{
public:
    void TurnOn()
    {
        std::cout &lt;&lt; &quot;불을 켭니다.&quot; &lt;&lt; std::endl;
    }

    void TurnOff()
    {
        std::cout &lt;&lt; &quot;불을 끕니다.&quot; &lt;&lt; std::endl;
    }
};

class LightOnCommand : public ICommand
{
private:
    Light* light;

public:
    LightOnCommand(Light* l) : light(l) {}

    void Execute() override
    {
        light-&gt;TurnOn();
    }

    void Undo() override
    {
        light-&gt;TurnOff();
    }
};

class LightOffCommand : public ICommand
{
private:
    Light* light;

public:
    LightOffCommand(Light* l) : light(l) {}

    void Execute() override 
    {
        light-&gt;TurnOff();
    }

    void Undo() override
    {
        light-&gt;TurnOn();
    }
};

class RemoteControl
{
private:
    ICommand* command;

public:
    void setCommand(ICommand* c)
    {
        command = c;
    }

    void pressButton()
    {
        command-&gt;Execute();
    }

    void pressUndo()
    {
        command-&gt;Undo();
    }
};


int main()
{
    Light livingRoomLight;

    LightOnCommand lightOn(&amp;livingRoomLight);
    LightOffCommand lightOff(&amp;livingRoomLight);

    RemoteControl remote;

    // 불 켜기 명령 실행
    remote.setCommand(&amp;lightOn);
    remote.pressButton();

    // 불 끄기 명령 실행
    remote.setCommand(&amp;lightOff);
    remote.pressButton();

    // 직전 명령 취소 (불 켜기)
    remote.pressUndo();

    return 0;
}</code></pre>
<hr>
<h2 id="여행경로-문제">여행경로 문제</h2>
<p><a href="https://school.programmers.co.kr/learn/courses/30/lessons/43164">https://school.programmers.co.kr/learn/courses/30/lessons/43164</a></p>
<pre><code class="language-cpp">bool Travel(int index, vector&lt;vector&lt;string&gt;&gt;&amp; leftTickets, vector&lt;string&gt;&amp; travels)
{
    vector&lt;string&gt; ticket = leftTickets[index];
    string now = ticket[1];

    travels.push_back(now);
    leftTickets.erase(leftTickets.begin() + index);

    bool result = false;
    for (int i = 0; i &lt; leftTickets.size(); i++)
        if (now == leftTickets[i][0])
            result |= Travel(i, leftTickets, travels);

    //중간에 이거 안되는데? 하면 다시 leftTrickets를 채워서 뒤로 보내야돼
    //완료면 그 계산은 거기서 끝

    if (result)
        return true;
    else
    {
        if (leftTickets.size() == 0)
            return true;
        else
        {
            if (index &lt; leftTickets.size())
                leftTickets.insert(leftTickets.begin() + index, ticket);
            else
                leftTickets.push_back(ticket);

            travels.pop_back();

            return false;
        }
    }
}

vector&lt;string&gt; solution(vector&lt;vector&lt;string&gt;&gt; tickets)
{
    for (int i = 0; i &lt; tickets.size() - 1; i++)
        for (int j = 0; j &lt; tickets.size() - 1; j++)
            if (tickets[j][0] &gt; tickets[j + 1][0] || tickets[j][1] &gt; tickets[j + 1][1])
                swap(tickets[j], tickets[j+1]);

    vector&lt;string&gt; travels;
    for (int i = 0; i &lt; tickets.size(); i++)
        if (tickets[i][0] == &quot;ICN&quot;)
        {
            travels.push_back(&quot;ICN&quot;);

            vector&lt;vector&lt;string&gt;&gt; clone(tickets);
            Travel(i, clone, travels);

            if (clone.size() == 0)
                break;

            //실패했으면 초기화
            travels.clear();
        }

    return travels;
}</code></pre>
<p>Graph 혹은 map을 이용해 푸는게 훨씬 편하고 빨랐을 것 같다.
데이터를 정리하고 DFS를 하는게 훨씬 맞는 방식이었던 것 같다.</p>
<p>중간에 <code>sort(tickets.begin(), tickets.end(), [](vector&lt;string&gt;&amp; first, vector&lt;string&gt;&amp; second))</code>를 이용해 sort를 하려고 했는데 계속 assert가 떠서 원인을 모르겠어서 그냥 직접 정렬했다.</p>
<hr>
<p>무조건 int를 쓰지 말고 int32를 써라</p>
<p>레퍼런스 타입으로 다른 곳에 공개할 때 Get이 아닌 Acess를 붙여 공개하는게 좋아보임
예)
const FString&amp; GetName();
FString&amp; AcessName();</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[트리, 그래프]]></title>
            <link>https://velog.io/@kim_yw/%EB%B0%B1%ED%8A%B8%EB%9E%98%ED%82%B92-%ED%8A%B8%EB%A6%AC-%EA%B7%B8%EB%9E%98%ED%94%84</link>
            <guid>https://velog.io/@kim_yw/%EB%B0%B1%ED%8A%B8%EB%9E%98%ED%82%B92-%ED%8A%B8%EB%A6%AC-%EA%B7%B8%EB%9E%98%ED%94%84</guid>
            <pubDate>Mon, 24 Mar 2025 10:42:58 GMT</pubDate>
            <description><![CDATA[<h1 id="트리">트리</h1>
<p>트리(Tree)는 계층적(hierarchical) 데이터 구조로 여러 개의 노드(node)가 부모-자식 관계로 연결되어 있는 자료구조이다.</p>
<pre><code>      A (루트)
     / \
    B   C
   /     \
  D       E</code></pre><h2 id="bst">BST</h2>
<p>이진 탐색 트리(BST)는 이전 트리의 한 종류로, 탐색에 최적화된 구조를 가진다.
탐색, 삽입, 삭제가 O(log N)의 시간 복잡도를 가진다.
아래의 규칙을 지켜야한다.</p>
<ol>
<li>왼쪽 서브트리의 값 &lt; 부모 노드의 값</li>
<li>오른쪽 서브트리의 값 &gt; 부모 노드의 값</li>
<li>이러한 규칙이 모든 노드에 재귀적으로 적용됨</li>
</ol>
<hr>
<h1 id="그래프">그래프</h1>
<p>그래프(Graph)는 정점(Vertex)와 간선(Edge)로 이루어진 비선형 데이터 구조이다.
트리는 특정한 계층 구조를 가졌지만, 그래프는 트리보다 더 자유로운 형태를 가지고 있다.
그래프는 모든 방향으로 연결될 수 있는 자료구조이다.</p>
<p>그래프는 현실 세계의 다양한 관계를 표현하는데 유용하다.</p>
<ul>
<li>SNS -&gt; 사람(노드)들이 친구 관계(간선)로 연결된 형태</li>
<li>교통망 -&gt; 도시(노드) 간의 도로(간선) 연결</li>
<li>웹 페이지 구조 -&gt; 웹 페이지(노드)들이 링크(간선)로 연결</li>
</ul>
<p>그래프는 아래로 이루어져있다.</p>
<ul>
<li>정점(Vertex) : 그래프에서 데이터를 담고 있는 요소<ul>
<li>노드(Node)라고도 부른다.</li>
</ul>
</li>
<li>간선(Edge) : 노드 간의 연결을 의미함</li>
</ul>
<br>

<p>그래프는 </p>
<ul>
<li>방향성이 있을 수도 있고 없을 수도 있다.</li>
<li>가중치가 있을 수도 있고 없을 수도 있다.</li>
<li>순환이 있을 수도 있고 없을 수도 있다.</li>
</ul>
<br>

<p>그래프를 컴퓨터에서 표현할 때 크게 2가지로 표현할 수 있데 오늘은 인접 행렬만 알아본다.</p>
<h3 id="인접-행렬adjacenct-matrix">인접 행렬(Adjacenct Matrix)</h3>
<pre><code>      A  B  C  D
    ----------------
  A | 0  1  0  1
  B | 1  0  1  1
  C | 0  1  0  1
  D | 1  1  1  0</code></pre><p>위의 인접 행렬의 경우에는 비가중치 그래프이다.
가중치라고 하면 1 대신 가중치 값을 저장하면 된다.</p>
<ul>
<li>장점<ul>
<li>간선이 존재하는지 여부를 O(1) 시간에 확인 가능</li>
<li>구현이 단순함 (배열만 사용)</li>
<li>가중치 그래프에 쉽게 확장 가능</li>
</ul>
</li>
<li>단점<ul>
<li>공간 복잡도 O(V²)<ul>
<li>노드 수가 많으면 메모리 낭비 심함</li>
</ul>
</li>
<li>연결된 노드를 찾는 데 O(V)</li>
</ul>
</li>
</ul>
<br>

<h3 id="직접-짠-그래프-코드">직접 짠 그래프 코드</h3>
<p>인접 행렬으로 설정 가능하게 제작했다.</p>
<pre><code class="language-cpp">class Graph
{
public:
    class Edge
    {
    public:
        int toIndex;
        float weight;

    public:
        Edge(int to, float weightValue = 1)
        {
            toIndex = to;
            weight = weightValue;
        }
    };

    class Node
    {
    public:
        int index;
        vector&lt;Edge&gt; edges;

    public:
        void AddEdge(Edge edge)
        {
            edges.push_back(edge);
        }
    };

private:
    vector&lt;Node&gt; nodes;

public:
    void AddNode()
    {
        Node n;
        n.index = nodes.size();
        nodes.push_back(n);
    }

    void AddEdge(int from, int to, float weight = 1)
    {
        nodes[from].AddEdge(Edge(to, weight));
    }

    Node&amp; Get(int index)
    {
        return nodes[index];
    }

    void Print()
    {
        for (int i = 0; i &lt; nodes.size(); i++)
        {
            cout &lt;&lt; &quot;Node&quot; &lt;&lt; i &lt;&lt; &quot; : &quot;;

            for (int j = 0; j &lt; nodes[i].edges.size(); j++)
                cout &lt;&lt; &quot;[&quot; &lt;&lt; nodes[i].edges[j].toIndex &lt;&lt; (nodes[i].edges[j].weight ? &quot;, &quot; + to_string(nodes[i].edges[j].weight) : &quot;&quot;) &lt;&lt; &quot;] &quot;;

            cout &lt;&lt; endl;
        }
    }
};

int main()
{
    Graph g;

    int nodeCount;
    cout &lt;&lt; &quot;노드 갯수 : &quot;;
    cin &gt;&gt; nodeCount;

    for (int i = 0; i &lt; nodeCount; i++)
        g.AddNode();

    for (int i = 0; i &lt; nodeCount; i++)
        for (int j = 0; j &lt; nodeCount; j++)
        {
            //weight 값
            float temp;
            cin &gt;&gt; temp;

            if (temp)
                g.AddEdge(i, j, temp);
        }

    g.Print();
}</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[파사드 패턴, 백트래킹]]></title>
            <link>https://velog.io/@kim_yw/%ED%8C%8C%EC%82%AC%EB%93%9C-%ED%8C%A8%ED%84%B4-%EB%B0%B1%ED%8A%B8%EB%9E%98%ED%82%B9</link>
            <guid>https://velog.io/@kim_yw/%ED%8C%8C%EC%82%AC%EB%93%9C-%ED%8C%A8%ED%84%B4-%EB%B0%B1%ED%8A%B8%EB%9E%98%ED%82%B9</guid>
            <pubDate>Thu, 20 Mar 2025 12:01:39 GMT</pubDate>
            <description><![CDATA[<h1 id="파사드facade-패턴">파사드(Facade) 패턴</h1>
<p>현대의 소프트웨어는 수많은 클래스, 메서드, 라이브러리, API 등으로 이루어져있다.
클라이언트가 모든 세부사항을 직접 알고 접근하는 건 복잡하고, 유지보수가 어려워질 수 있다.
그래서 이런 세부 사항을 한곳에서 담당하여 기능을 정의해두고 사용자는 간단하게 사용하는 패턴이다</p>
<p><img src="https://velog.velcdn.com/images/kim_yw/post/74292f6c-3ef1-4a44-bf91-5976d91506b3/image.jpg" alt=""></p>
<ul>
<li><p>Facade (파사드)
클라이언트가 직접 사용하게 되는 단순한 인터페이스를 제공한다.
클라이언트의 요청을 받아 적절한 서브시스템으로 전달하고 처리한다.</p>
</li>
<li><p>Subsystem Classes (서브시스템 클래스)
실제로 기능을 수행하는 여러 클래스로, 파사드가 이들을 내부적으로 호출하여 작업을 처리한다.</p>
</li>
<li><p>Client (클라이언트)
파사드 인터페이스를 통해 서브시스템의 기능을 사용한다.</p>
</li>
</ul>
<br>

<p>어째 말만 들으면 &#39;그냥 평소에 하던거 아닌가? GameManager 같은 Manager 같은걸로 하는거잖아&#39;라고 할 수 있다.
근데 맞다. 이미 그런 식으로 모아서 제작하고 실제 사용은 간단하게 정리를 해두는 것이 파사드 패턴인거다.</p>
<hr>
<h1 id="백트래킹backtracking">백트래킹(Backtracking)</h1>
<p>백트래킹은 해를 찾아가는 도중에 ‘이 길은 아니다’가 확정되면, 즉시 되돌아(Backtrack) 가서 다른 경로를 탐색하는 알고리즘 설계 기법이다.</p>
<p>브루트포스와 유사하지만, 가능성이 없는 곳은 빠르게 걸러서 시간을 아끼는 방식이다.
이와 같이 거르는 것을 가지치기(Pruning)라고 한다.</p>
<p>방식에 따라 DFS, BFS로 나뉜다.
DFS는 깊이 우선 탐색이고
BFS는 너비 우선 탐색이다.</p>
<h2 id="n-queen-문제">N-Queen 문제</h2>
<p>백트래킹의 대표적 문제 중 하나로, 체스의 퀸을 N x N 체스판에 N개 배치했을 때 서로를 공격할 수 없는 위치에 놓을수 있는 방법이 있는지 찾는 문제이다.</p>
<p>퀸은 대각선, 수직, 수평을 전부 이동할 수 있으며, 이 퀸들을 서로 공격할 수 없는 위치에 배치하는게 이번 문제의 해다.</p>
<p>Q를 퀸, ■을 남은 칸으로 표현했을 때</p>
<p>Q ■ ■ ■
■ ■ Q ■
■ ■ ■ ■
■ ■ ■ ■</p>
<p>여기까지 배치하면 다음 Q을 배치할 수 없다는 사실을 알 수 있다.
그렇게 되면 이전 단계로 돌아가 다시 배치를 한다.</p>
<p>Q ■ ■ ■
■ ■ ■ Q
■ Q ■ ■
■ ■ ■ ■</p>
<p>여기까지 와도 4번째 줄에 배치할 수 없으니 다시 돌아간다.</p>
<p>■ Q ■ ■
■ ■ ■ Q
Q ■ ■ ■
■ ■ Q ■</p>
<p>최종적으로 이렇게 배치했을 때 조건에 맞게 배치되며 종료되게 된다.</p>
<pre><code class="language-cpp">#include &lt;iostream&gt;
#include &lt;vector&gt;

using namespace std;

// 체스판 출력 함수
void printBoard(const vector&lt;vector&lt;int&gt;&gt;&amp; board, int n)
{
    cout &lt;&lt; &quot;====&quot; &lt;&lt; endl;
    for (int i = 0; i &lt; n; i++) {
        for (int j = 0; j &lt; n; j++) {
            if (board[i][j] == 1) {
                cout &lt;&lt; &quot;Q &quot;;
            }
            else {
                cout &lt;&lt; &quot;. &quot;;
            }
        }
        cout &lt;&lt; endl;
    }
    cout &lt;&lt; &quot;====&quot; &lt;&lt; endl;
}

// 해당 위치가 유망한지 확인하는 함수
bool isPromising(const vector&lt;vector&lt;int&gt;&gt;&amp; board, int row, int col, int n)
{
    // 같은 행은 퀸을 배치하지 않으므로 검사하지 않음

    // 같은 열에 퀸이 있는지 확인
    for (int i = 0; i &lt; row; i++)
        if (board[i][col] == 1)
            return false;

    // 왼쪽 대각선에 퀸이 있는지 확인
    for (int i = row, j = col; i &gt;= 0 &amp;&amp; j &gt;= 0; i--, j--)
        if (board[i][j] == 1)
            return false;

    // 오른쪽 대각선에 퀸이 있는지 확인
    for (int i = row, j = col; i &gt;= 0 &amp;&amp; j &lt; n; i--, j++)
        if (board[i][j] == 1)
            return false;

    // 모든 검사를 통과했으므로 유망함
    return true;
}

// 백트래킹을 사용하여 N-Queen 문제 해결
void solveNQueens(vector&lt;vector&lt;int&gt;&gt;&amp; board, int row, int n)
{
    // 모든 행에 퀸을 배치했으면 해결책 출력
    if (row == n)
    {
        printBoard(board, n);
        return;
    }

    // 현재 행의 각 열에 퀸 배치 시도
    // 4x4 체스판이면 0, 1, 2, 3 열을 차례로 시도
    for (int col = 0; col &lt; n; col++)
    {
        // 해당 위치(row, col)가 유망한지 확인!
        if (isPromising(board, row, col, n))
        {
            // 퀸 배치
            board[row][col] = 1;

            // 다음 행으로 재귀 호출
            solveNQueens(board, row + 1, n);

            // 백트래킹: 현재 위치의 퀸을 제거
            board[row][col] = 0;
        }
        // 유망하지 않으면 퀸을 배치하지 않고 다음 열로 넘어가는 구조
    }
}

int main()
{
    int n = 4; // 4-Queen 문제
    vector&lt;vector&lt;int&gt;&gt; board(n, vector&lt;int&gt;(n, 0)); // 4 x 4 체스판 초기화

    solveNQueens(board, 0, n);
}</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[프록시 패턴, 피드백]]></title>
            <link>https://velog.io/@kim_yw/%ED%94%84%EB%A1%9D%EC%8B%9C-%ED%8C%A8%ED%84%B4</link>
            <guid>https://velog.io/@kim_yw/%ED%94%84%EB%A1%9D%EC%8B%9C-%ED%8C%A8%ED%84%B4</guid>
            <pubDate>Tue, 18 Mar 2025 12:01:34 GMT</pubDate>
            <description><![CDATA[<h1 id="프록시proxy-패턴">프록시(Proxy) 패턴</h1>
<p>어떤 객체에 대한 접근을 제어하거나, 실제 객체의 대리 역할을 수행하는 구조 디자인 패턴이다.
대리인(Proxy)라는 뜻처럼 원본을 대신하여 동작, 접근 제어, 로깅, 캐싱 등을 수행한다.</p>
<p>어댑터 패턴과 다른점은 어댑터 패턴은 인터페이스에 맞는 행동을 하게 Wrapping만 해주는 패턴인데 프록시 패턴은 접근을 제어한다는 것에서 다르다.</p>
<h3 id="예시">예시</h3>
<ul>
<li><p>접근 제어 / 권한 관리
DB같은 직접 접근은 위험한경우
접근하기 전에 권한을 검사해 통과될 경우에만 접근하게 만들 수 있다.</p>
</li>
<li><p>지연 로딩
어떤 객체(예: 대용량 이미지, 네트워크 연결, DB 쿼리)의 생성 비용이 매우 클 때, 필요한 시점까지 실제 객체를 만들지 않고, 프록시로 대체해 둘 수 있음.
예를 들어, “가상 프록시(Virtual Proxy)”를 사용하면, 클라이언트가 실제로 접근하기 전까지 원본 객체를 생성하지 않음으로써 초기 비용을 줄일 수 있다.</p>
</li>
<li><p>로깅 / 트래킹 / 모니터링
메서드 호출이 일어날 때마다 프록시가 중간에 개입하여, 로깅이나 사용 통계를 남길 수 있다.
예를 들어, 네트워크 호출 전/후에 로그를 찍거나, 요청 카운트를 세는 등 부수적인 기능을 쉽게 추가할 수 있다.</p>
</li>
<li><p>네트워크 / 원격 호출
서버에 실제로 연결하고 싶을 때, Proxy가 마치 로컬 객체인 것처럼 보여주고, 내부적으로는 원격 RPC를 수행할 수도 있다. (원격 프록시(Remote Proxy))
내부적으로는 RPC, Socket 통신 등을 통해 서버에 있는 객체를 호출하지만, 클라이언트 입장에서는 지역(Local) 객체처럼 보이게 된다.</p>
</li>
</ul>
<p>ex) 게임에서 상점 지연 로딩</p>
<pre><code class="language-cpp">class IShop
{
public:
    virtual void Enter() = 0;
};

class RealShop : public IShop
{
public:
    void Enter() override
    {
        cout &lt;&lt; &quot;상점에 입장했습니다.&quot; &lt;&lt; endl;
    }
};

class ShopProxy : public IShop
{
private:
    RealShop* _realShop;
    int _playerLevel;
    int _requiredLevel;

public:
    ShopProxy(int playerLevel, int requiredLevel)
    {
        _playerLevel = playerLevel;
        _requiredLevel = requiredLevel;
    }

    void Enter() override
    {
        if (_playerLevel &lt; _requiredLevel)
        {
            cout &lt;&lt; _requiredLevel &lt;&lt; &quot; 레벨 이상만 입장 가능&quot; &lt;&lt; endl;
            return;
        }

        if (!_realShop)
            _realShop = new RealShop();

        _realShop-&gt;Enter();
    }
};

int main()
{
    cout &lt;&lt; &quot;플레이어 레벨 : 5&quot; &lt;&lt; endl;

    IShop* shop = new ShopProxy(5, 10);
    shop-&gt;Enter();

    cout &lt;&lt; endl &lt;&lt; &quot;[플레이어 레벨: 15]&quot; &lt;&lt; endl;

    IShop* highLevelShop = new ShopProxy(15, 10);
    highLevelShop-&gt;Enter();
}</code></pre>
<hr>
<h2 id="피드백">피드백</h2>
<p>GameState는 특정 클라를 특정할 수 없어서
NetMulticast를 써서 모든 클라가 메세지를 받게 구현해야함</p>
<p>Replicated가 되있다고 해도 값이 바뀌자마자 전달되는게 아님
연속되서 바뀌면 텀을 두고 발신함</p>
<p>값이 바뀐다고 바로 전달되는게 아님</p>
<p>값을 수신하는 기준은 네트워크 설정에 따라 다름
기본적으로 Replicated는 UDP임
내 클라에선 잘 되겠지만 실제 네트워크로 들어가면 패킷 손실이 일어날 수 있음</p>
<p>언리얼에도 네트워크를 일부로 구리게 설정해서 테스트해볼 수 있음</p>
<p>보통 GameState or GameMode에서 ServerTick 오차를 얻을 수 있는데 그걸로 타이머를 보정함</p>
<p>GameServerWorldTimeSecond?
지속적으로 서버의 시간을 받아오면서 그걸로 보정을 해야한다 라는 내용이라 함
<a href="https://vorixo.github.io/devtricks/non-destructive-synced-net-clock/">https://vorixo.github.io/devtricks/non-destructive-synced-net-clock/</a></p>
<p><a href="https://dev.epicgames.com/documentation/en-us/unreal-engine/API/Runtime/Engine/GameFramework/AGameStateBase/GetServerWorldTimeSeconds?application_version=4.27">https://dev.epicgames.com/documentation/en-us/unreal-engine/API/Runtime/Engine/GameFramework/AGameStateBase/GetServerWorldTimeSeconds?application_version=4.27</a></p>
<hr>
<p>만약 사용하는 GameMode가 GameModeBase를 상속 받았다면 GameState도 GameStateBase를 반드시 써야함.
BeginPlay가 작동을 안함;</p>
<p>UI에서 커밋시 이벤트는 Enter, 포커스 벗어남, ESC같은 키로 벗어난거 3가지에서 다 실행된다.
특히 Enter로 해버리면 포커스 벗어남이랑 같이 실행되서 2번 실행된다 주의하자</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[어댑터 패턴, 언리얼 네트워크]]></title>
            <link>https://velog.io/@kim_yw/%EC%96%B4%EB%8C%91%ED%84%B0-%ED%8C%A8%ED%84%B4-%EC%96%B8%EB%A6%AC%EC%96%BC-%EB%84%A4%ED%8A%B8%EC%9B%8C%ED%81%AC</link>
            <guid>https://velog.io/@kim_yw/%EC%96%B4%EB%8C%91%ED%84%B0-%ED%8C%A8%ED%84%B4-%EC%96%B8%EB%A6%AC%EC%96%BC-%EB%84%A4%ED%8A%B8%EC%9B%8C%ED%81%AC</guid>
            <pubDate>Thu, 13 Mar 2025 09:14:30 GMT</pubDate>
            <description><![CDATA[<h2 id="어댑터adapter-패턴">어댑터(Adapter) 패턴</h2>
<p>호환되지 않는 인터페이스를 가진 객체를 클라이언트가 원하는 인터페이스에 맞춰주는 역할을 하는 <strong>구조 디자인 패턴</strong>이다.</p>
<p>이는 기존에 있던 것에 맞춰 개발을 해놨는데, 어떠한 이유로 기존에 있던 걸 다른 것으로 교환했을 때 혹은 기존의 것이 바뀔 가능성이 있다면 그 교환된 것에 따라 코드를 다시 짜는건 비효율적일 수 있다.</p>
<p>그럴 때 기존 클래스와 같은 함수를 호출하고, 실제로는 내부에서 바뀐 로직을 처리해주는 일종의 wrapper마냥 작동하게 만들어주는 것이 어댑터 패턴이다.</p>
<pre><code class="language-cpp">class IDuck
{
public:
    virtual void Quack() = 0;
    virtual void Fly() = 0;
};

class ITurkey
{
public:
    virtual void Gobble() = 0;
    virtual void FlyShortDistance() = 0;
};

class MallardDuck : public IDuck
{
public:
    void Quack() override
    {
        cout &lt;&lt; &quot;꽉&quot; &lt;&lt; endl;
    }

    void Fly() override
    {
        cout &lt;&lt; &quot;날아용~&quot; &lt;&lt; endl;
    }
};

class WildTurkey : public ITurkey
{
public:
    void Gobble() override
    {
        cout &lt;&lt; &quot;gobble gobble&quot; &lt;&lt; endl;
    }

    void FlyShortDistance() override
    {
        cout &lt;&lt; &quot;짧게 날아용...&quot; &lt;&lt; endl;
    }
};

class TurkeyAdapter : public IDuck
{
public:
    ITurkey* turkey;

    TurkeyAdapter(ITurkey* turkey)
    {
        this-&gt;turkey = turkey;
    }

    void Quack() override
    {
        // Duck의 Quack()을 Turkey의 Gobble()로 변환
        turkey-&gt;Gobble();
    }

    void Fly() override
    {
        // 오리는 멀리 날 수 있지만, 칠면조는 짧은 거리만 날 수 있으므로
        // 여러 번 호출해 보정하는 방식으로 처리함
        for (int i = 0; i &lt; 5; i++)
        {
            turkey-&gt;FlyShortDistance();
        }
    }
};


int main()
{
    IDuck* duck = new MallardDuck();
    duck-&gt;Quack(); 
    duck-&gt;Fly();   

    //ITurkey는 IDuck과 호환되지 않음
    ITurkey* turkey = new WildTurkey();

    //칠면조를 오리처럼 사용하고 싶다면, TurkeyAdapter를 이용
    IDuck* turkeyAdapter = new TurkeyAdapter(turkey);
    turkeyAdapter-&gt;Quack(); // 내부적으로 turkey.Gobble() 호출
    turkeyAdapter-&gt;Fly();   // 내부적으로 여러 번 FlyShortDistance() 호출
}</code></pre>
<hr>
<h1 id="언리얼-네트워크">언리얼 네트워크</h1>
<h2 id="dedicated-server">Dedicated Server</h2>
<p>Dedicated Server는 전용 서버라는 뜻으로 서버의 로직만 추려서 빌드 된 버전을 말한다.</p>
<ul>
<li>별도 빌드 : 필요</li>
<li>클라이언트 기능 포함 : 안 함</li>
<li>보안 : 좋음</li>
<li>용도 : 독립 서버를 갖춘 게임 환경</li>
<li>개발 난이도 : 상대적으로 깔끔</li>
</ul>
<h2 id="listen-server">Listen Server</h2>
<p>Listen Server는 클라이언트 중 하나가 서버의 기능을 겸직하고 있는 형태다.</p>
<ul>
<li>별도 빌드 : 불필요</li>
<li>클라이언트 기능 포함 : 네</li>
<li>보안 : 해킹에 취약</li>
<li>용도 : LAN 환경에서 근거리 그룹</li>
<li>개발 난이도 : 클라이언트와 서버의 기능이 합쳐져 있으므로 권한 체크 필수</li>
</ul>
<hr>
<p>간단한 채팅 프로그램으로 네트워크를 테스트하는 도중에 생긴 문제이다.</p>
<p>언리얼에서 멀티 플레이를 실험할 시 <code>새 에더터 창(PIE)</code>에서 실행하면 엔진에서 실행되는 걸로 아는데 <code>독립형 게임</code>으로 실행할 시 그나마 실제 빌드한 게임과 유사하게 실행되는 것으로 안다.</p>
<p><img src="https://velog.velcdn.com/images/kim_yw/post/3186b9ef-0cbc-4b49-b8e5-f76b2085fdce/image.png" alt=""></p>
<p>허나 보던 강의대로 따라하며 네트워크를 익히는 도중 PIE에선 메세지가 전달되는데 독립형 게임에선 전달이 되지 않는 문제가 있었다.</p>
<p>간단한 채팅을 전달하는 것인데 문제가 생겨 찾아보니 결국 HasAuthority() 함수의 문제였다.
유니티의 Mirror(예전 UNet)에선 Authority가 해당 오브젝트의 주인인 클라이언트이기에 문제점을 잘 찾지 못했는데, 언리얼에선 Authority가 기본적으론 서버에게 있다고한다.
GameMode로 자동으로 생성된 PlayerController의 Authority가 서버에게 있어 HasAuthority()를 기준으로 메세지를 출력하니 클라이언트에선 뜨지 않고 서버에서만 뜨는 문제였다.</p>
<p>해당 Controller가 자신 클라이언트가 주인인지 알기 위해서는 IsLocalController()를 사용하면 된다.</p>
<p><img src="https://velog.velcdn.com/images/kim_yw/post/42554f69-6109-4b1b-950a-a797577578a4/image.png" alt=""></p>
<p>언리얼에선 Authority가 대부분 서버에 있고 클라는 그 액터를 복사하는 방식이라고 한다.</p>
<hr>
<h3 id="질문--답변">질문 &amp; 답변</h3>
<p>튜터님께 질문 후 답변을 정리했다.</p>
<br>

<p>Q. 멀티 환경에서 GameMode로 인한 PlayerController 등의 자동 생성 액터의 생성 타이밍이 어떻게 되나요?</p>
<p>A. 클라는 클라대로 생성되고 서버는 서버대로 생성하는데, 생성 후 서버에서 Replication 활성화 된 액터는 네트워크 ID가 같다면 Link 상태가 되고 서버가 Authority를 가지게 된다.</p>
<p>연결되는 타이밍은 자신이 알기론 Beginplay가 불러지고 그 후 링크될거다.
BeginPlay는 멀티 환경으로 가면 여러번 호출될 수도 있는걸로 안다.</p>
<p>실행 순서는 정해져있지 않으니 염두해놓고 짜야한다.
클라쪽에선 서버쪽이 먼저인지 클라쪽이 먼저인지는 확정이 아니다.</p>
<p>Network Connection이 완벽하게 되면 어떤 함수 실행되는데 그 함수에서 초기화를 연결에 관련된 작업을 실행하는 등의 방법이 있다.
First Replication을 확보할 수 있는걸로 안다.
그 때가 어떻게보면 진짜 BeginPlay인거다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[알고리즘 문제, 언리얼 네트워크]]></title>
            <link>https://velog.io/@kim_yw/%EC%95%8C%EA%B3%A0%EB%A6%AC%EC%A6%98-%EB%AC%B8%EC%A0%9C</link>
            <guid>https://velog.io/@kim_yw/%EC%95%8C%EA%B3%A0%EB%A6%AC%EC%A6%98-%EB%AC%B8%EC%A0%9C</guid>
            <pubDate>Wed, 12 Mar 2025 08:47:06 GMT</pubDate>
            <description><![CDATA[<p>bitmasking을 이용한 문제</p>
<p>Q. 길이가 n(1 ≤ n ≤ 15)인 배열(또는 벡터)이 있는데 이 배열로부터 만들어질 수 있는 모든 부분집합의 합을 전부 구하여, 부분집합의 합 중에서 유일한 값들의 개수를 출력하는 프로그램을 짜보시오.</p>
<pre><code class="language-cpp">void solution()
{
    vector&lt;int&gt; nums = { 1, 2, 3  };
    set&lt;int&gt; sumSet;

    for (int i = 0; i &lt; (1 &lt;&lt; nums.size()); i++)
    {
        cout &lt;&lt; &quot;[ &quot;;
        int sum = 0;

        for (int j = 0; j &lt; nums.size(); j++)
        {
            if (i &amp; (1 &lt;&lt; j))
            {
                cout &lt;&lt; nums[j] &lt;&lt; &quot; &quot;;
                sum += nums[j];
            }
        }

        cout &lt;&lt; &quot;], 합 : &quot; &lt;&lt; sum &lt;&lt; endl;
        sumSet.insert(sum);
    }

    cout &lt;&lt; endl;
    cout &lt;&lt; &quot;중복을 제거한 총 합 개수 : &quot; &lt;&lt; sumSet.size() &lt;&lt; endl;
}</code></pre>
<p>비트 연산자를 이용한 방식이 잘 이해가 안됬는데, 이번에 어떻게 사용하는지 감을 좀 잡은 것 같다.</p>
<hr>
<p>지갑크기 구하기
<a href="https://school.programmers.co.kr/learn/courses/30/lessons/86491">https://school.programmers.co.kr/learn/courses/30/lessons/86491</a></p>
<pre><code class="language-cpp">int solution(vector&lt;pair&lt;int, int&gt;&gt; businessCardVec)
{
    int xMax = 0, yMax = 0;

    for (auto&amp; ele : businessCardVec)
    {
        int eleMax = max(ele.first, ele.second);
        int eleMin = min(ele.first, ele.second);
        int* purseMax = xMax &gt; yMax ? &amp;xMax : &amp;yMax;
        int* purseMin = purseMax == &amp;xMax ? &amp;yMax : &amp;xMax;

        if (*purseMax &lt; eleMax)
            *purseMax = eleMax;

        if (*purseMin &lt; eleMin)
            *purseMin = eleMin;
    }

    cout &lt;&lt; &quot;xMax : &quot; &lt;&lt; xMax &lt;&lt; &quot; yMax : &quot; &lt;&lt; yMax &lt;&lt; endl;

    return xMax * yMax;
}</code></pre>
<p>걍 조금 더 쉽게 회전 시켰으면 됬을거같다.</p>
<hr>
<p>수포자 모의고사
<a href="https://school.programmers.co.kr/learn/courses/30/lessons/42840">https://school.programmers.co.kr/learn/courses/30/lessons/42840</a></p>
<pre><code class="language-cpp">vector&lt;int&gt; solution(vector&lt;int&gt; answers)
{
    //수포자 번호, 카운트
    map&lt;int, int&gt; answerMap;

    //수포자 패턴들
    vector&lt;vector&lt;int&gt;&gt; patterns =
    {
        {1, 2, 3, 4, 5},
        {2, 1, 2, 3, 2, 4, 2, 5},
        {3, 3, 1, 1, 2, 2, 4, 4, 5, 5 }
    };

    for (int i = 0; i &lt; answers.size(); i++)
    {
        //수포자들의 패턴
        for (int j = 0; j &lt; 3; j++)
            if (answers[i] == patterns[j][i % patterns[j].size()])
                answerMap[j]++;
    }

    int maxValue = 0;
    for (auto&amp; ele : answerMap)
        maxValue = max(maxValue, ele.second);

    vector&lt;int&gt; result;
    for (auto&amp; ele : answerMap)
        if (ele.second == maxValue)
            result.push_back(ele.first + 1);

    return result;
}</code></pre>
<p>차피 숫자번 째 학생의 번호의 점수를 저자아는거라 map이 아니라 vector로 쓰는게 훨씬 나았을 것 같다.</p>
<hr>
<h1 id="언리얼-네트워크">언리얼 네트워크</h1>
<h2 id="repnotify">RepNotify</h2>
<ul>
<li>용도 : 값 변경 시 <strong><code>서버 → 클라이언트</code></strong> 로 이벤트 호출</li>
<li>호출방법 : 자동</li>
<li>실행 : 클라이언트</li>
<li>네트워크가 연결되어 있으면 신뢰 가능</li>
</ul>
<pre><code class="language-cpp">UPROPERTY(ReplicatedUsing=OnRep_HealthChanged)
int32 PlayerHealth = 100;

UFUNCTION()
void OnRep_HealthChanged();</code></pre>
<h2 id="actorreplication">ActorReplication</h2>
<ul>
<li>용도 : 변수값의 동기화 및 오너쉽 관리로 <strong><code>서버 → 클라이언트</code></strong>로 실행</li>
<li>호출방법 : 자동</li>
<li>실행 : 서버에서 값 변경 후 클라이언트에 반영</li>
<li>네트워크가 연결되어 있으면 신뢰 가능</li>
</ul>
<pre><code class="language-cpp">UPROPERTY(Replicated)
int32 PlayerHealth = 100;</code></pre>
<h2 id="rpcs">RPCs</h2>
<ul>
<li><p>용도 : 서버와 클라이언트 간 양방향 함수 호출 </p>
</li>
<li><p>호출방법 : 수동</p>
</li>
<li><p>실행 : 서버, 클라이언트 둘 다 가능</p>
</li>
<li><p>Reliable(TCP) / Unreliable(UDP) 선택 가능</p>
</li>
<li><p><strong><code>서버-&gt;단일 클라이언트</code></strong> 로 함수를 실행시키고 싶으면 Clinet를 UPROPERTY 안에 넣으면 된다.</p>
</li>
<li><p><strong><code>클라이언트-&gt;서버</code></strong> 로 함수를 실행시키고 싶으면 Server를 UPROPERTY 안에 넣으면 된다.</p>
</li>
<li><p><strong><code>서버-&gt;모든 클라이언트</code></strong> 로 함수를 실행시키고 싶으면 NetMulticast를 UPROPERTY 안에 넣으면 된다.</p>
</li>
</ul>
<pre><code class="language-cpp">UFUNCTION(Server, Reliable)
void ServerFireWeapon();

UFUNCTION(NetMulticast, Unreliable)
void MulticastPlayExplosionEffect();</code></pre>
<p>RPC는 기본적으로 비신뢰성 통신이다.
RPC 호출이 원격 머신에서 확실히 실행되도록 하기 위해서는 Reliable 키워드를 붙이면 된다.</p>
<h3 id="요건-및-주의사항">요건 및 주의사항</h3>
<p>RPC 의 정상 작동을 위해 충족시켜야 하는 요건이 몇 가지 있다.</p>
<ol>
<li>Actor 에서 호출되어야 한다.</li>
<li>Actor 는 빈드시 replicated 여야 한다.</li>
<li>서버에서 호출되고 클라이언트에서 실행되는 RPC 의 경우, 해당 Actor를 실제 소유하고 있는 클라이언트에서만 함수가 실행된다.</li>
<li>클라이언트에서 호출되고 서버에서 실행되는 RPC 의 경우, 클라이언트는 RPC 가 호출되는 Actor를 소유해야 합니다.</li>
</ol>
<blockquote>
<ul>
<li>Multicast RPC 는 예외다.</li>
</ul>
</blockquote>
<ul>
<li>서버에서 호출되는 경우, 서버에서는 로컬에서 실행될 뿐만 아니라 현재 연결된 모든 클라이언트에서도 실행된다.</li>
<li>클라이언트에서 호출되는 경우, 로컬에서만 실행되며, 서버에서는 실행되지 않는다.</li>
<li>현재 멀티캐스트 이벤트에 대해 단순한 throw틀 조절하는 메커니즘이 있다.
멀티캐스트 함수는 주어진 액터의 네트워크 업데이트 기간동안 두 번 이상 리플리케이트되지 않는다.</li>
</ul>
<br>

<h3 id="출처">출처</h3>
<p><a href="%22https://dev.epicgames.com/documentation/ko-kr/unreal-engine/remote-procedure-calls-in-unreal-engine%22">언리얼 RPC</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[팩토리 패턴]]></title>
            <link>https://velog.io/@kim_yw/%ED%8C%A9%ED%86%A0%EB%A6%AC-%ED%8C%A8%ED%84%B4-%EC%96%B8%EB%A6%AC%EC%96%BC-%EB%84%A4%ED%8A%B8%EC%9B%8C%ED%81%AC</link>
            <guid>https://velog.io/@kim_yw/%ED%8C%A9%ED%86%A0%EB%A6%AC-%ED%8C%A8%ED%84%B4-%EC%96%B8%EB%A6%AC%EC%96%BC-%EB%84%A4%ED%8A%B8%EC%9B%8C%ED%81%AC</guid>
            <pubDate>Tue, 11 Mar 2025 02:13:38 GMT</pubDate>
            <description><![CDATA[<h1 id="팩토리factory-패턴">팩토리(Factory) 패턴</h1>
<p>&#39;팩토리 패턴(Factory Pattern)&#39;은 객체 생성 로직을 별도의 &#39;팩토리&#39;(메서드·클래스·인터페이스 등)로 캡슐화하여, 클라이언트 코드가 특정 구체 클래스에 직접 의존하지 않도록 하는 생성(Creational) 디자인 패턴 계열을 가리킨다.</p>
<blockquote>
<p>대표적인 종류</p>
<ul>
<li>Factory Method 패턴</li>
<li>Abstract Factory 패턴</li>
<li>(확장) Simple Factory(심플 팩토리) 라고 불리는 간단한 변형도 많이 사용됨.</li>
</ul>
</blockquote>
<br>

<p>&#39;팩토리 메서드&#39; 패턴은 추상 클래스(또는 인터페이스)가 객체 생성을 위한 추상 메서드(또는 오버라이드 가능한 메서드)를 정의하고, 이 메서드를 자식 클래스에서 오버라이드하여 구체 객체를 생성하도록 하는 구조이다.</p>
<p>즉, 어떤 객체를 생성할지는 하위 클래스에게 위임하므로, 클라이언트는 추상 팩토리 메서드만 알고 있으면 된다</p>
<h3 id="factory-method-패턴">Factory Method 패턴</h3>
<ol>
<li>서브클래스(별도 Creator)에 따라 다른 종류의 객체를 생성해야 할 때<ul>
<li>예: 문서 애플리케이션(<code>TextApplication</code> vs <code>DrawingApplication</code>), 게임 지역별 몬스터(<code>ForestSpawner</code> vs <code>CaveSpawner</code>) 등</li>
</ul>
</li>
<li>객체 생성 과정을 재정의(오버라이드)하고 싶을 때<ul>
<li>프레임워크가 골격(<code>Creator</code>)만 두고, 사용자가 커스텀 서브클래스에서 생성 방식을 확장/재정의</li>
</ul>
</li>
<li>if-else / switch 분기를 줄이고, 새로운 구체 클래스 추가 시 기존 코드 수정을 최소화하고 싶을 때</li>
</ol>
<br>

<p>** 예) 몬스터 스포너 **</p>
<pre><code class="language-cpp">//구상 Creator 클래스 사용될 인터페이스 &amp; 구현 클래스
class IMonster
{
public:
    virtual void Spawn() = 0;
    virtual void Attack() = 0;
};

class Slime : public IMonster
{
public:
    void Spawn() override
    {
        cout &lt;&lt; &quot;슬라임 스폰&quot; &lt;&lt; endl;
    }

    void Attack() override
    {
        cout &lt;&lt; &quot;질퍽&quot; &lt;&lt; endl;
    }
};

class Goblin : public IMonster
{
public:
    void Spawn() override
    {
        cout &lt;&lt; &quot;고블린 스폰&quot; &lt;&lt; endl;
    }

    void Attack() override
    {
        cout &lt;&lt; &quot;부웅&quot; &lt;&lt; endl;
    }
};

//추상 Creator 클래스
class MonsterSpawner
{
protected:
    virtual IMonster* CreateMonster() = 0; // 팩토리 메서드

public:
    void SpawnMonster()
    {
        IMonster* monster = CreateMonster();
        monster-&gt;Spawn();
        monster-&gt;Attack();
    }
};

// 구상 Creator 클래스
class ForestSpawner : public MonsterSpawner
{
protected:
    virtual IMonster* CreateMonster() override
    {
        return new Slime();
    }
};

class CaveSpawner : public MonsterSpawner
{
protected:
    virtual IMonster* CreateMonster() override
    {
        return new Goblin();
    }
};

// 사용
int main()
{
    MonsterSpawner* forest = new ForestSpawner();
    MonsterSpawner* cave = new CaveSpawner();

    forest-&gt;SpawnMonster(); // 슬라임이 등장
    cave-&gt;SpawnMonster();   // 고블린이 등장
}</code></pre>
<hr>
<h3 id="추상-팩토리abstract-factory-패턴">추상 팩토리(Abstract Factory) 패턴</h3>
<p>추상 팩토리 패턴은 서로 연관된(또는 의존 관계가 있는) 복수의 객체를 한꺼번에 생성해야 할 때 사용하는 패턴이다.</p>
<p>예 : Windows UI 세트(WindowsButton + WindowsCheckbox), Mac UI 세트(MacButton + MacCheckbox)처럼서로 호환되는 객체들을 일관되게 만들고 싶을 때 사용</p>
<hr>
<h3 id="심플-팩토리simple-factory-패턴">심플 팩토리(Simple Factory) 패턴</h3>
<p>심플 팩토리는 정적 메서드 기반의 간단한 팩토리 기법을 가리킨다.
별도의 팩토리 클래스(또는 팩토리 메서드) 하나만 만들어놓고, <code>CreateXXX()</code> 같은 메서드 내부에서 <code>if-else</code>나 <code>switch</code>를 사용하여 구체 객체를 생성해 반환한다.</p>
<br>

<pre><code class="language-cpp">class IDocument
{

};

class WordDocument : public IDocument {};
class PdfDocument : public IDocument { };

class DocumentFactory
{
public:
    static IDocument* CreateDocument(string type)
    {
        if (type == &quot;Word&quot;)
            return new WordDocument();
        else if (type == &quot;PDF&quot;)
            return new PdfDocument();
        else
            throw exception(&quot;Unknown Document Type&quot;);
    }
};

int main()
{
    //사용 시
    IDocument* doc1 = DocumentFactory::CreateDocument(&quot;Word&quot;);
    IDocument* doc2 = DocumentFactory::CreateDocument(&quot;PDF&quot;);
}</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[네트워크 공부, 브루트포스, 비트 연산]]></title>
            <link>https://velog.io/@kim_yw/%EB%84%A4%ED%8A%B8%EC%9B%8C%ED%81%AC-%EA%B3%B5%EB%B6%80-%EB%B8%8C%EB%A3%A8%ED%8A%B8%ED%8F%AC%EC%8A%A4-%EB%B9%84%ED%8A%B8-%EC%97%B0%EC%82%B0</link>
            <guid>https://velog.io/@kim_yw/%EB%84%A4%ED%8A%B8%EC%9B%8C%ED%81%AC-%EA%B3%B5%EB%B6%80-%EB%B8%8C%EB%A3%A8%ED%8A%B8%ED%8F%AC%EC%8A%A4-%EB%B9%84%ED%8A%B8-%EC%97%B0%EC%82%B0</guid>
            <pubDate>Mon, 10 Mar 2025 09:40:57 GMT</pubDate>
            <description><![CDATA[<p>브루트포스란 모든 경우의 수를 전부 시도하는 것을 말한다.</p>
<p>보통 자료의 크기가 작거나, 최적화된 알고리즘을 떠올리기 어려울 때 사용함</p>
<p>비트 기반으로 Masking하는 작업을 bitmasking이라고 한다.</p>
<h3 id="비트연산-문제">비트연산 문제</h3>
<p>비트 연산자를 활용해 {1, 2, 3}의 모든  집합을 출력하라</p>
<p>해답</p>
<pre><code class="language-cpp">vector&lt;int&gt; arr = { 1,2,3 };
int n = arr.size();

for (int i = 0; i &lt; (1 &lt;&lt; n); i++)
{
    for (int j = 0; j &lt; n; j++)
    {
        if (i &amp; (1 &lt;&lt; j))
            cout &lt;&lt; arr[j] &lt;&lt; &quot; &quot;;
    }

    cout &lt;&lt; endl;
}</code></pre>
<hr>
<h1 id="네트워크">네트워크</h1>
<h2 id="노드">노드</h2>
<ul>
<li>네트워크의 기본 단위이다</li>
<li>엔드 노드와 중간 노드 두 종류가 있다</li>
</ul>
<h2 id="링크">링크</h2>
<ul>
<li>노드 간 데이터를 전송하는 연결 (인터넷, Wifi, 블루트스 등등)</li>
</ul>
<h2 id="프로토콜">프로토콜</h2>
<ul>
<li>데이터 전송 규칙 (TCP, UDP, HTTP, FTP 등)</li>
</ul>
<h2 id="네트워크의-배치-형태topology">네트워크의 배치 형태(Topology)</h2>
<ul>
<li><p>P2P
단 2대를 연결하는 경우 사용한다.
1:1 통신이다.</p>
</li>
<li><p>Bus
1개의 BNC 케이블(케이블 TV 동축케이블)에 여러 대의 컴퓨터를 연결하는 구성
전체에 브로드캐스팅하면 수신 측이 자지 것만 채택하고 나머지는 무시함
Bus는 양방향 통신이 불가능함</p>
</li>
<li><p>링(Ring)
1개의 BNC 케이블을 원형으로 만든 뒤에 여러 대의 컴퓨터를 연결하는 구성
버스에 비해 양방향 통신이 가능하지만 버스 같이 초기 네트워크에서 구성되던 형태라 요즘은 쓰지 않는다.</p>
</li>
</ul>
<br>
아래의 기술들이 나오며 위의 기술들은 더이상 사용되지 않음

<ul>
<li><p>스타(Star)
중앙 허브(Hub) 또는 스위치(Switch)에 연결된 구조
대부분의 가정과 사무실, IDC 센터 등에서 많이 채택하는 구조
중간 허브/스위치가 문제가 생기면 다 같이 문제가 생김
요즘은 </p>
</li>
<li><p>메쉬(Mesh)
1:n 구성으로 신뢰성이 높지만, 복잡하고, 비용이 비싸서 군사 영역, 무선 네트워크에 주로 사용함</p>
</li>
<li><p>트리
스타 토폴로지를 확장한 널리 사용되는 형태
회사, IDC 센터, ISP 사업자 등
현재 제일 많이 사용되는 형태</p>
</li>
</ul>
<h2 id="네트워크-레이어-모델osi-모델">네트워크 레이어 모델(OSI 모델)</h2>
<ol>
<li><p>물리 계층(L1)
물리적인 매체로 케이블, 전기신호, 광신호를 직접 관리
이더넷, USB, 블루트스 디바이스 등</p>
</li>
<li><p>데이터링크 계층(L2)
물리 계층에서 전달된 비트를 프레임으로 변환하고, 에러검사, 흐름제어, 링크관리
MAC Adress, MAC 프로토콜, PPP(Point-to-Point protocol) 등</p>
</li>
<li><p>네트워크 계층(L3)
데이터를 목적가지 옮기는 역할로 패킷의 경로를 결정하고 패킷 주소를 관리
IP, IPX, 라우터 등</p>
</li>
<li><p>전송 계층(L4)
데이터 전송을 관리하며, 프름제어, 오류제어, 세그먼트화, 연결 설정/해제를 담당하며 논리적 포트를 사용
TCP(Transmission Control Protocol), UDP(User Datagram Protocol)</p>
</li>
<li><p>세션 계층(L5)
통신의 세션을 설정, 관리하며, 데이터 교환 동기화(Sync)와 체크 포인트 관리를 담당함
RPC(Remote Procedure Call), NetBIOS</p>
</li>
<li><p>프리젠테이션 계층(L6)
데이터 형식을 변환하고 암호화 및 압축 처리를 함
SSL/TLS, JPEG, MPEG 등</p>
</li>
<li><p>어플리케이션 계층(L7)
네트워크 서비스에 접근하고 UI 제공 및 담당
HTTP, FTP, SMTP, DNS 등</p>
</li>
</ol>
<br>

<p>소프트웨어 개발자들은 5 ~ 7번까지의 계층을 컨틀롤할 수 있다. </p>
<p>OSI 모델에 적용되는 계층에 따라 스위치의 종류가 달라진다.</p>
<table>
<thead>
<tr>
<th></th>
<th>참조데이터</th>
<th>용도</th>
</tr>
</thead>
<tbody><tr>
<td>L2 스위치</td>
<td>MAC 주소 테이블</td>
<td>스위치에 물려 있는 디바이스 간의 통신 전송</td>
</tr>
<tr>
<td>L3 스위치</td>
<td>라우팅 테이블</td>
<td>라우팅 테이블을 참조하여 인접 라우터를 기억하고 원거리 통신</td>
</tr>
<tr>
<td>L4 스위치</td>
<td>포트번호</td>
<td>포트번호를 사용해 트래픽을 분배(로드밸런싱)하는 역할</td>
</tr>
<tr>
<td>L7 스위치</td>
<td>데이터기반</td>
<td>HTTP헤더, URL, 쿠키등의 어플리케이션 데이터를 기반으로 한 로드밸런싱, 보안적용</td>
</tr>
</tbody></table>
<h3 id="hub와-switch의-차이">HUB와 SWITCH의 차이</h3>
<ul>
<li>Hub는 데이터 전송 시 연결된 모든 디바이스에 이게 니꺼냐고 물어보고 그렇다는 디바이스에 전달</li>
<li>SWITCH는 MAC Address를 사용해 1번에 전달</li>
</ul>
<p>딱 봐도 보안에서 차이가 나는데 요즘은 SWITCH가 저렴해서 많이 쓴다는거 같음</p>
<h3 id="세션이란">세션이란</h3>
<p>세션은 클라이언트(예: 플레이어)와 서버 간의 통신이 시작되고 유지되는 동안의 논리적 연결이다.
게임 세션을 예로 들면, 세션이 연결되어 있는 동안 플레이어의 상태(State)는 유지되고, 세션이 끊기면 플레이어의 상태는 사라진다.</p>
<h2 id="네트워크의-유형">네트워크의 유형</h2>
<ul>
<li><p>PAN(Personal Area Network)
개인용 네트워크
짧은 거리를 통신함
블루투스, Zigbee, NFC 등</p>
</li>
<li><p>LAN(Local Area Network)
근거리 통신망으로 수백m ~ 몇km까지 통신
사무실, 집, 학교 등의 네트워크에서 사용</p>
</li>
<li><p>MAN(Metropolitan Area Network)
도시 단위 통신망으로 몇km ~ 수십km
도시내 공공기관 등이 사용</p>
</li>
<li><p>WAN(Wide Area Network)
광역 통신망으로 도시, 국가, 대륙 간 수백km ~ 수천km
ISP 네트워크, 다국적 기업의 지사 연결 등에 사용</p>
</li>
<li><p>Internet
지구 전체를 커버하는 통신</p>
</li>
</ul>
<h2 id="대표적인-프로토콜">대표적인 프로토콜</h2>
<ul>
<li><p>TCP
전송 계층에서 작동하며, 데이터를 보낸 후 수신을 확인함 (연결 유지)</p>
</li>
<li><p>UDP
전송 계층에서 작동하며, 데이터를 보내기만 함 (비연결)</p>
</li>
<li><p>IP
네트워크 계층에서 작동하며, IP 주소를 사용해서 패킷을 옮기는 역할로 모든 인터넷의 기반 기술</p>
</li>
</ul>
<h2 id="포트">포트</h2>
<p>전송 계층에서 IP 주소와 함께 사용하는 특정 프로세스나 서비스를 연결하기 위한 주소체계이다.
0~65535의 값을 가진다.</p>
<ul>
<li><p>0~1023
기본 서비스 대역</p>
<ul>
<li>HTTP : 80</li>
<li>HTTPS : 443</li>
<li>FTP : 21</li>
<li>SSH : 22</li>
<li>SMTP : 25</li>
<li>IMAP : 143, 993</li>
</ul>
</li>
<li><p>1024 ~ 49151
응용 프로그램 또는 서비스를 등록된 대역이다.</p>
</li>
<li><p>49152~65535
동적 또는 사설 포트이다.</p>
</li>
</ul>
<hr>
<h2 id="cap-이론">CAP 이론</h2>
<p>분산 시스템의 &#39;동기화 조건 3개를 모두 만족하는 시스템은 없다&#39;는 이론이다.
아래의 3개를 모두 만족하는 시스템은 없다는 이론이라는 소리이다.</p>
<ol>
<li><p>Consistency (일관성)
전체 시스템은 동일한 상태 값을 갖고 있어야 한다.</p>
</li>
<li><p>Availability (가용성)
언제든지 시스템에 접근하여 값을 읽고 쓸 수 있어야 한다.</p>
</li>
<li><p>Partition Tolerance (분할 용인)
시스템을 분할하여 병렬 처리 등이 가능해야 한다.</p>
</li>
</ol>
<h3 id="가용성-보정">가용성 보정</h3>
<p>가용성을 포기하고 일관성을 우선으로 하는 CP (또는 PC) (일관성, 분할 용인을 말하는 거임) 설계의 대표적인 게임이 스타크래프트이다.
전체가 동시에 업데이트 되기 때문에 그렇다.</p>
<p>조종하는 유저조차 RPC로 데이터를 서버로 보내고 서버에서 모든 유저의 컴퓨터에 명령을 보내 그 명령에 해당하는 행동을 하게 만든다.</p>
<p>예를들어 유닛을 눌러 이동 명령을 내리면 바로 사운드 같은 리액션이 출력되지만 서버에서 명령이 도착하기 전까지는 움직이지 않는다.
사운드나 이펙트 같은 리액션을 줘서 마치 바로바로 작동하듯이 플레이어를 속이는 식으로 제작되었다.</p>
<p>특징</p>
<ul>
<li>가용성이 떨어지는 경우 세션을 폭파시키고 남은 플레이어가 승리함</li>
</ul>
<p>ex)
하스스톤, 체스, 장기, 고스톱 같은 보드게임</p>
<h3 id="일관성-보정">일관성 보정</h3>
<p>일관성을 포기하고 가용성을 우선으로 하는 AP(PA) 설계의 대표적인 게임은 최근에 등장하는 대부분의 게임들이다.
게임 클라이언트가 우선 업데이트하고, 후에 일관성을 보정하는 방식을 사용한다.</p>
<p>예를들어 유닛 하나를 움직이면 일단 그 쪽으로 움직이는데 만약 서버에서 명령이 왔는데 그 위치가 다르다면 해당 위치로 Rollback 해버리고 아니면 그대로 냅둔다.</p>
<p>이 방식을 <strong>클라이언트 예측</strong>과 <strong>서버 조정</strong>을 사용한다고 한다.
요즘 대부분의 게임들은 이런 방식을 사용한다.</p>
<p>특징</p>
<ul>
<li>특정 플레이어가 Latency 가 떨어지더라도 세션을 그대로 유지됩니다.</li>
</ul>
<p>ex)
포트나이트</p>
<h3 id="비동기-async">비동기 (Async)</h3>
<p>네트워크 세션을 유지하기 어려운 모바일 환경 등에서 사용하는 방법
&quot;보장된 데이터를 사용&quot;해서 게임 환경을 구축하는 경우이다.</p>
<p>특징</p>
<ul>
<li>느리지만 손실 허용이 안되는 TCP 프로토콜을 사용한다.</li>
<li>이벤트만 서버로 전송하고, 서버는 이벤트를 검증하고 그 결과를 DB에 저장함</li>
<li>게임 플레이어 외에는 다른 플레이어의 접속 여부와 관계없이 게임 진행이 가능함</li>
</ul>
<p>제일 채팅 서버와 비슷함</p>
<p>ex)
클래시 오브 클랜</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[반복자(Iterator) 패턴, 빌더(Builder) 패턴]]></title>
            <link>https://velog.io/@kim_yw/%EB%B0%98%EB%B3%B5%EC%9E%90Iterator-%ED%8C%A8%ED%84%B4-%EB%B9%8C%EB%8D%94Builder-%ED%8C%A8%ED%84%B4</link>
            <guid>https://velog.io/@kim_yw/%EB%B0%98%EB%B3%B5%EC%9E%90Iterator-%ED%8C%A8%ED%84%B4-%EB%B9%8C%EB%8D%94Builder-%ED%8C%A8%ED%84%B4</guid>
            <pubDate>Thu, 06 Mar 2025 02:50:07 GMT</pubDate>
            <description><![CDATA[<h1 id="반복자-패턴">반복자 패턴</h1>
<p>반복자 패턴은 내부 구조를 노출하지 않고, 컬렉션의 요소들을 하나씩 순회(traverse)할 수 있도록 하는 행동(Behavioral) 디자인 패턴이다.
STL의 컨테니어들에 항상 들어가있다.</p>
<p>자료구조마다 순회 방식이 다를 수 있다. (예:) 트리는 깊이 우선 탐색(DFS)과 넓이 우선 탐색(BFS) 등 여러 순회 방식이 있다.)</p>
<p>특정 컬렉션을 순회할 때 내부 구조에 직접 접근할 수 있게 된다면, 컬렉션이 변경될 때 마다 클라이언트 코드도 함께 수정해야 하므로 결합도가 높아진다는 문제가 있다.</p>
<p>반복자들은 보통 다음 요소를 가져오는 메서드, 순회가 끝났는지 확인하는 메서드 등을 포함한 인터페이스를 구현한다.
예를 들어 아래처럼 함수를 구현할 수 있다.</p>
<p><code>bool HasNext()</code> : 다음 요소가 존재하는가?
<code>T Next()</code> : 다음 요소를 가져오고, 내부적으로 포인터(현재 위치)를 한 칸 전진</p>
<hr>
<h3 id="반복자-인터페이스iterator-interface">반복자 인터페이스(Iterator Interface)</h3>
<pre><code class="language-cpp">template &lt;typename T&gt;
class IIterator
{
    bool HasNext();
    T* Next();
};</code></pre>
<p>위에서 말한대로 Interface를 구현한다.
HaseNext가 true라면 Next로 다음 요소에 접근하는 방식</p>
<hr>
<h3 id="컬렉션-인터페이스collection-interface">컬렉션 인터페이스(Collection Interface)</h3>
<pre><code class="language-cpp">template &lt;typename T&gt;
class IAggregate
{
    IIterator&lt;T&gt; CreateIterator();
};</code></pre>
<p><code>CreateIterator()</code> : 이 메서드를 통해 컬렉션에 맞는 반복자를 생성(또는 반환)한다.</p>
<p>주의할 점은, 반복자 반환 타입이 <code>IIterator&lt;T&gt;</code> 라는 점이다.
이로써, 컬렉션 구현체가 무엇이든, 클라이언트는 <strong>반복자 인터페이스</strong>만 보고 순회할 수 있다.</p>
<hr>
<h1 id="빌더-패턴">빌더 패턴</h1>
<br>

<p>빌더(Builder) 패턴은 복잡한 객체의 생성 로직을 객체 내부나 거대한 생성자 대신, 별도의 빌더 객체로 캡슐화하여, 다음을 가능하게한다.</p>
<ul>
<li><p>단계별(점진적) 생성</p>
<ul>
<li>객체를 만들 때 필요한 여러 단계(벽 짓기, 문 설치, 창문 설치 등)를 순차적으로 호출하여 완성해 나갈 수 있음<ul>
<li>필요한 것만 호출해 생성할 수 있음. ex) 테라스를 추가한다던가</li>
</ul>
</li>
</ul>
<br>
</li>
<li><p>동일한 생성 과정을 통해 여러 표현을 지원</p>
<ul>
<li>같은 빌더 인터페이스를 가진 다른 구상 빌더로 교체하면, 전혀 다른 객체도 만들 수 있음</li>
<li>예: <code>CarBuilder</code>가 스포츠카를 만들 수도, SUV를 만들 수도 있음 (옵션/단계 호출 방식만 다름)</li>
</ul>
</li>
</ul>
<br>

<ul>
<li>(옵션) 디렉터(Director)<ul>
<li>객체를 어떻게 조합할지를 별도 클래스(디렉터)에 맡길 수 있음</li>
<li>같은 빌더라도, 디렉터가 호출하는 단계 순서만 바꿔도 결과물이 달라짐</li>
<li>클라이언트는 디렉터에게 “이 빌더 써서 이 제품 만들어줘”라고만 말하면 되고, 세부 생성 로직은 디렉터와 빌더가 처리</li>
<li>제작 대행 같은 느낌이다.</li>
</ul>
</li>
</ul>
<hr>
<pre><code class="language-cpp">class Character
{
public:
    float hp;
    float damage;
    string job;
    string weapon;
};</code></pre>
<p>이 캐릭터 클래스를 빌더로 만든다고 해보자
현재는 너무 간단해서 빌더까지 필요 없겠지만 Builder를 만든다고 생각해보자</p>
<pre><code class="language-cpp">
class CharacterBuilder
{
private:
    Character* character;

public:
    void Reset()
    {
        character = new Character();
    }

    void SetHp(float hp)
    {
        character-&gt;hp = hp;
    }

    void SetDamage(float damage)
    {
        character-&gt;damage = damage;
    }

    void SetJob(string job)
    {
        character-&gt;job = job;
    }

    void SetWeapon(string weapon)
    {
        character-&gt;weapon = weapon;
    }

    Character* GetResult()
    {
        return character;
    }
};</code></pre>
<p>다음과 같이 빌더를 만들었다면
아래와 같이 사용 가능하다.</p>
<pre><code class="language-cpp">int main()
{
    CharacterBuilder builder;

    builder.Reset();
    builder.SetJob(&quot;전사&quot;);
    builder.SetHp(100);
    builder.SetDamage(10);
    builder.SetWeapon(&quot;장검&quot;);

    Character* warrior = builder.GetResult();
}</code></pre>
<p>이런 식으로 순차적으로 구성을 쌓아 제작할 수 있다.</p>
]]></description>
        </item>
    </channel>
</rss>