<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>sungwoo_dev.log</title>
        <link>https://velog.io/</link>
        <description>응애 개발자</description>
        <lastBuildDate>Sat, 28 Feb 2026 08:21:14 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <image>
            <title>sungwoo_dev.log</title>
            <url>https://velog.velcdn.com/images/sungwoo_dev/profile/e15e4518-dbce-4a0e-9810-9619a5cc582e/image.jpeg</url>
            <link>https://velog.io/</link>
        </image>
        <copyright>Copyright (C) 2019. sungwoo_dev.log. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/sungwoo_dev" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[OpenCode 설치 및 사용법 정리 (초보자용 가이드)]]></title>
            <link>https://velog.io/@sungwoo_dev/OpenCode-%EC%84%A4%EC%B9%98-%EB%B0%8F-%EC%82%AC%EC%9A%A9%EB%B2%95-%EC%A0%95%EB%A6%AC-%EC%B4%88%EB%B3%B4%EC%9E%90%EC%9A%A9-%EA%B0%80%EC%9D%B4%EB%93%9C</link>
            <guid>https://velog.io/@sungwoo_dev/OpenCode-%EC%84%A4%EC%B9%98-%EB%B0%8F-%EC%82%AC%EC%9A%A9%EB%B2%95-%EC%A0%95%EB%A6%AC-%EC%B4%88%EB%B3%B4%EC%9E%90%EC%9A%A9-%EA%B0%80%EC%9D%B4%EB%93%9C</guid>
            <pubDate>Sat, 28 Feb 2026 08:21:14 GMT</pubDate>
            <description><![CDATA[<hr>
<h2 id="1-opencode가-뭐야">1. OpenCode가 뭐야?</h2>
<p>OpenCode는 <strong>터미널에서 사용하는 AI 코딩 도구(CLI)</strong>다.
ChatGPT처럼 대화하지만, 내 로컬 프로젝트 폴더 안에서:</p>
<ul>
<li>코드 생성</li>
<li>코드 수정</li>
<li>파일 생성</li>
<li>리팩토링</li>
<li>설명 요청</li>
</ul>
<p>같은 작업을 바로 수행할 수 있다.</p>
<p>즉,</p>
<blockquote>
<p>웹에서 쓰는 GPT →
내 프로젝트 폴더 안에서 직접 코드 작업하는 AI</p>
</blockquote>
<p>라고 이해하면 쉽다.</p>
<hr>
<h1 id="2-설치-방법-windows-기준">2. 설치 방법 (Windows 기준)</h1>
<h2 id="1단계-git-bash-또는-powershell-열기">1단계: Git Bash 또는 PowerShell 열기</h2>
<ul>
<li>Windows 검색 → <code>Git Bash</code></li>
<li>또는 <code>PowerShell</code></li>
</ul>
<h3 id="붙여넣기-단축키">붙여넣기 단축키</h3>
<ul>
<li>Git Bash: <code>Shift + Insert</code></li>
<li>PowerShell: <code>Ctrl + V</code></li>
</ul>
<hr>
<h2 id="2단계-설치-명령어-실행">2단계: 설치 명령어 실행</h2>
<p>아래 명령어 복사해서 붙여넣기:</p>
<pre><code class="language-bash">curl -fsSL https://opencode.ai/install | bash</code></pre>
<p>설치가 완료되면 터미널에서:</p>
<pre><code class="language-bash">opencode</code></pre>
<p>라고 입력했을 때 실행되면 성공.</p>
<hr>
<h1 id="3-provider-연결하기-제일-중요">3. Provider 연결하기 (제일 중요)</h1>
<p>OpenCode는 <strong>AI 모델과 연결해야 작동한다.</strong></p>
<p>이걸 Provider라고 한다.</p>
<p>예시 Provider:</p>
<ul>
<li>OpenAI</li>
<li>Anthropic</li>
<li>로컬 LLM (Ollama 등)</li>
</ul>
<h2 id="openai-연결-예시">OpenAI 연결 예시</h2>
<h3 id="1단계-openai-api-key-발급">1단계: OpenAI API Key 발급</h3>
<ol>
<li><a href="https://platform.openai.com">https://platform.openai.com</a> 접속</li>
<li>API Key 생성</li>
</ol>
<hr>
<h3 id="2단계-환경변수-등록-windows-powershell">2단계: 환경변수 등록 (Windows PowerShell)</h3>
<pre><code class="language-powershell">setx OPENAI_API_KEY &quot;여기에_내_API키&quot;</code></pre>
<p>터미널을 <strong>완전히 닫았다가 다시 열기</strong></p>
<hr>
<h3 id="3단계-provider-설정">3단계: Provider 설정</h3>
<pre><code class="language-bash">opencode config</code></pre>
<p>또는</p>
<pre><code class="language-bash">opencode connect</code></pre>
<p>여기서 OpenAI 선택</p>
<hr>
<h1 id="4-기본-사용법">4. 기본 사용법</h1>
<h2 id="1️⃣-프로젝트-폴더로-이동">1️⃣ 프로젝트 폴더로 이동</h2>
<p>예:</p>
<pre><code class="language-bash">cd D:\UnrealProject\MyGame</code></pre>
<p>그 다음:</p>
<pre><code class="language-bash">opencode</code></pre>
<p>이제 이 폴더 안에서 AI가 작업한다.</p>
<hr>
<h2 id="2️⃣-대화-방식-사용">2️⃣ 대화 방식 사용</h2>
<p>예:</p>
<pre><code>이 프로젝트에 C++로 점프 기능 추가해줘</code></pre><p>또는</p>
<pre><code>이 코드 리팩토링해줘</code></pre><p>또는</p>
<pre><code>현재 폴더 구조 설명해줘</code></pre><hr>
<h1 id="5-opencode로-가능한-것">5. OpenCode로 가능한 것</h1>
<h3 id="코드-생성">코드 생성</h3>
<ul>
<li>C++</li>
<li>Unreal C++</li>
<li>Python</li>
<li>React</li>
<li>서버 코드 등</li>
</ul>
<h3 id="파일-생성">파일 생성</h3>
<ul>
<li>헤더 파일 자동 생성</li>
<li>클래스 구조 생성</li>
<li>README 작성</li>
</ul>
<h3 id="코드-수정">코드 수정</h3>
<ul>
<li>기존 코드 읽고 수정</li>
<li>버그 수정</li>
<li>최적화</li>
</ul>
<hr>
<h1 id="6-opencode의-한계">6. OpenCode의 한계</h1>
<h3 id="❌-자동으로-unreal-editor를-조작하진-못함">❌ 자동으로 Unreal Editor를 조작하진 못함</h3>
<ul>
<li>에디터 버튼 클릭 불가</li>
<li>블루프린트 직접 수정 불가</li>
</ul>
<h3 id="❌-인터넷-검색-자동으로-하지-않음-기본-설정">❌ 인터넷 검색 자동으로 하지 않음 (기본 설정)</h3>
<ul>
<li>로컬 파일 기반 작업</li>
</ul>
<hr>
<h1 id="7-opencode-vs-chatgpt-차이">7. OpenCode vs ChatGPT 차이</h1>
<table>
<thead>
<tr>
<th>구분</th>
<th>ChatGPT</th>
<th>OpenCode</th>
</tr>
</thead>
<tbody><tr>
<td>위치</td>
<td>웹</td>
<td>내 PC 터미널</td>
</tr>
<tr>
<td>파일 접근</td>
<td>불가능</td>
<td>가능</td>
</tr>
<tr>
<td>코드 직접 수정</td>
<td>복붙 필요</td>
<td>직접 수정</td>
</tr>
<tr>
<td>프로젝트 단위 작업</td>
<td>어려움</td>
<td>가능</td>
</tr>
</tbody></table>
<hr>
<h1 id="8-실전-예시-언리얼-c-기준">8. 실전 예시 (언리얼 C++ 기준)</h1>
<h3 id="예시-1">예시 1</h3>
<pre><code>ACharacter 상속받은 클래스에 더블 점프 기능 추가해줘</code></pre><h3 id="예시-2">예시 2</h3>
<pre><code>이 프로젝트에서 B01으로 시작하는 StaticMesh 자동으로 배열로 모으는 코드 만들어줘</code></pre><hr>
<h1 id="9-opencode는-어떤-개념인가">9. OpenCode는 어떤 개념인가?</h1>
<p>이건 이런 개념이다:</p>
<ul>
<li>LLM (Large Language Model)</li>
<li>CLI 기반 AI Agent</li>
<li>로컬 코드 어시스턴트</li>
</ul>
<p>즉,</p>
<blockquote>
<p>GPT를 내 프로젝트 안으로 끌고 들어온 것</p>
</blockquote>
<p>이라고 보면 정확하다.</p>
<hr>
<h1 id="10-추천-사용-흐름">10. 추천 사용 흐름</h1>
<ol>
<li>기능 설계는 ChatGPT에서 정리</li>
<li>실제 코드 생성/수정은 OpenCode</li>
<li>Unreal Editor에서 테스트</li>
<li>다시 OpenCode로 수정</li>
</ol>
<p>이 방식이 가장 효율적이다.</p>
<hr>
<h1 id="11-초보자가-헷갈리는-부분-정리">11. 초보자가 헷갈리는 부분 정리</h1>
<h3 id="q-bash는-폴더마다-하나인가">Q: Bash는 폴더마다 하나인가?</h3>
<p>아니다.
터미널은 하나고, <code>cd</code>로 이동하는 것이다.</p>
<hr>
<h3 id="q-gpt랑-뭐가-다른-거야">Q: GPT랑 뭐가 다른 거야?</h3>
<p>GPT는 대화
OpenCode는 프로젝트 직접 수정</p>
<hr>
<h3 id="q-unreal-c도-가능">Q: Unreal C++도 가능?</h3>
<p>가능.
헤더/CPP 구조 이해하고 생성도 잘한다.</p>
<hr>
<h1 id="마무리-정리">마무리 정리</h1>
<p>OpenCode는:</p>
<ul>
<li>AI를 터미널에서 쓰는 도구</li>
<li>로컬 프로젝트 직접 수정 가능</li>
<li>코딩 생산성을 크게 올려줌</li>
<li>특히 C++/언리얼 프로젝트에 매우 강력함</li>
</ul>
<hr>
]]></description>
        </item>
        <item>
            <title><![CDATA[프로젝트 구성 폴더 역할 정리]]></title>
            <link>https://velog.io/@sungwoo_dev/%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8-%EA%B5%AC%EC%84%B1-%ED%8F%B4%EB%8D%94-%EC%97%AD%ED%95%A0-%EC%A0%95%EB%A6%AC</link>
            <guid>https://velog.io/@sungwoo_dev/%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8-%EA%B5%AC%EC%84%B1-%ED%8F%B4%EB%8D%94-%EC%97%AD%ED%95%A0-%EC%A0%95%EB%A6%AC</guid>
            <pubDate>Tue, 13 Jan 2026 12:11:33 GMT</pubDate>
            <description><![CDATA[<p><strong>Config</strong> : 게임 프로젝트의 설정 값을 보관하는 공간. 이 폴더를 제거하면 게임 프로젝트의 중요한 설정 정보가 날아가므로 항상 보관.</p>
<p><strong>Content</strong> : 게임 프로젝트에 사용하는 에셋을 관리하는 공간. 항상 보관해야 한다.</p>
<p><strong>Intermediate</strong> : 프로젝트 관리에 필요한 임시 파일들을 저장하는 공간. 이 폴더는 제거해도 에디터에 의해 자동으로 재생성된다.</p>
<p><strong>Saved</strong> : 에디터 작업 중에 생성된 결과물을 저장하는 공간. ex)세이브파일, 스크린샷은 모두 이곳에 저장된다. 이 폴더를 제거하면 수동으로 저장한 세이브파일이나 스크린샷등이 삭제될 수 있지만, 게임 프로젝트에는 영향을 주지 않는다.</p>
<p><strong>Binaries</strong> : C++ 코드가 컴파일된 결과물을 저장하는 공간. 이 폴더는 삭제해도 빌드할 때마다 새롭게 생성된다.</p>
<p><strong>Source</strong> : C++ 소스 코드가 위치한 공간. C++ 소스 외에도 언리얼 엔진의 독특한 빌드 설정을 담은 C# 소스파일이 있으며, 폴더를 삭제할 때 프로젝트 구성이 망가지므로 주의해야 한다.</p>
<p><strong>.sln</strong> : C++ 프로젝트를 관리하기 위한 비주얼 스튜디오의 솔루션 파일. 솔루션이 관리하는 각 프로젝트 파일은 Intermediate 폴더 내 ProjectFiles 폴더에 있다. 프로젝트 파일과 솔루션 파일은 삭제하더라도 uproject 파일을 우클릭해 뜨는 Generate Visual Studio project file 메뉴를 선택하면 언제든지 재생성할 수 있다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Git HEAD와 브랜치, 그리고 git reset 한 번에 이해하기]]></title>
            <link>https://velog.io/@sungwoo_dev/Git-HEAD%EC%99%80-%EB%B8%8C%EB%9E%9C%EC%B9%98-%EA%B7%B8%EB%A6%AC%EA%B3%A0-git-reset-%ED%95%9C-%EB%B2%88%EC%97%90-%EC%9D%B4%ED%95%B4%ED%95%98%EA%B8%B0</link>
            <guid>https://velog.io/@sungwoo_dev/Git-HEAD%EC%99%80-%EB%B8%8C%EB%9E%9C%EC%B9%98-%EA%B7%B8%EB%A6%AC%EA%B3%A0-git-reset-%ED%95%9C-%EB%B2%88%EC%97%90-%EC%9D%B4%ED%95%B4%ED%95%98%EA%B8%B0</guid>
            <pubDate>Thu, 08 Jan 2026 01:46:31 GMT</pubDate>
            <description><![CDATA[<p>Git을 쓰다 보면 자주 보게 되는 개념이 있다.</p>
<ul>
<li>HEAD</li>
<li>브랜치(branch)</li>
<li>git reset</li>
</ul>
<p>처음에는 각각 따로 외워야 할 것처럼 보이지만,
사실은 “포인터가 어디를 가리키고 있느냐” 하나로 연결된 개념이다.</p>
<hr>
<h2 id="1-브랜치란-무엇인가">1. 브랜치란 무엇인가</h2>
<p>브랜치는 커밋을 가리키는 포인터다.
즉, 브랜치는 “이 커밋을 기준으로 작업 중이다”라는 표시다.</p>
<h3 id="브랜치-구조-예시">브랜치 구조 예시</h3>
<pre><code>A --- B --- C   (main)</code></pre><ul>
<li>A, B, C는 커밋</li>
<li>main 브랜치는 가장 최신 커밋 C를 가리킨다</li>
</ul>
<p>이 상태에서 새로운 커밋을 만들면:</p>
<pre><code>A --- B --- C --- D   (main)</code></pre><p>main 브랜치는 자동으로 D로 이동한다.
브랜치는 “고정된 개념”이 아니라 계속 움직이는 포인터다.</p>
<hr>
<h2 id="2-head는-무엇인가">2. HEAD는 무엇인가</h2>
<p>HEAD는 “현재 내가 보고 있는 위치”를 가리키는 포인터다.</p>
<p>대부분의 경우 HEAD는 브랜치를 가리킨다.</p>
<pre><code>HEAD -&gt; main -&gt; C</code></pre><p>의미를 풀면:</p>
<ul>
<li>나는 main 브랜치를 체크아웃했고</li>
<li>main은 C 커밋을 가리키고 있으며</li>
<li>현재 작업 디렉토리는 C 기준이다</li>
</ul>
<p>중요한 포인트는 이것이다.</p>
<ul>
<li>브랜치는 커밋을 가리킨다</li>
<li>HEAD는 브랜치를 가리킨다</li>
</ul>
<p>즉,
HEAD → 브랜치 → 커밋
이 구조로 이어진다.</p>
<hr>
<h2 id="3-git-reset은-무엇을-하는가">3. git reset은 무엇을 하는가</h2>
<p>git reset은 브랜치가 가리키는 커밋 위치를 옮기는 명령이다.</p>
<p>즉,</p>
<ul>
<li>HEAD를 움직이는 것이 아니라</li>
<li>브랜치 포인터 자체를 과거 커밋으로 이동시킨다</li>
</ul>
<h3 id="reset-전-상태">reset 전 상태</h3>
<pre><code>A --- B --- C   (main)
              ^
             HEAD</code></pre><h3 id="reset-후-상태">reset 후 상태</h3>
<pre><code>A --- B   (main)
          ^
         HEAD</code></pre><p>C 커밋이 “사라진 것처럼” 보이지만,
정확히 말하면 main 브랜치가 B로 이동한 것이다.</p>
<hr>
<h2 id="4-reset을-하면-왜-head도-같이-이동할까">4. reset을 하면 왜 HEAD도 같이 이동할까?</h2>
<p>HEAD는 항상 “현재 체크아웃된 브랜치”를 따라간다.</p>
<ul>
<li>HEAD → main</li>
<li>main이 가리키는 커밋이 바뀌면</li>
<li>결과적으로 HEAD 기준 커밋도 바뀐다</li>
</ul>
<p>그래서 reset 결과를 보면
“HEAD가 이동한 것처럼” 느껴지는 것이다.</p>
<hr>
<h2 id="5-git-checkout과의-차이">5. git checkout과의 차이</h2>
<p>많이 헷갈리는 부분이라 같이 정리해본다.</p>
<h3 id="git-reset">git reset</h3>
<ul>
<li>브랜치 포인터를 이동시킨다</li>
<li>브랜치 기준 자체가 바뀐다</li>
<li>히스토리를 되돌리는 느낌</li>
</ul>
<h3 id="git-checkout">git checkout</h3>
<ul>
<li>HEAD를 다른 브랜치나 커밋으로 이동시킨다</li>
<li>브랜치는 그대로 둔다</li>
</ul>
<h4 id="checkout으로-커밋을-직접-찍는-경우">checkout으로 커밋을 직접 찍는 경우</h4>
<pre><code>HEAD -&gt; C
main -&gt; D</code></pre><p>이 상태를 Detached HEAD 상태라고 부른다.</p>
<p>이 상태에서 커밋을 만들면
브랜치에 연결되지 않은 “떠 있는 커밋”이 되기 때문에 주의가 필요하다.</p>
<hr>
<h2 id="6-이-개념이-중요한-이유">6. 이 개념이 중요한 이유</h2>
<p>이 구조를 이해하면 다음이 명확해진다.</p>
<ul>
<li>왜 reset을 하면 커밋이 사라진 것처럼 보이는지</li>
<li>왜 checkout으로 과거 커밋을 보면 경고가 뜨는지</li>
<li>왜 브랜치를 기준으로 작업하는 것이 중요한지</li>
</ul>
<p>Git은 결국
“포인터를 어디로 옮기느냐”의 문제다.</p>
<hr>
<h2 id="7-한-번에-정리">7. 한 번에 정리</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>HEAD</td>
<td>현재 사용 중인 브랜치를 가리키는 포인터</td>
</tr>
<tr>
<td>git reset</td>
<td>브랜치 포인터를 과거로 이동</td>
</tr>
<tr>
<td>git checkout</td>
<td>HEAD를 다른 위치로 이동</td>
</tr>
</tbody></table>
<hr>
]]></description>
        </item>
        <item>
            <title><![CDATA[GIT LFS 세팅]]></title>
            <link>https://velog.io/@sungwoo_dev/GIT-LFS-%EC%84%B8%ED%8C%85</link>
            <guid>https://velog.io/@sungwoo_dev/GIT-LFS-%EC%84%B8%ED%8C%85</guid>
            <pubDate>Sat, 06 Dec 2025 11:32:00 GMT</pubDate>
            <description><![CDATA[<p>요약: Git LFS는 <code>git lfs install</code> → <code>git lfs track</code> → 커밋 순서로 설정한다.</p>
<p>Git LFS(Git Large File Storage)를 프로젝트에 적용할 때 기본적으로 필요한 명령어들을 가장 깔끔한 흐름대로 정리해준다.</p>
<hr>
<h2 id="1-lfs-설치처음-한-번만">1. LFS 설치(처음 한 번만)</h2>
<pre><code class="language-bash">git lfs install</code></pre>
<hr>
<h2 id="2-특정-확장자를-lfs로-관리예-psd-png-등">2. 특정 확장자를 LFS로 관리(예: *.psd, *.png 등)</h2>
<pre><code class="language-bash">git lfs track &quot;*.psd&quot;
git lfs track &quot;*.png&quot;
git lfs track &quot;*.uasset&quot;
git lfs track &quot;*.umap&quot;</code></pre>
<p>Unreal Engine 프로젝트라면 보통 이렇게:</p>
<pre><code class="language-bash">git lfs track &quot;*.uasset&quot;
git lfs track &quot;*.umap&quot;</code></pre>
<hr>
<h2 id="3-gitattributes-확인">3. <code>.gitattributes</code> 확인</h2>
<p>위 명령을 실행하면 자동으로 아래처럼 <code>.gitattributes</code>가 생성·수정된다.</p>
<pre><code class="language-bash">cat .gitattributes</code></pre>
<hr>
<h2 id="4-변경-사항-커밋">4. 변경 사항 커밋</h2>
<pre><code class="language-bash">git add .gitattributes
git commit -m &quot;Add Git LFS tracking&quot;</code></pre>
<hr>
<h2 id="5-기존-대용량-파일을-lfs로-전환옵션">5. 기존 대용량 파일을 LFS로 전환(옵션)</h2>
<p>이미 커밋된 파일을 LFS로 옮기려면:</p>
<pre><code class="language-bash">git lfs migrate import --include=&quot;*.uasset,*.umap&quot;</code></pre>
<hr>
<h2 id="6-정상-작동-확인">6. 정상 작동 확인</h2>
<pre><code class="language-bash">git lfs ls-files</code></pre>
<hr>
<h2 id="unreal-engine-프로젝트용-추천-설정-요약">Unreal Engine 프로젝트용 추천 설정 요약</h2>
<pre><code class="language-bash">git lfs install
git lfs track &quot;*.uasset&quot;
git lfs track &quot;*.umap&quot;
git add .gitattributes
git commit -m &quot;Setup LFS for Unreal Engine&quot;</code></pre>
<p>필요하면 추가로 사용하는 확장자도 LFS에 넣으면 된다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[시간복잡도 (time complexity)]]></title>
            <link>https://velog.io/@sungwoo_dev/%EC%8B%9C%EA%B0%84%EB%B3%B5%EC%9E%A1%EB%8F%84-time-complexity</link>
            <guid>https://velog.io/@sungwoo_dev/%EC%8B%9C%EA%B0%84%EB%B3%B5%EC%9E%A1%EB%8F%84-time-complexity</guid>
            <pubDate>Fri, 05 Dec 2025 06:12:02 GMT</pubDate>
            <description><![CDATA[<p>프로그램이 얼마나 빠르게 실행되는지를 판단할 때 가장 많이 사용하는 개념이 시간복잡도다. 코드 실행 시간을 직접 재는 대신, <strong>입력 크기(N)</strong> 가 커질수록 걸리는 시간이 어떻게 증가하는지를 수학적으로 표현한 것이 시간복잡도다.</p>
<hr>
<h2 id="1-시간복잡도가-필요한-이유">1. 시간복잡도가 필요한 이유</h2>
<ul>
<li>실제 실행 시간은 컴퓨터 성능, CPU 상태, 운영체제 등 환경에 따라 달라진다.</li>
<li>하지만 알고리즘의 ‘성능’을 비교하려면 환경에 영향을 받지 않는 공통 기준이 필요하다.</li>
<li>그래서 <strong>입력 크기가 커질 때 시간이 증가하는 패턴</strong>을 기준으로 알고리즘을 비교한다.</li>
</ul>
<hr>
<h2 id="2-big-o-표기법o-표기법">2. Big-O 표기법(O 표기법)</h2>
<p>가장 많이 쓰는 시간복잡도 표기법이며, <strong>가장 빠르게 증가하는 항만 남기고 나머지는 버린 형태</strong>로 표현한다.</p>
<table>
<thead>
<tr>
<th>표기</th>
<th>의미</th>
<th>예시</th>
</tr>
</thead>
<tbody><tr>
<td>O(1)</td>
<td>상수 시간</td>
<td>배열에서 인덱스로 값 꺼내기</td>
</tr>
<tr>
<td>O(log N)</td>
<td>로그 시간</td>
<td>이진 탐색</td>
</tr>
<tr>
<td>O(N)</td>
<td>선형 시간</td>
<td>for문 한 번</td>
</tr>
<tr>
<td>O(N log N)</td>
<td>로그 곱</td>
<td>정렬 알고리즘(퀵/합병/힙)</td>
</tr>
<tr>
<td>O(N²)</td>
<td>제곱</td>
<td>이중 for문</td>
</tr>
<tr>
<td>O(2ⁿ)</td>
<td>지수</td>
<td>부분집합, 백트래킹</td>
</tr>
<tr>
<td>O(N!)</td>
<td>팩토리얼</td>
<td>순열 생성</td>
</tr>
</tbody></table>
<hr>
<h2 id="3-시간복잡도-직관적으로-이해하기">3. 시간복잡도 직관적으로 이해하기</h2>
<h3 id="o1">O(1)</h3>
<p>입력 크기와 상관없이 항상 일정한 시간.</p>
<pre><code class="language-cpp">int x = arr[5];   // 배열 인덱싱</code></pre>
<h3 id="on">O(N)</h3>
<p>입력 크기만큼 작업 반복.</p>
<pre><code class="language-cpp">for (int i = 0; i &lt; N; i++) { ... }</code></pre>
<h3 id="on²">O(N²)</h3>
<p>N이 커질수록 작업량이 기하급수적으로 증가.
이중 반복문이 대표적.</p>
<pre><code class="language-cpp">for (int i = 0; i &lt; N; i++) {
    for (int j = 0; j &lt; N; j++) { ... }
}</code></pre>
<h3 id="olog-n">O(log N)</h3>
<p>전체 범위를 절반씩 줄여가며 탐색.
이진 탐색(Binary Search) 등이 여기에 해당.</p>
<h3 id="on-log-n">O(N log N)</h3>
<p>정렬 알고리즘에서 자주 등장하며, 효율적인 알고리즘의 기준값으로 여겨진다.</p>
<hr>
<h2 id="4-시간복잡도-계산-기본-공식">4. 시간복잡도 계산 기본 공식</h2>
<h3 id="1-가장-많이-반복되는-부분에-집중">1) 가장 많이 반복되는 부분에 집중</h3>
<p>전체 시간에서 <strong>가장 큰 영향</strong>을 미치는 부분만 고려한다.</p>
<h3 id="2-상수는-버린다">2) 상수는 버린다</h3>
<p>예: O(2N) → O(N)</p>
<h3 id="3-가장-큰-차수만-남긴다">3) 가장 큰 차수만 남긴다</h3>
<p>예: O(N² + N) → O(N²)</p>
<hr>
<h2 id="5-실전-예제">5. 실전 예제</h2>
<h3 id="예제-1-이중-반복문">예제 1. 이중 반복문</h3>
<pre><code class="language-cpp">for (int i = 0; i &lt; N; i++) {
    for (int j = 0; j &lt; N; j++) {
        cout &lt;&lt; i &lt;&lt; j &lt;&lt; endl;
    }
}</code></pre>
<p>반복 횟수: N × N = N²
시간복잡도: O(N²)</p>
<h3 id="예제-2-절반씩-줄이는-반복">예제 2. 절반씩 줄이는 반복</h3>
<pre><code class="language-cpp">int x = N;
while (x &gt; 1) {
    x /= 2;
}</code></pre>
<p>반복 횟수: log₂(N)
시간복잡도: O(log N)</p>
<hr>
<h2 id="6-왜-시간복잡도를-알아야-할까">6. 왜 시간복잡도를 알아야 할까?</h2>
<ul>
<li>코드가 <strong>입력 크기 증가에 버틸 수 있는지</strong> 판단할 수 있다.</li>
<li>최적화된 게임 엔진, 서버, 데이터 처리 시스템 개발에서 필수.</li>
<li>C++, Unreal Engine처럼 성능이 중요한 분야에서는 더욱 중요.</li>
</ul>
<hr>
<h2 id="정리">정리</h2>
<ul>
<li>시간복잡도 = 입력 크기(N)에 따라 증가하는 연산량을 수학적으로 표현한 것</li>
<li>가장 중요한 개념은 <strong>Big-O 표기법</strong></li>
<li>목표는 빠른 알고리즘, 즉 <strong>성능 좋은 코드 구조를 만드는 것</strong></li>
</ul>
<hr>
]]></description>
        </item>
        <item>
            <title><![CDATA[OOP 객체지향 5대 원칙 – LSP(리스코프 치환 원칙)]]></title>
            <link>https://velog.io/@sungwoo_dev/OOP-%EA%B0%9D%EC%B2%B4%EC%A7%80%ED%96%A5-5%EB%8C%80-%EC%9B%90%EC%B9%99-LSP%EB%A6%AC%EC%8A%A4%EC%BD%94%ED%94%84-%EC%B9%98%ED%99%98-%EC%9B%90%EC%B9%99</link>
            <guid>https://velog.io/@sungwoo_dev/OOP-%EA%B0%9D%EC%B2%B4%EC%A7%80%ED%96%A5-5%EB%8C%80-%EC%9B%90%EC%B9%99-LSP%EB%A6%AC%EC%8A%A4%EC%BD%94%ED%94%84-%EC%B9%98%ED%99%98-%EC%9B%90%EC%B9%99</guid>
            <pubDate>Fri, 28 Nov 2025 06:35:09 GMT</pubDate>
            <description><![CDATA[<p>객체지향에서 자주 듣는 SOLID 원칙 중 하나가 LSP(Liskov Substitution Principle, 리스코프 치환 원칙)이다. 말만 들으면 어렵지만, 실제로는 “상속을 제대로 쓰기 위한 최소한의 규칙”에 가깝다.</p>
<hr>
<h2 id="1-lsp가-말하고-싶은-핵심-한-줄">1. LSP가 말하고 싶은 핵심 한 줄</h2>
<p><strong>부모 타입에 자식 타입을 넣어도 아무 문제 없이 동작해야 한다.</strong><br>즉, 부모가 기대하는 행동을 자식도 반드시 지켜야 한다.</p>
<hr>
<h2 id="2-왜-이런-원칙이-필요할까">2. 왜 이런 원칙이 필요할까?</h2>
<p>상속은 코드 재사용에 좋지만, 잘못 쓰면 구조가 엉망이 된다.<br>특히 자식 클래스가 부모 클래스의 약속을 깨버리면, 다음과 같은 문제가 생긴다.</p>
<ul>
<li>부모를 기준으로 작성된 코드가 예측대로 작동하지 않음  </li>
<li>자식 클래스가 부모의 일부 기능을 무너뜨림  </li>
<li>유지보수 시 “왜 이 코드가 여기서 깨지지?” 하는 상황 발생</li>
</ul>
<p>LSP는 이런 상황을 방지하기 위한 기준이다.</p>
<hr>
<h2 id="3-대표적인-잘못된-예--직사각형rectangle-vs-정사각형square">3. 대표적인 잘못된 예 – 직사각형(Rectangle) vs 정사각형(Square)</h2>
<p>고전적인 예제로 많이 나오는 케이스다.</p>
<pre><code class="language-cpp">class Rectangle {
public:
    virtual void SetWidth(int w) { width = w; }
    virtual void SetHeight(int h) { height = h; }

protected:
    int width, height;
};

class Square : public Rectangle {
public:
    void SetWidth(int w) override {
        width = height = w;
    }
    void SetHeight(int h) override {
        width = height = h;
    }
};</code></pre>
<p>문제는 부모를 기준으로 코드가 작성되어 있을 때 발생한다.</p>
<pre><code class="language-cpp">void ResizeTo(Rectangle&amp; rect) {
    rect.SetWidth(10);
    rect.SetHeight(20);

    // Rectangle이라면 width=10, height=20을 기대하지만,
    // Square가 들어오면 width=20, height=20이 되어버린다.
}</code></pre>
<p>정사각형은 직사각형의 규칙(가로와 세로가 독립적)을 지킬 수 없으므로 상속 관계 자체가 잘못된 것이다.
LSP를 지키지 않은 대표적 사례.</p>
<hr>
<h2 id="4-lsp를-지키기-위한-실제-기준">4. LSP를 지키기 위한 실제 기준</h2>
<h3 id="1-부모의-기능을-약화시키거나-제한하면-안-된다">1) 부모의 기능을 <strong>약화시키거나 제한하면 안 된다</strong></h3>
<p>예: 부모 함수는 아무 때나 SetWidth 가능하지만,
자식에서 “특정 조건에서만 가능하다”고 바꾼다면 LSP 위반이다.</p>
<h3 id="2-부모의-기대-행동을-깨면-안-된다">2) 부모의 기대 행동을 <strong>깨면 안 된다</strong></h3>
<p>예: 부모의 Move()는 1m 이동인데
자식이 Move()를 2m 이동하게 만들면 안 된다.</p>
<h3 id="3-예외를-더-많이-던져도-안-된다">3) 예외를 더 많이 던져도 안 된다</h3>
<p>부모는 정상 실행인데
자식은 자꾸 예외를 던지면 부모의 계약을 위반한 것이다.</p>
<h3 id="4-부모의-입력출력-타입을-바꾸면-안-된다">4) 부모의 입력/출력 타입을 바꾸면 안 된다</h3>
<p>부모는 int를 받는데
자식은 float만 받는 식으로 바꿔버리면 완전히 어긋난다.</p>
<hr>
<h2 id="5-게임-개발·언리얼-기준으로-본-lsp-예시">5. 게임 개발·언리얼 기준으로 본 LSP 예시</h2>
<h3 id="잘못된-예">잘못된 예</h3>
<pre><code class="language-cpp">class AWeapon {
public:
    virtual void Attack() { /* 기본 공격 */ }
};

class ABow : public AWeapon {
public:
    void Attack() override {
        // 조건: 무조건 화살이 있어야만 Attack 가능
        // 부모는 조건 없이 Attack 가능한데 자식이 제한 조건을 추가함
    }
};</code></pre>
<p>부모 기준으로 Attack()은 어떤 상황에서도 실행되리라 기대하지만,
자식이 자기 마음대로 조건을 추가하면 부모를 치환했을 때 코드가 깨진다.</p>
<h3 id="올바른-수정-방향">올바른 수정 방향</h3>
<ul>
<li>SetArrowCount(), CanShoot() 같은 검증은 Attack() 바깥에서 처리한다.</li>
<li>혹은 Weapon → RangedWeapon → Bow 같은 구조로 레벨을 나눈다.</li>
</ul>
<hr>
<h2 id="6-결국-lsp가-지키려는-것">6. 결국 LSP가 지키려는 것</h2>
<p>LSP는 “상속을 설계할 때 최소한의 약속을 지키도록 강제하는 원칙”이다.</p>
<p>정리하면 다음과 같다.</p>
<ul>
<li>상속은 단순히 코드 재사용 목적이 아니다</li>
<li>부모의 규칙을 자식이 지키지 못한다면 상속 관계를 다시 생각해야 한다</li>
<li>자식 객체를 부모처럼 다뤄도 프로그램이 정상 동작해야 한다</li>
</ul>
<p>이 기준만 지켜도 클래스 구조가 훨씬 견고해진다.</p>
<hr>
<h2 id="7-lsp-정리-한-문장">7. LSP 정리 한 문장</h2>
<p>상속 관계라면 “부모처럼 행동할 수 있는가?”를 반드시 확인해야 한다.</p>
<hr>
<h2 id="끝">끝</h2>
<p>SOLID 중 LSP는 실제로 상속 설계할 때 가장 자주 깨지는 원칙이기도 하다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[void 함수 특징]]></title>
            <link>https://velog.io/@sungwoo_dev/void-%ED%95%A8%EC%88%98-%ED%8A%B9%EC%A7%95</link>
            <guid>https://velog.io/@sungwoo_dev/void-%ED%95%A8%EC%88%98-%ED%8A%B9%EC%A7%95</guid>
            <pubDate>Fri, 28 Nov 2025 05:27:15 GMT</pubDate>
            <description><![CDATA[<hr>
<h1 id="c에서-void-함수는-언제-쓰는가">C++에서 void 함수는 언제 쓰는가?</h1>
<h3 id="한-줄-요약">한 줄 요약</h3>
<p><strong>결과값을 돌려줄 필요가 없을 때 쓰는 함수가 <code>void</code> 함수다.</strong><br>즉, &quot;일만 하고 끝나는 함수&quot;.</p>
<hr>
<h2 id="void-함수가-필요한-순간">void 함수가 필요한 순간</h2>
<p>함수를 만들 때는 보통 두 가지 경우가 있다.</p>
<ol>
<li><strong>행동(Action)을 수행하는 함수</strong></li>
<li><strong>값(Value)을 계산해서 돌려주는 함수</strong></li>
</ol>
<p>여기서 기준은 간단하다.</p>
<ul>
<li><strong>값이 필요 없다 → void</strong></li>
<li><strong>값을 받아서 써야 한다 → void 아님 (int, float, bool 등)</strong></li>
</ul>
<hr>
<h2 id="간단-예시로-이해하기">간단 예시로 이해하기</h2>
<h3 id="1-행동만-하고-끝나는-함수-→-void">1) 행동만 하고 끝나는 함수 → void</h3>
<pre><code class="language-cpp">void PrintHello()
{
    cout &lt;&lt; &quot;Hello!&quot; &lt;&lt; endl;
}</code></pre>
<ul>
<li>화면 출력이라는 행동만 하고 끝.</li>
<li>호출한 쪽에서 받아야 할 값이 없음.</li>
</ul>
<p>사용:</p>
<pre><code class="language-cpp">PrintHello();   // 실행만 하면 됨</code></pre>
<hr>
<h3 id="2-계산한-값을-받아야-하는-함수-→-void-아님">2) 계산한 값을 받아야 하는 함수 → void 아님</h3>
<pre><code class="language-cpp">int Add(int x, int y)
{
    return x + y;
}</code></pre>
<p>사용:</p>
<pre><code class="language-cpp">int result = Add(2, 3);
// result에 5가 담김</code></pre>
<p>값이 필요하기 때문에 절대 void일 수 없음.</p>
<hr>
<h2 id="언리얼-스타일-예시">언리얼 스타일 예시</h2>
<h3 id="문-여는-함수-→-void">문 여는 함수 → void</h3>
<pre><code class="language-cpp">void OpenDoor()
{
    PlayDoorOpenAnimation();
    bIsOpen = true;
}</code></pre>
<p>호출하는 쪽에서는 단지:</p>
<pre><code class="language-cpp">OpenDoor();</code></pre>
<ul>
<li>문을 열라는 “행동”만 필요</li>
<li>문이 열린 후 어떤 숫자나 값이 필요한 것은 아님
그래서 void.</li>
</ul>
<hr>
<h2 id="void인지-아닌지-판단하는-기준매우-중요">void인지 아닌지 판단하는 기준(매우 중요)</h2>
<p>함수 하나 만들기 전에 아래 두 가지를 자신에게 물어보자.</p>
<ol>
<li><p><strong>“이 함수가 만든 값을 호출한 쪽에서 바로 써야 하나?”</strong>
→ <strong>예</strong>: void 쓰면 안 됨.
(반환 타입을 int, float, bool, struct, FVector, AActor* 등으로 지정)</p>
</li>
<li><p><strong>“그냥 이 함수가 어떤 행동만 하면 되고,
결과값은 필요 없나?”</strong>
→ <strong>예</strong>: 이때가 바로 void.</p>
</li>
</ol>
<h3 id="한-줄-정리">한 줄 정리</h3>
<ul>
<li><strong>결과값이 필요 없다 → void</strong></li>
<li><strong>결과값이 필요하다 → void 말고 그 값의 타입</strong></li>
</ul>
<hr>
<h2 id="잘못-설계된-void-예시">잘못 설계된 void 예시</h2>
<p>값을 계산해놓고 반환하지 않으면 이런 문제가 생긴다.</p>
<pre><code class="language-cpp">void MakeDamage(int Base, float Rate)
{
    int Damage = Base * Rate; // 계산함
    // 하지만 아무것도 돌려주지 않음
}</code></pre>
<p>이러면 호출하는 쪽은 Damage를 쓸 수 없음.</p>
<p>개선 버전:</p>
<pre><code class="language-cpp">int MakeDamage(int Base, float Rate)
{
    return Base * Rate;
}</code></pre>
<hr>
<h2 id="마무리">마무리</h2>
<p>정리하면 정말 간단하다.</p>
<ul>
<li><strong>돌려줄 값이 없으면 void</strong></li>
<li><strong>돌려줄 값이 있으면 그 데이터 타입</strong></li>
</ul>
<p>게임 개발, 언리얼 C++에서도 이 기준 그대로 적용된다.
특히 “행동 중심” 기능(문 열기, 체력 깎기, UI 보여주기 등)은 대부분 void이고,
“값 가져오기” 기능은 반환 타입이 있다.</p>
<pre><code></code></pre>]]></description>
        </item>
        <item>
            <title><![CDATA[얕은 복사(Shallow Copy) vs 깊은 복사(Deep Copy)]]></title>
            <link>https://velog.io/@sungwoo_dev/%EC%96%95%EC%9D%80-%EB%B3%B5%EC%82%ACShallow-Copy-vs-%EA%B9%8A%EC%9D%80-%EB%B3%B5%EC%82%ACDeep-Copy</link>
            <guid>https://velog.io/@sungwoo_dev/%EC%96%95%EC%9D%80-%EB%B3%B5%EC%82%ACShallow-Copy-vs-%EA%B9%8A%EC%9D%80-%EB%B3%B5%EC%82%ACDeep-Copy</guid>
            <pubDate>Thu, 20 Nov 2025 06:11:48 GMT</pubDate>
            <description><![CDATA[<p>얕은 복사는 ‘주소만 복사’, 깊은 복사는 ‘내용까지 새로 복사’하는 차이다.</p>
<p>C++에서 <strong>복사 생성자</strong>를 어떻게 구현하느냐에 따라 객체가 ‘얕게 복사’될 수도 있고 ‘깊게 복사’될 수도 있다.<br>특히 <strong>포인터 멤버(int*)를 가진 클래스</strong>라면 둘의 차이가 결과에 큰 영향을 준다.</p>
<hr>
<h2 id="1-얕은-복사-shallow-copy">1. 얕은 복사 (Shallow Copy)</h2>
<h3 id="개념">개념</h3>
<ul>
<li>객체의 멤버 값을 그대로 복사한다.</li>
<li><strong>포인터 멤버가 있다면 주소값만 복사</strong>한다.</li>
<li>즉, <strong>원본 객체와 복사된 객체가 같은 메모리(data)를 공유</strong>하게 된다.</li>
<li>그래서 하나를 수정하면 <strong>둘 다 값이 변한다</strong>.</li>
</ul>
<h3 id="예시-코드">예시 코드</h3>
<pre><code class="language-cpp">class MyArray {
public:
    int size;
    int* data;

    MyArray(int size) {
        this-&gt;size = size;
        data = new int[size];
    }

    // 얕은 복사 생성자
    MyArray(const MyArray&amp; other) {
        this-&gt;size = other.size;
        this-&gt;data = other.data;   // 주소값만 복사됨
    }
};</code></pre>
<h3 id="문제점">문제점</h3>
<ul>
<li>두 객체가 같은 data[]를 바라봄 → <strong>의도치 않은 변화 발생</strong></li>
<li>하나가 소멸되며 delete[] 호출 시 → 다른 객체는 <strong>댕글링 포인터(dangling pointer)</strong>가 됨</li>
</ul>
<hr>
<h2 id="2-깊은-복사-deep-copy">2. 깊은 복사 (Deep Copy)</h2>
<h3 id="개념-1">개념</h3>
<ul>
<li>객체가 가진 <strong>모든 데이터 자체를 새로 복사</strong>한다.</li>
<li>포인터 멤버가 있을 경우
→ <strong>새로운 메모리(new)</strong>를 만들고
→ 원본 배열의 내용을 하나씩 복사한다.</li>
<li>복사된 객체는 <strong>완전히 독립적인 메모리</strong>를 가진다.</li>
</ul>
<h3 id="예시-코드-1">예시 코드</h3>
<pre><code class="language-cpp">class MyArray {
public:
    int size;
    int* data;

    MyArray(int size) {
        this-&gt;size = size;
        data = new int[size];
    }

    ~MyArray() {
        delete[] data;
    }

    // 깊은 복사 생성자
    MyArray(const MyArray&amp; other) {
        this-&gt;size = other.size;
        this-&gt;data = new int[size];      // 새 메모리 확보

        for (int i = 0; i &lt; size; i++) {
            this-&gt;data[i] = other.data[i];   // 데이터 자체 복사
        }
    }
};</code></pre>
<h3 id="장점">장점</h3>
<ul>
<li>복사된 객체는 <strong>원본과 완전히 분리</strong>된다.</li>
<li>서로 영향을 주지 않는다.</li>
<li>소멸자에서도 각자 자기 메모리만 관리하면 된다.</li>
</ul>
<hr>
<h2 id="핵심-차이-요약">핵심 차이 요약</h2>
<table>
<thead>
<tr>
<th>구분</th>
<th>얕은 복사</th>
<th>깊은 복사</th>
</tr>
</thead>
<tbody><tr>
<td>포인터 복사 방식</td>
<td>주소값만 복사</td>
<td>새 메모리 생성 후 값 복사</td>
</tr>
<tr>
<td>두 객체 관계</td>
<td>같은 데이터를 공유</td>
<td>완전히 분리된 개별 객체</td>
</tr>
<tr>
<td>수정 시 영향</td>
<td>서로에게 영향 있음</td>
<td>서로 영향 없음</td>
</tr>
<tr>
<td>delete 실행 시 문제</td>
<td>매우 큰 문제 발생 (중복 해제 위험)</td>
<td>안전</td>
</tr>
</tbody></table>
<hr>
<h2 id="결론">결론</h2>
<ul>
<li><strong>포인터나 동적 메모리를 가진 클래스라면 거의 무조건 깊은 복사</strong>를 구현해야 한다.</li>
<li>얕은 복사는 매우 위험하고, 특별한 목적이 있는 경우에만 사용한다.</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[업캐스팅(Upcasting) & 다운캐스팅(Downcasting)]]></title>
            <link>https://velog.io/@sungwoo_dev/%EC%97%85%EC%BA%90%EC%8A%A4%ED%8C%85Upcasting-%EB%8B%A4%EC%9A%B4%EC%BA%90%EC%8A%A4%ED%8C%85Downcasting</link>
            <guid>https://velog.io/@sungwoo_dev/%EC%97%85%EC%BA%90%EC%8A%A4%ED%8C%85Upcasting-%EB%8B%A4%EC%9A%B4%EC%BA%90%EC%8A%A4%ED%8C%85Downcasting</guid>
            <pubDate>Thu, 20 Nov 2025 01:29:01 GMT</pubDate>
            <description><![CDATA[<p>프로그래밍에서 클래스끼리 <strong>부모-자식 관계</strong>가 있을 때,
참조나 포인터를 어떻게 변환해서 사용하는지에 따라 두 가지 캐스팅이 등장한다.</p>
<hr>
<h1 id="업캐스팅-upcasting">업캐스팅 (Upcasting)</h1>
<p><strong>자식 → 부모로 변환하는 것.</strong>
항상 안전하고, 자동으로 변환된다.</p>
<h3 id="왜-안전할까">왜 안전할까?</h3>
<p>자식 클래스는 부모 클래스의 기능을 모두 포함하고 있기 때문에<br>부모 포인터로 바라봐도 문제가 생기지 않는다.</p>
<h3 id="일반-c-예시">일반 C++ 예시</h3>
<pre><code class="language-cpp">class A {};
class B : public A {};

B* Child = new B();
A* Parent = Child;  // 업캐스팅 (자동)</code></pre>
<h3 id="unreal-c-예시">Unreal C++ 예시</h3>
<pre><code class="language-cpp">AMyCharacter* MyChar = GetWorld()-&gt;SpawnActor&lt;AMyCharacter&gt;();
AActor* ActorPtr = MyChar;  // 업캐스팅</code></pre>
<h3 id="업캐스팅의-특징">업캐스팅의 특징</h3>
<ul>
<li>자동 변환</li>
<li>안전함</li>
<li>포인터/참조에서만 의미 있음</li>
<li>값으로 변환하면 slicing(잘림) 현상 발생</li>
</ul>
<hr>
<h1 id="다운캐스팅-downcasting">다운캐스팅 (Downcasting)</h1>
<p><strong>부모 → 자식으로 변환하는 것.</strong>
항상 위험하며, 반드시 체크해야 한다.</p>
<p>부모 포인터가 실제로 자식 객체를 가리키는지 확신할 수 없기 때문.</p>
<h3 id="일반-c-예시-1">일반 C++ 예시</h3>
<pre><code class="language-cpp">A* Parent = new B();  
B* Child = static_cast&lt;B*&gt;(Parent); // 위험</code></pre>
<h3 id="unreal에서는-cast-사용-안전">Unreal에서는 Cast&lt;&gt; 사용 (안전!)</h3>
<pre><code class="language-cpp">AActor* ActorPtr = GetSomeActor();

AMyCharacter* MyChar = Cast&lt;AMyCharacter&gt;(ActorPtr);

if (MyChar)
{
    // 캐스팅 성공 → ActorPtr이 실제로 AMyCharacter임
}
else
{
    // 캐스팅 실패 → 타입이 다름
}</code></pre>
<p><code>Cast&lt;&gt;</code>는 실패 시 <code>nullptr</code>을 반환하기 때문에
엔진 크래시를 막을 수 있는 가장 안전한 다운캐스팅 방식이다.</p>
<hr>
<h2 id="왜-업캐스팅은-포인터참조에서만-한다고-할까">왜 “업캐스팅은 포인터/참조에서만 한다”고 할까?</h2>
<p>업캐스팅은 객체 자체를 바꾸는 것이 아니라
“가리키는 관점만 부모로 바꾸는 것”이기 때문이다.</p>
<pre><code class="language-cpp">B Child;
A&amp; Ref = Child;   // 참조 업캐스팅
A* Ptr = &amp;Child;  // 포인터 업캐스팅</code></pre>
<p>반대로 값으로 바꾸면 업캐스팅이 아니라
복사 + 잘림(slicing)이 발생하므로 쓰지 않는다.</p>
<pre><code class="language-cpp">B Child;
A Parent = Child; // 잘림 발생 → 비추천</code></pre>
<hr>
<h1 id="unreal에서-자주-쓰는-캐스팅-예시">Unreal에서 자주 쓰는 캐스팅 예시</h1>
<h3 id="1-overlap-이벤트에서-플레이어-판별">1) Overlap 이벤트에서 플레이어 판별</h3>
<pre><code class="language-cpp">void OnOverlap(AActor* OtherActor)
{
    AMyCharacter* MyChar = Cast&lt;AMyCharacter&gt;(OtherActor);
    if (MyChar)
    {
        UE_LOG(LogTemp, Warning, TEXT(&quot;플레이어 진입!&quot;));
    }
}</code></pre>
<h3 id="2-actor-→-특정-component-다운캐스팅">2) Actor → 특정 Component 다운캐스팅</h3>
<pre><code class="language-cpp">UPrimitiveComponent* Comp = Cast&lt;UPrimitiveComponent&gt;(Hit.GetComponent());</code></pre>
<h3 id="3-부모-배열에서-자식-타입-찾기">3) 부모 배열에서 자식 타입 찾기</h3>
<pre><code class="language-cpp">TArray&lt;AActor*&gt; Actors;

for (AActor* Actor : Actors)
{
    ADoorBase* Door = Cast&lt;ADoorBase&gt;(Actor);
    if (Door)
    {
        // Door만 골라서 동작
    }
}</code></pre>
<hr>
<h2 id="정리">정리</h2>
<h3 id="업캐스팅">업캐스팅</h3>
<ul>
<li>자식 → 부모</li>
<li>자동 변환</li>
<li>안전함</li>
<li>포인터/참조에서만 의미 있음</li>
</ul>
<h3 id="다운캐스팅">다운캐스팅</h3>
<ul>
<li>부모 → 자식</li>
<li>위험함</li>
<li>Unreal에서는 <code>Cast&lt;&gt;</code>를 꼭 사용</li>
<li>캐스팅 실패 시 <code>nullptr</code>로 안전하게 처리</li>
</ul>
<hr>
]]></description>
        </item>
        <item>
            <title><![CDATA[객체지향 프로그래밍(OOP) 4대 원칙 정리]]></title>
            <link>https://velog.io/@sungwoo_dev/%EA%B0%9D%EC%B2%B4%EC%A7%80%ED%96%A5-%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%B0%8DOOP-4%EB%8C%80-%EC%9B%90%EC%B9%99-%EC%A0%95%EB%A6%AC</link>
            <guid>https://velog.io/@sungwoo_dev/%EA%B0%9D%EC%B2%B4%EC%A7%80%ED%96%A5-%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%B0%8DOOP-4%EB%8C%80-%EC%9B%90%EC%B9%99-%EC%A0%95%EB%A6%AC</guid>
            <pubDate>Tue, 18 Nov 2025 07:01:01 GMT</pubDate>
            <description><![CDATA[<p>OOP(Object Oriented Programming)는<br>“데이터와 기능을 객체로 묶어 현실을 코드로 표현하는 방식”이다.<br>핵심은 아래 4가지이다.</p>
<hr>
<h1 id="1-캡슐화-encapsulation">1. 캡슐화 (Encapsulation)</h1>
<pre><code>
┌───────────────────────┐
│       Player          │
│ ┌───────────────────┐ │
│ │   private:        │ │   ← 내부 숨김
│ │   Health = 100    │ │
│ └───────────────────┘ │
│                       │
│  public:              │
│   GetHealth()         │ ← 외부 접근 함수
│   TakeDamage()        │
└───────────────────────┘
</code></pre><p>핵심: <strong>중요한 데이터는 감추고, 함수로만 접근하게 한다.</strong></p>
<h3 id="개념">개념</h3>
<ul>
<li>데이터(변수)와 기능(함수)을 객체 내부에 묶는다.</li>
<li>중요한 데이터는 숨기고, 함수로만 접근하도록 만든다.</li>
</ul>
<h3 id="이유">이유</h3>
<ul>
<li>데이터 보호</li>
<li>내부 구조가 바뀌어도 외부 코드가 영향을 덜 받음</li>
</ul>
<h3 id="예시">예시</h3>
<pre><code class="language-cpp">class Player
{
private:
    int Health;

public:
    Player() : Health(100) {}

    int GetHealth() const
    {
        return Health;
    }

    void TakeDamage(int Amount)
    {
        if (Amount &lt; 0) return;
        Health -= Amount;
        if (Health &lt; 0) Health = 0;
    }
};</code></pre>
<hr>
<h1 id="2-상속-inheritance">2. 상속 (Inheritance)</h1>
<pre><code>       Character
    ┌────────────────┐
    │ Move()         │
    │ Jump()         │
    └────────────────┘
             ▲
             │ (is-a)
             │
    ┌────────────────┐
    │     Player     │
    │ Attack()       │ ← 자식만의 기능
    └────────────────┘</code></pre><pre><code>
핵심: **부모 기능 물려받고, 자식은 기능을 추가 확장한다.**</code></pre><h3 id="개념-1">개념</h3>
<ul>
<li>부모 클래스를 기반으로 새로운 자식 클래스를 만드는 것</li>
<li>“is-a 관계”일 때 사용
예) Player is a Character</li>
</ul>
<h3 id="이유-1">이유</h3>
<ul>
<li>공통 기능 재사용</li>
<li>중복 코드 감소</li>
</ul>
<h3 id="예시-1">예시</h3>
<pre><code class="language-cpp">class Character
{
public:
    void Move() {}
    void Jump() {}
};

class Player : public Character
{
public:
    void Attack() {}
};</code></pre>
<p>사용:</p>
<pre><code class="language-cpp">Player P;
P.Move();
P.Attack();</code></pre>
<hr>
<h1 id="3-다형성-polymorphism">3. 다형성 (Polymorphism)</h1>
<pre><code>    Character* Ptr
           │
           ▼
 ┌───────────────────┐
 │   Attack()        │ ← 하나의 인터페이스
 └───────────────────┘
     ▲          ▲
     │ override │ override
     │          │</code></pre><p>┌────────────────┐   ┌────────────────┐
│    Player      │   │     Enemy      │
│ Attack()       │   │ Attack()       │
└────────────────┘   └────────────────┘</p>
<pre><code>
`Ptr-&gt;Attack()` 호출 시:

- Player 객체면 → Player::Attack()
- Enemy 객체면 → Enemy::Attack()

핵심: **같은 함수 이름이지만 객체 타입에 따라 동작이 다르다.**
</code></pre><h3 id="개념-2">개념</h3>
<ul>
<li>같은 함수 이름이지만 실제 실행되는 동작은 타입마다 다름</li>
<li>virtual / override 로 구현</li>
</ul>
<h3 id="이유-2">이유</h3>
<ul>
<li>다양한 객체를 같은 인터페이스로 다룰 수 있음</li>
</ul>
<h3 id="예시-2">예시</h3>
<pre><code class="language-cpp">class Character
{
public:
    virtual void Attack()
    {
        // 기본 공격
    }
};

class Player : public Character
{
public:
    void Attack() override
    {
        // 플레이어 공격
    }
};

class Enemy : public Character
{
public:
    void Attack() override
    {
        // 적 공격
    }
};</code></pre>
<p>사용:</p>
<pre><code class="language-cpp">void DoAttack(Character* C)
{
    C-&gt;Attack(); // 타입별로 다르게 실행됨
}</code></pre>
<hr>
<h1 id="4-추상화-abstraction">4. 추상화 (Abstraction)</h1>
<pre><code>
┌───────────────────────────┐
│        IWeapon             │
│  Fire() = 0 (규칙만 정의) │ ← 인터페이스
└───────────────────────────┘
▲             ▲
│ implement   │ implement
│             │
┌─────────────────┐   ┌─────────────────┐
│       Gun        │   │       Bow        │
│  Fire(): 총 발사 │   │ Fire(): 활 발사 │
└─────────────────┘   └─────────────────┘
</code></pre><p>핵심:<br><strong>사용자는 Fire()만 알면 되고, 내부 구현은 몰라도 된다.</strong></p>
<pre><code></code></pre><h3 id="개념-3">개념</h3>
<ul>
<li>복잡한 내부 구현은 숨기고</li>
<li>꼭 필요한 기능만 외부에 제공하는 것</li>
</ul>
<h3 id="이유-3">이유</h3>
<ul>
<li>코드 단순화</li>
<li>실제 구현이 바뀌어도 외부 코드는 유지됨</li>
</ul>
<h3 id="예시-순수-가상-함수">예시 (순수 가상 함수)</h3>
<pre><code class="language-cpp">class IWeapon
{
public:
    virtual ~IWeapon() {}
    virtual void Fire() = 0; // 반드시 구현해야 한다
};

class Gun : public IWeapon
{
public:
    void Fire() override { /* 총 발사 */ }
};

class Bow : public IWeapon
{
public:
    void Fire() override { /* 활 발사 */ }
};</code></pre>
<p>사용:</p>
<pre><code class="language-cpp">void Use(IWeapon* W)
{
    W-&gt;Fire();
}</code></pre>
<hr>
<h3 id="정리">정리</h3>
<ul>
<li><p>캡슐화
→ 변수 숨기고 함수로만 접근하도록 보호</p>
</li>
<li><p>상속
→ 부모 기능 재사용, 구조적 코드 구성 가능</p>
</li>
<li><p>다형성
→ 같은 함수 이름, 타입마다 다른 동작</p>
</li>
<li><p>추상화
→ 핵심 기능만 공개, 복잡한 구현은 숨김</p>
</li>
</ul>
<hr>
<pre><code>┌─────────────────────────────────────────┐
│             OOP 4대 원칙               │
├─────────────────────────────────────────┤
│ 1. Encapsulation  →  데이터 보호/은닉  │
│ 2. Inheritance     →  부모 기능 재사용 │
│ 3. Polymorphism    →  타입별 다른 동작 │
│ 4. Abstraction     →  핵심 기능만 노출 │
└─────────────────────────────────────────┘
</code></pre><hr>
]]></description>
        </item>
        <item>
            <title><![CDATA[Git 기초 명령어 요약]]></title>
            <link>https://velog.io/@sungwoo_dev/Git-%EA%B8%B0%EC%B4%88-%EB%AA%85%EB%A0%B9%EC%96%B4-%EC%9A%94%EC%95%BD</link>
            <guid>https://velog.io/@sungwoo_dev/Git-%EA%B8%B0%EC%B4%88-%EB%AA%85%EB%A0%B9%EC%96%B4-%EC%9A%94%EC%95%BD</guid>
            <pubDate>Sun, 26 Oct 2025 05:57:36 GMT</pubDate>
            <description><![CDATA[<hr>
<h2 id="📦-기본-명령어">📦 기본 명령어</h2>
<table>
<thead>
<tr>
<th>명령어</th>
<th>설명</th>
</tr>
</thead>
<tbody><tr>
<td><code>clone</code></td>
<td>원격 저장소 복사</td>
</tr>
<tr>
<td><code>add</code></td>
<td>스테이지 영역에 작업 파일 추가</td>
</tr>
<tr>
<td><code>commit</code></td>
<td>세이브 — 스테이지 영역의 파일들을 커밋으로 저장</td>
</tr>
<tr>
<td><code>push</code></td>
<td>원격 저장소에 커밋 업로드</td>
</tr>
</tbody></table>
<hr>
<h2 id="🧱-파일-내용-되돌리기">🧱 파일 내용 되돌리기</h2>
<ul>
<li>특정 파일을 마지막 커밋 상태로 되돌리고 싶다면<br>→ 해당 파일 선택 → <strong>코드 뭉치 버리기</strong> 선택</li>
</ul>
<hr>
<h2 id="🌿-브랜치-branch">🌿 브랜치 (Branch)</h2>
<p><strong>브랜치란:</strong> 기존 내용을 유지한 채 새로운 내용을 추가할 때 사용<br><strong>체크아웃(Checkout):</strong> 특정 브랜치(혹은 커밋)으로 돌아가기</p>
<ul>
<li>SourceTree에서는 브랜치 이름을 <strong>더블클릭</strong>하면 체크아웃 가능</li>
</ul>
<hr>
<h2 id="🔀-병합-merge">🔀 병합 (Merge)</h2>
<h3 id="병합하기-1--fast-forward">병합하기 1 — Fast-forward</h3>
<ul>
<li>헤드 브랜치에 변경 사항이 없고  </li>
<li>병합 대상 브랜치가 헤드로부터 시작된 경우<br>→ <strong>아주 쉽게 병합 가능</strong></li>
</ul>
<h3 id="병합하기-2--일반-병합">병합하기 2 — 일반 병합</h3>
<ul>
<li>헤드 브랜치에 새로운 커밋이 생긴 경우<br>→ <strong>진짜 병합</strong>이 필요함<br>충돌이 나도 겁내지 말자!</li>
</ul>
<hr>
<h2 id="⚡-충돌-해결하기">⚡ 충돌 해결하기</h2>
<ul>
<li><strong>가장 중요한 점:</strong> 겁내지 말아요!</li>
<li>같은 파일을 두 커밋에서 동시에 수정했을 때 충돌 확률이 높음  </li>
<li>에디터나 <strong>SourceTree</strong>를 사용해서 충돌 해결 가능</li>
</ul>
<hr>
<h2 id="⏪-커밋-되돌리기">⏪ 커밋 되돌리기</h2>
<h3 id="1-reset-사용">1. <code>reset</code> 사용</h3>
<ul>
<li><strong>장점:</strong> 쉬움  </li>
<li><strong>단점:</strong> 커밋이 날아감, 강제 푸시 필요</li>
</ul>
<h3 id="2-브랜치-만들어-되돌리기">2. 브랜치 만들어 되돌리기</h3>
<ul>
<li><strong>장점:</strong> 내용이 사라지지 않음  </li>
<li><strong>단점:</strong> 트리가 지저분해짐</li>
</ul>
<h3 id="3-revert">3. <code>revert</code></h3>
<ul>
<li><strong>가장 정석적인 방법</strong>  </li>
<li>커밋은 그대로 남고, 되돌리는 커밋이 새로 생김  </li>
<li><strong>주의:</strong> 선택한 커밋의 내용을 되돌림  </li>
<li>여러 커밋을 되돌릴 경우 → <strong>최신부터 순서대로 revert</strong></li>
</ul>
<hr>
<h2 id="✏️-커밋-덮어쓰기">✏️ 커밋 덮어쓰기</h2>
<ul>
<li><code>commit --amend</code> : 직전 커밋 수정  </li>
<li>이미 push 했다면 <code>push --force</code> 필요</li>
</ul>
<hr>
<h2 id="📥-stash-임시-저장">📥 stash (임시 저장)</h2>
<ul>
<li>다른 브랜치로 이동하기 전에 <strong>현재 작업 내용을 임시 저장</strong></li>
<li>매우 유용하므로 자주 활용하자<br>예)  <pre><code class="language-bash">git stash
git checkout other-branch
git stash pop</code></pre>
</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[Reflection 시스템 정리]]></title>
            <link>https://velog.io/@sungwoo_dev/UPROPERTY%EC%99%80-UFUNCTION-%EC%B0%A8%EC%9D%B4%EC%A0%90</link>
            <guid>https://velog.io/@sungwoo_dev/UPROPERTY%EC%99%80-UFUNCTION-%EC%B0%A8%EC%9D%B4%EC%A0%90</guid>
            <pubDate>Fri, 24 Oct 2025 13:31:01 GMT</pubDate>
            <description><![CDATA[<hr>
<h1 id="unreal-c-리플렉션reflection-시스템-완전-정리">[Unreal C++] 리플렉션(Reflection) 시스템 완전 정리</h1>
<blockquote>
<p>🎨 <strong>C++과 블루프린트를 잇는 언리얼 리플렉션 시스템 완전 정복</strong></p>
</blockquote>
<hr>
<h2 id="리플렉션reflection-시스템이란">리플렉션(Reflection) 시스템이란?</h2>
<p>언리얼 엔진의 <strong>리플렉션 시스템(Reflection System)</strong> 은
<em>C++ 코드와 언리얼 엔진의 다양한 시스템(에디터, 블루프린트, 직렬화, 네트워킹 등)</em> 간의
상호작용을 가능하게 해주는 핵심 메커니즘이다.</p>
<p>즉, <strong>런타임 또는 컴파일 타임에 객체의 속성과 메서드 정보를 동적으로 접근하고 조작할 수 있도록</strong> 도와준다.
이 시스템 덕분에 C++로 작성한 코드가 엔진의 다양한 기능과 유기적으로 연결된다.</p>
<p>리플렉션은 주로 <code>UPROPERTY()</code>와 <code>UFUNCTION()</code> 매크로를 통해 구현되며,
<strong>언리얼 헤더 툴(UHT, Unreal Header Tool)</strong> 이 이를 분석하여 필요한 리플렉션 코드를 생성한다.</p>
<hr>
<h2 id="uproperty">UPROPERTY()</h2>
<p><code>UPROPERTY()</code>는 클래스의 <strong>멤버 변수</strong>를 언리얼 엔진 내부 시스템에 노출시키는 매크로이다.
즉, 에디터나 블루프린트에서 해당 변수를 조작할 수 있도록 연결해주는 역할을 한다.</p>
<h3 id="주요-기능">주요 기능</h3>
<ul>
<li><strong>에디터 노출</strong>: 디자이너나 개발자가 에디터에서 직접 값을 수정할 수 있음</li>
<li><strong>블루프린트 통합</strong>: 블루프린트에서 접근하거나 수정 가능</li>
<li><strong>자동 직렬화</strong>: 게임 데이터를 저장/불러올 때 자동으로 직렬화되어 저장</li>
<li><strong>가비지 컬렉션(GC) 관리</strong>: <code>UPROPERTY</code>로 선언된 포인터는 자동으로 참조 카운트 관리</li>
<li><strong>메타데이터 지정</strong>: <code>EditAnywhere</code>, <code>BlueprintReadWrite</code> 등으로 편집 및 접근 권한 세부 제어 가능</li>
</ul>
<pre><code class="language-cpp">UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = &quot;Movement&quot;)
float MoveSpeed;</code></pre>
<p>이 예시는 <code>MoveSpeed</code> 변수를 에디터에서 수정 가능하게 하고,
블루프린트에서도 읽기/쓰기 모두 가능하도록 설정한 예시다.</p>
<h3 id="추가-세부-키워드-참고-dlemrcndtistorycom96">추가 세부 키워드 (참고: <a href="https://dlemrcnd.tistory.com/96">dlemrcnd.tistory.com/96</a>)</h3>
<h4 id="변수-공개--수정-권한">변수 공개 &amp; 수정 권한</h4>
<ul>
<li><code>VisibleDefaultsOnly</code> : 인스펙터 창에서 보기만 가능</li>
<li><code>VisibleInstanceOnly</code> : 월드 배치에서만 보기 가능</li>
<li><code>VisibleAnywhere</code> : 둘 다 보기 가능</li>
<li><code>EditDefaultsOnly</code> : 에디터에서만 수정 가능</li>
<li><code>EditInstanceOnly</code> : 월드 배치에서만 수정 가능</li>
<li><code>EditAnywhere</code> : 둘 다 수정 가능</li>
</ul>
<h4 id="블루프린트-접근-권한">블루프린트 접근 권한</h4>
<ul>
<li><code>BlueprintReadOnly</code> : 블루프린트에서 읽기만 가능</li>
<li><code>BlueprintReadWrite</code> : 블루프린트에서 읽기 &amp; 쓰기 가능</li>
<li><code>BlueprintGetter</code>, <code>BlueprintSetter</code> : 특정 함수로 접근/수정 지정 가능</li>
</ul>
<h4 id="기타-옵션">기타 옵션</h4>
<ul>
<li><code>Category</code> : 디테일 패널의 그룹 이름 지정</li>
<li><code>meta = (AllowPrivateAccess = &quot;true&quot;)</code> : private 변수라도 에디터에 노출 가능</li>
</ul>
<hr>
<h2 id="ufunction">UFUNCTION()</h2>
<p><code>UFUNCTION()</code>은 클래스의 <strong>멤버 함수</strong>를 언리얼 엔진이나 블루프린트에서 사용할 수 있도록 하는 매크로이다.
즉, 블루프린트에서 함수 호출 노드로 노출시키거나, 네트워크 동작에 활용할 수 있게 한다.</p>
<h3 id="주요-기능-1">주요 기능</h3>
<ul>
<li><strong>블루프린트 노출</strong>: 블루프린트 내에서 함수 호출 가능</li>
<li><strong>네트워크 동작 정의</strong>: <code>Server</code>, <code>Client</code>, <code>NetMulticast</code> 등으로 네트워크 함수 지정</li>
<li><strong>이벤트 바인딩</strong>: 특정 이벤트 발생 시 자동으로 호출되도록 설정 가능</li>
<li><strong>메타데이터 제어</strong>: 함수의 동작 방식 및 접근성을 세밀하게 조정 가능</li>
</ul>
<pre><code class="language-cpp">UFUNCTION(BlueprintCallable, Category = &quot;Movement&quot;)
void MoveForward(float Value);</code></pre>
<p>이 예시는 블루프린트에서 <code>MoveForward()</code> 함수를 노드로 호출할 수 있도록 만든 예시이다.</p>
<h3 id="추가-세부-키워드-참고-dlemrcndtistorycom96-1">추가 세부 키워드 (참고: <a href="https://dlemrcnd.tistory.com/96">dlemrcnd.tistory.com/96</a>)</h3>
<ul>
<li><code>BlueprintCallable</code> : C++ 함수를 블루프린트에서 사용 가능</li>
<li><code>BlueprintPure</code> : 반환값만 있고 상태 변경이 없는 함수에 적합</li>
<li><code>UPARAM</code> : 참조 인자를 블루프린트에 넘길 때 사용</li>
<li><code>BlueprintImplementableEvent</code> : C++에서 함수 원형만 선언하고, 블루프린트에서 구현</li>
<li><code>BlueprintNativeEvent</code> : C++에서 기본 구현 제공, 블루프린트에서 오버라이드 가능</li>
</ul>
<hr>
<h2 id="리플렉션-시스템의-작동-원리">리플렉션 시스템의 작동 원리</h2>
<ol>
<li><p>개발자가 C++ 코드에 <code>UCLASS</code>, <code>UPROPERTY</code>, <code>UFUNCTION</code> 등을 작성한다.</p>
</li>
<li><p>빌드 시 <strong>UHT(Unreal Header Tool)</strong> 이 코드를 스캔하고,
클래스와 속성, 함수 정보를 메타데이터 형태로 생성한다.</p>
</li>
<li><p>엔진은 이 정보를 활용해:</p>
<ul>
<li>에디터 노출</li>
<li>블루프린트 연결</li>
<li>직렬화 및 네트워킹
을 자동 처리한다.</li>
</ul>
</li>
</ol>
<pre><code class="language-plaintext">C++ 코드 작성
    ↓
UHT가 코드 분석 및 메타데이터 생성
    ↓
엔진이 런타임에 정보 활용 (에디터, 블루프린트, 네트워킹 등)</code></pre>
<hr>
<h2 id="핵심-요약">핵심 요약</h2>
<table>
<thead>
<tr>
<th>항목</th>
<th>역할</th>
</tr>
</thead>
<tbody><tr>
<td><code>UCLASS()</code></td>
<td>클래스를 리플렉션 시스템에 등록</td>
</tr>
<tr>
<td><code>UPROPERTY()</code></td>
<td>변수를 엔진 시스템에 노출</td>
</tr>
<tr>
<td><code>UFUNCTION()</code></td>
<td>함수를 엔진 및 블루프린트에 노출</td>
</tr>
<tr>
<td><code>USTRUCT()</code></td>
<td>구조체를 리플렉션 시스템에 등록</td>
</tr>
<tr>
<td><code>UENUM()</code></td>
<td>열거형(enum)을 블루프린트/에디터에서 사용 가능하게 함</td>
</tr>
</tbody></table>
<hr>
<h2 id="정리">정리</h2>
<p><code>UPROPERTY()</code>와 <code>UFUNCTION()</code>은 단순한 코드 장식이 아니라,
<strong>언리얼 리플렉션 시스템의 핵심적인 연결 매개체</strong>이다.
이를 통해 개발자는 C++로 작성한 코드를 엔진 내부 기능과 자연스럽게 통합시켜
<strong>더 유연하고 효율적인 워크플로우</strong>를 구축할 수 있다.</p>
<hr>
<h3 id="📘-한-줄-요약">📘 한 줄 요약</h3>
<blockquote>
<p>언리얼 리플렉션 시스템은 C++과 엔진 내부 시스템을 연결하는 &quot;정보 노출 메커니즘&quot;이며,
<code>UPROPERTY()</code>와 <code>UFUNCTION()</code>은 그 핵심 도구이다.</p>
</blockquote>
<hr>
]]></description>
        </item>
        <item>
            <title><![CDATA[Jetbrain Rider 플러그인]]></title>
            <link>https://velog.io/@sungwoo_dev/Jetbrain-Rider-%ED%94%8C%EB%9F%AC%EA%B7%B8%EC%9D%B8</link>
            <guid>https://velog.io/@sungwoo_dev/Jetbrain-Rider-%ED%94%8C%EB%9F%AC%EA%B7%B8%EC%9D%B8</guid>
            <pubDate>Sun, 19 Oct 2025 07:28:50 GMT</pubDate>
            <description><![CDATA[<p>Better Comments (1.0)</p>
<p>Active Tab Highlighter (1.5.1)</p>
<p>Better Highlights (2025.2.0)</p>
<p>Bracket Block (0.1.3)</p>
<p>CodeGlance Pro (2.0.0)</p>
<p>GitToolBox (600.1.12+243)</p>
<p>MultiHighlight (3.2.1)</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[DVC 프로젝트 관리 순서]]></title>
            <link>https://velog.io/@sungwoo_dev/DVC-%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8-%EA%B4%80%EB%A6%AC-%EC%88%9C%EC%84%9C</link>
            <guid>https://velog.io/@sungwoo_dev/DVC-%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8-%EA%B4%80%EB%A6%AC-%EC%88%9C%EC%84%9C</guid>
            <pubDate>Sun, 19 Oct 2025 07:27:05 GMT</pubDate>
            <description><![CDATA[<ol>
<li>dvc add ./Content/</li>
<li>dvc push</li>
<li>git add Cotent.dvc</li>
<li>git commit -m “커밋 메시지”</li>
<li>git push</li>
</ol>
]]></description>
        </item>
        <item>
            <title><![CDATA[애니메이션 템플릿]]></title>
            <link>https://velog.io/@sungwoo_dev/%EC%95%A0%EB%8B%88%EB%A9%94%EC%9D%B4%EC%85%98-%ED%85%9C%ED%94%8C%EB%A6%BF</link>
            <guid>https://velog.io/@sungwoo_dev/%EC%95%A0%EB%8B%88%EB%A9%94%EC%9D%B4%EC%85%98-%ED%85%9C%ED%94%8C%EB%A6%BF</guid>
            <pubDate>Sun, 07 Sep 2025 12:21:45 GMT</pubDate>
            <description><![CDATA[<hr>
<h1 id="ue5-애니메이션-블루프린트-템플릿--데이터-테이블-정리">UE5 애니메이션 블루프린트 템플릿 &amp; 데이터 테이블 정리</h1>
<p>이번 글에서는 다음 내용을 다룬다.</p>
<ol>
<li>애니메이션 블루프린트 템플릿  </li>
<li>Linked Anim Graph를 통한 모듈화  </li>
<li>직업별 애니메이션 데이터 관리 (데이터 테이블)  </li>
</ol>
<hr>
<h2 id="1-animinstance-클래스와-애님-블루프린트-연결">1) AnimInstance 클래스와 애님 블루프린트 연결</h2>
<img src="https://velog.velcdn.com/images/sungwoo_dev/post/86274307-06ee-43b5-a615-092b39dad0d7/image.png" width="600">
<img src="https://velog.velcdn.com/images/sungwoo_dev/post/bfeedf37-1758-48af-a9b9-8fab1ffa8a60/image.png" width="600">

<ul>
<li>SkeletalMesh 컴포넌트 → <strong>Details &gt; Anim Class</strong> 에 AnimBlueprint 지정 가능  </li>
<li>AnimBlueprint은 결국 <strong>AnimInstance를 상속</strong>한 클래스이다.  </li>
</ul>
<hr>
<h2 id="2-변수-핀-노출">2) 변수 핀 노출</h2>
<img src="https://velog.velcdn.com/images/sungwoo_dev/post/57c4bc59-1c7e-4634-89db-75ff9aecd3dc/image.png" width="600">

<ul>
<li>변수 생성  </li>
</ul>
<img src="https://velog.velcdn.com/images/sungwoo_dev/post/b67cb991-c69a-4373-9dca-dd8bb67de261/image.png" width="600">

<ul>
<li><strong>Expose as Pin</strong> 체크 → 노드에서 핀으로 노출됨  </li>
<li>다른 블루프린트에서 값을 주입해 애니메이션 계산에 활용 가능  </li>
</ul>
<hr>
<h2 id="3-linked-anim-graph-개념">3) Linked Anim Graph 개념</h2>
<ul>
<li>다른 애니메이션 블루프린트를 모듈 단위로 불러올 수 있는 기능  </li>
<li>장점:  <ul>
<li>애니메이션을 <strong>분리·재사용</strong> 가능  </li>
<li>직업별, 무기별, 상황별 애니메이션을 <strong>모듈화</strong> 가능  </li>
</ul>
</li>
</ul>
<blockquote>
<p>참고: Control Rig은 레벨 시퀀스에서 애니메이션 제작에 사용.<br>리타게팅은 다른 스켈레톤 애니메이션을 변환할 때 사용.  </p>
</blockquote>
<hr>
<h2 id="4-애니메이션-템플릿-만들기">4) 애니메이션 템플릿 만들기</h2>
<img src="https://velog.velcdn.com/images/sungwoo_dev/post/6a65a4a6-01d9-4cf3-8990-8c0332f9c621/image.png" width="600">

<ul>
<li>AnimInstance를 상속받은 C++ 클래스 생성  </li>
<li>CoreMinimal.h 대신 <strong>GameInfo.h</strong> 포함 (공용 헤더 관리 목적)  </li>
</ul>
<img src="https://velog.velcdn.com/images/sungwoo_dev/post/9a5565d0-a7dd-43a4-8ca6-b8ee5cf24efb/image.png" width="600">

<ul>
<li>모든 플레이어가 공통으로 쓸 수 있는 <strong>공용 AnimInstance</strong> 구조 설계  </li>
</ul>
<hr>
<h2 id="5-템플릿-animbp와-child-animbp">5) 템플릿 AnimBP와 Child AnimBP</h2>
<img src="https://velog.velcdn.com/images/sungwoo_dev/post/a440dc59-cdd8-48e9-9e77-99c7c085b9d8/image.png" width="600">

<ul>
<li>AnimBP를 만들 때 <strong>Template</strong> 선택 → 스켈레톤 지정이 불가능  </li>
<li>즉, “애니메이션 그래프의 틀”만 정의  </li>
</ul>
<img src="https://velog.velcdn.com/images/sungwoo_dev/post/706c4584-27c2-472a-9dd8-7cbe4570c00f/image.png" width="600">
<img src="https://velog.velcdn.com/images/sungwoo_dev/post/846a7b90-dce5-458e-9495-2cb83bb07f99/image.png" width="600">

<ul>
<li>스켈레톤 지정된 일반 AnimBP와 달리 <strong>추상적</strong>인 형태  </li>
</ul>
<img src="https://velog.velcdn.com/images/sungwoo_dev/post/f0d8a9c8-c7ad-4516-a227-fa86b0e56e40/image.png" width="600">

<ul>
<li>Child AnimBP로 상속 시 <strong>Anim Graph 없음</strong>  </li>
<li>대신 Linked Anim Graph 또는 Override 방식을 활용  </li>
</ul>
<hr>
<h2 id="6-구현부노티파이-복사">6) 구현부/노티파이 복사</h2>
<img src="https://velog.velcdn.com/images/sungwoo_dev/post/0b30f71c-c9bb-4e8b-83b8-6c05f460ff62/image.png" width="600">
<img src="https://velog.velcdn.com/images/sungwoo_dev/post/1e7ead3b-78d8-4551-82e6-664cdebbb341/image.png" width="600">
<img src="https://velog.velcdn.com/images/sungwoo_dev/post/27ed3dea-1534-4efd-8df1-488c80c0ac16/image.png" width="600">
<img src="https://velog.velcdn.com/images/sungwoo_dev/post/ab830e5d-1642-47ac-9079-275487cae6c0/image.png" width="600">

<ul>
<li>Template AnimBP에서 복사 → Child AnimBP에 구현부, 노티파이 붙여넣기  </li>
<li>Child는 자체 그래프가 없으므로 Override로 맞춰준다  </li>
</ul>
<hr>
<h2 id="7-blendspace-player-적용">7) BlendSpace Player 적용</h2>
<img src="https://velog.velcdn.com/images/sungwoo_dev/post/a6ca6c77-ea7b-4d07-8cde-ed50b92d34ca/image.png" width="600">
<img src="https://velog.velcdn.com/images/sungwoo_dev/post/a92fbf4f-3fdc-4b42-928b-f0f893c39382/image.png" width="600">
<img src="https://velog.velcdn.com/images/sungwoo_dev/post/c7218292-da87-44a4-9293-53b3119fbc27/image.png" width="600">

<ul>
<li>BlendSpace Player 노드에 원하는 BlendSpace 에셋 지정  </li>
<li>Child AnimBP에서는 <strong>애님 그래프를 직접 작성할 수 없으므로</strong>  <ul>
<li>오른쪽 패널의 <strong>Asset Override</strong>에서 블렌드 스페이스 교체 가능  </li>
</ul>
</li>
</ul>
<img src="https://velog.velcdn.com/images/sungwoo_dev/post/dcde8c27-8741-4031-84c6-49ba3d6cafd5/image.png" width="600">
<img src="https://velog.velcdn.com/images/sungwoo_dev/post/816f1077-a809-445b-b4d2-dbd035ceafea/image.png" width="600">
<img src="https://velog.velcdn.com/images/sungwoo_dev/post/6ecae85d-126a-4cef-b3a8-f15cfe6944b5/image.png" width="600">

<ul>
<li>Child에서는 Anim Graph 없음  </li>
<li>Asset Override로 교체해야 동작  </li>
</ul>
<hr>
<h2 id="8-state-machine과-변수-세팅">8) State Machine과 변수 세팅</h2>
<img src="https://velog.velcdn.com/images/sungwoo_dev/post/b6c596dc-783a-4e0b-9de4-e49a049d6af9/image.png" width="600">

<ul>
<li>State Machine 이름을 넣어야 노드 이름이 정상 출력  </li>
</ul>
<img src="https://velog.velcdn.com/images/sungwoo_dev/post/698400aa-bbf5-4578-9091-4541535e4bfb/image.png" width="600">

<ul>
<li>BlendSpace의 X, Y 값에 대응하는 변수 세팅  </li>
</ul>
<img src="https://velog.velcdn.com/images/sungwoo_dev/post/4c24bc8f-81e0-42cc-bc53-8b2013f23b89/image.png" width="600">

<hr>
<h2 id="9-애니메이션-데이터-관리">9) 애니메이션 데이터 관리</h2>
<p>데이터를 관리하는 방법은 다양하다:  </p>
<ol>
<li><strong>데이터 에셋</strong>: 개별 오브젝트 단위 관리  </li>
<li><strong>데이터 테이블</strong>: 구조체 기반, CSV/JSON과 연동 가능  </li>
<li><strong>스트링 테이블</strong>: 문자열만 따로 관리할 때  </li>
<li><strong>커브/커브 테이블</strong>: 시간 기반 값 관리 (속도, 데미지 곡선 등)  </li>
</ol>
<img src="https://velog.velcdn.com/images/sungwoo_dev/post/78f298ee-9260-4408-bc0e-2d96831ff372/image.png" width="600">

<ul>
<li>데이터 테이블을 만들기 위해서는 <strong>구조체(Struct)</strong> 가 필요  </li>
<li>MMORPG 기준: 직업별 애니메이션 데이터를 데이터 테이블로 관리  </li>
</ul>
<p>예시: <code>FJobAnimData</code> 구조체</p>
<pre><code class="language-cpp">USTRUCT(BlueprintType)
struct FJobAnimData : public FTableRowBase
{
    GENERATED_BODY()

    UPROPERTY(EditAnywhere, BlueprintReadWrite)
    TObjectPtr&lt;UBlendSpace&gt; MoveBlendSpace;

    UPROPERTY(EditAnywhere, BlueprintReadWrite)
    TObjectPtr&lt;UAnimMontage&gt; AttackMontage;

    UPROPERTY(EditAnywhere, BlueprintReadWrite)
    FString JobName;
};</code></pre>
<p>이렇게 하면 직업별 애니메이션 자산을 테이블로 쉽게 불러와서 캐릭터에 적용할 수 있다.</p>
<hr>
<h2 id="10-마무리">10) 마무리</h2>
<ul>
<li>AnimBP 템플릿은 스켈레톤 지정 없이 <strong>애니메이션 그래프의 형식</strong>만 정의</li>
<li>Child AnimBP는 <strong>Asset Override 방식</strong>으로 구체적인 애니메이션 지정</li>
<li>Linked Anim Graph를 통해 애니메이션을 모듈 단위로 관리</li>
<li>MMORPG 같은 구조에서는 <strong>데이터 테이블</strong>로 직업별 애니메이션 세트 관리</li>
</ul>
<p>이 구조를 잡아두면 새로운 캐릭터/직업을 추가할 때 <strong>Anim Graph를 복잡하게 다시 짤 필요 없이</strong>, 데이터만 교체해서 확장할 수 있다.</p>
<hr>
]]></description>
        </item>
        <item>
            <title><![CDATA[애니메이션 상하체 분리와 레이어 애니메이션]]></title>
            <link>https://velog.io/@sungwoo_dev/%EC%95%A0%EB%8B%88%EB%A9%94%EC%9D%B4%EC%85%98-%EC%83%81%ED%95%98%EC%B2%B4-%EB%B6%84%EB%A6%AC%EC%99%80-%EB%A0%88%EC%9D%B4%EC%96%B4-%EC%95%A0%EB%8B%88%EB%A9%94%EC%9D%B4%EC%85%98</link>
            <guid>https://velog.io/@sungwoo_dev/%EC%95%A0%EB%8B%88%EB%A9%94%EC%9D%B4%EC%85%98-%EC%83%81%ED%95%98%EC%B2%B4-%EB%B6%84%EB%A6%AC%EC%99%80-%EB%A0%88%EC%9D%B4%EC%96%B4-%EC%95%A0%EB%8B%88%EB%A9%94%EC%9D%B4%EC%85%98</guid>
            <pubDate>Sat, 06 Sep 2025 12:42:59 GMT</pubDate>
            <description><![CDATA[<p>핵심은 <em>기본 모션(Base Pose)</em> 위에 <em>섞어줄 모션(Blend Pose)</em> 을 덮어주는 방식이다.</p>
<h2 id="1-레이어-애니메이션-기본">1) 레이어 애니메이션 기본</h2>
<img src="https://velog.velcdn.com/images/sungwoo_dev/post/af753328-86ae-40b1-9d96-0ec21c3e1d1c/image.png" width="640">
<img src="https://velog.velcdn.com/images/sungwoo_dev/post/bb2385cc-104f-47d0-a1e8-44f33d9c3676/image.png" width="640">

<ul>
<li>Base Pose: 기본 동작 (예: 달리기)  </li>
<li>Blend Pose: 섞어줄 모션 (예: 공격)  </li>
<li>Idle(가만히 있을 때)는 굳이 사용할 필요 없음  </li>
</ul>
<hr>
<h2 id="2-캐시와-블렌드-모드">2) 캐시와 블렌드 모드</h2>
<img src="https://velog.velcdn.com/images/sungwoo_dev/post/ca4e7ec1-571f-4535-bed7-888fb53f228c/image.png" width="500">

<ul>
<li>Attack 캐시를 먼저 만든다 → 재사용 가능  </li>
</ul>
<img src="https://velog.velcdn.com/images/sungwoo_dev/post/0d09f991-6fd8-4fbb-9ec3-ee0b08b38ba9/image.png" width="500">

<ul>
<li>블렌드 모드 설정  </li>
</ul>
<img src="https://velog.velcdn.com/images/sungwoo_dev/post/30547fa5-74ae-4384-817b-d341bedcb471/image.png" width="640">

<ul>
<li>블렌드 모드 → 레이어 세팅에서 본(Bone) 이름 입력  </li>
<li>뎁스 블렌드: 하위 본까지 적용 범위  <ul>
<li>Depth = 0 → 전체 적용  </li>
<li>Depth = 1 → 한 단계 아래까지  </li>
</ul>
</li>
</ul>
<hr>
<h2 id="3-본-구조-이해">3) 본 구조 이해</h2>
<img src="https://velog.velcdn.com/images/sungwoo_dev/post/a034cb65-8e2d-4b19-a044-b1fcc1b4ca02/image.png" width="500">

<ul>
<li>본(Bone): 캐릭터의 뼈대  </li>
</ul>
<img src="https://velog.velcdn.com/images/sungwoo_dev/post/b4ddfd4d-3a95-455e-8060-0be2fdc8bec2/image.png" width="640">

<ul>
<li>pelvis를 기준으로 모든 본이 붙어있음  </li>
<li>spine을 기준으로 두 다리가 붙어 있음  </li>
</ul>
<p>즉, 특정 뼈를 중심으로 Blend Pose를 적용할 수 있다.  </p>
<hr>
<h2 id="4-적용-예시">4) 적용 예시</h2>
<img src="https://velog.velcdn.com/images/sungwoo_dev/post/ded7f4eb-18c4-49eb-b0a8-bf1644713ace/image.png" width="640">

<ul>
<li>spine_01을 기준으로 Blend 적용 시 → 달리면서 공격 가능  </li>
<li>pelvis로 하면 전신이 덮여서 공격 중 달리기가 불가능  </li>
</ul>
<img src="https://velog.velcdn.com/images/sungwoo_dev/post/ff572c04-9c66-4d35-9553-e5b7db05b2b4/image.png" width="640">

<hr>
<h2 id="5-조건-분기-movespeed-활용">5) 조건 분기 (MoveSpeed 활용)</h2>
<img src="https://velog.velcdn.com/images/sungwoo_dev/post/550ce9d4-1329-47c0-a62a-93471482d575/image.png" width="640">

<ul>
<li><code>MoveSpeed &gt; 0</code> → True Pose: Base Pose(달리기) + Attack 섞기  </li>
<li><code>MoveSpeed == 0</code> → False Pose: Attack만 출력  </li>
</ul>
<hr>
<h2 id="6-메시-스페이스-블렌딩--루트-모션">6) 메시 스페이스 블렌딩 &amp; 루트 모션</h2>
<img src="https://velog.velcdn.com/images/sungwoo_dev/post/dbd26124-a271-476d-9802-2e79a336e0fc/image.png" width="640">

<ul>
<li>메시 스페이스 회전 블렌드 → 메시 기준으로 회전값 섞음  </li>
<li>메시 스페이스 스케일 블렌드 → 메시 기준으로 스케일 섞음  </li>
<li>체크 시 공격 시 허리가 더 자연스럽게 비틀림  </li>
</ul>
<h3 id="루트-모션">루트 모션</h3>
<ul>
<li>캐릭터가 <strong>제자리에서만</strong> 움직이는 게 아니라, 애니메이션에 포함된 실제 이동을 반영  </li>
<li>켜고 끌 수 있음  </li>
</ul>
<img src="https://velog.velcdn.com/images/sungwoo_dev/post/58767b00-dcf1-4cb4-9752-585498ee915f/image.png" width="640">

<ul>
<li>루트 모션 활성화 → 애니메이션에서 Root Motion 추출  </li>
<li>캐릭터 클래스의 Movement Component → 루트 모션 옵션 지원  </li>
</ul>
<p>루트 본 기반 블렌드 루트 모션 (가중치: Weight) 사용 가능  </p>
<hr>
<h2 id="7-애니메이션-레이어">7) 애니메이션 레이어</h2>
<img src="https://velog.velcdn.com/images/sungwoo_dev/post/afd76d9d-2582-4752-b0bd-630f7a512900/image.png" width="640">
<img src="https://velog.velcdn.com/images/sungwoo_dev/post/afad4a98-274a-4cc9-969b-725a53b77033/image.png" width="640">

<ul>
<li>레이어 = 함수처럼 생각하면 됨  </li>
<li>인터페이스 = 다중 상속을 위한 구조  </li>
</ul>
<img src="https://velog.velcdn.com/images/sungwoo_dev/post/2aa83e09-9d81-43b7-84ed-6aeaf7289952/image.png" width="640">

<ul>
<li>클래스 세팅에서 <strong>애니메이션 레이어 인터페이스</strong> 등록  </li>
</ul>
<img src="https://velog.velcdn.com/images/sungwoo_dev/post/80106ecd-7f07-49f5-8d7e-0a78c2e616eb/image.png" width="640">

<ul>
<li>등록 완료 확인  </li>
</ul>
<hr>
<h2 id="8-레이어-구현">8) 레이어 구현</h2>
<img src="https://velog.velcdn.com/images/sungwoo_dev/post/33049ea9-4a26-410a-967a-168e44561b47/image.png" width="640">
<img src="https://velog.velcdn.com/images/sungwoo_dev/post/1c0658b0-24c3-4600-a5d4-1423d68b61c4/image.png" width="640">
<img src="https://velog.velcdn.com/images/sungwoo_dev/post/074f9bc0-2ea5-4418-8bdd-cced783f6312/image.png" width="640">

<ul>
<li>인터페이스를 더블클릭 → 함수처럼 구현 가능  </li>
<li>Attack, Locomotion, Aim 등 모듈화하여 깔끔하게 관리 가능  </li>
</ul>
<hr>
<h2 id="9-마무리">9) 마무리</h2>
<ul>
<li>레이어 애니메이션은 <strong>달리면서 공격</strong> 같은 복합 모션에서 많이 사용한다.  </li>
<li>Spine 기준으로 Blend를 걸면 상체/하체를 분리해서 동작 가능.  </li>
<li>MoveSpeed 조건, 메시 스페이스 블렌딩, 루트 모션까지 활용하면 자연스러운 전투 애니메이션 구현 가능.  </li>
<li>애니메이션 레이어 인터페이스를 활용하면 재사용성과 구조화가 크게 향상된다.  </li>
</ul>
<hr>
]]></description>
        </item>
        <item>
            <title><![CDATA[카메라회전과 에임 오프셋]]></title>
            <link>https://velog.io/@sungwoo_dev/%EC%B9%B4%EB%A9%94%EB%9D%BC%ED%9A%8C%EC%A0%84%EA%B3%BC-%EC%97%90%EC%9E%84-%EC%98%A4%ED%94%84%EC%85%8B</link>
            <guid>https://velog.io/@sungwoo_dev/%EC%B9%B4%EB%A9%94%EB%9D%BC%ED%9A%8C%EC%A0%84%EA%B3%BC-%EC%97%90%EC%9E%84-%EC%98%A4%ED%94%84%EC%85%8B</guid>
            <pubDate>Thu, 04 Sep 2025 13:30:44 GMT</pubDate>
            <description><![CDATA[<hr>
<h1 id="ue5-aim-offset으로-캐릭터-고개-회전-구현">UE5 Aim Offset으로 캐릭터 고개 회전 구현</h1>
<h2 id="1-카메라-회전-입력-액션-생성">1) 카메라 회전 입력 액션 생성</h2>
<img src="https://velog.velcdn.com/images/sungwoo_dev/post/3d36da9e-2be0-4389-bd74-b39cdd5da66d/image.png" width="640">

<ul>
<li><code>IACameraRotation</code> 입력 액션 생성  </li>
</ul>
<img src="https://velog.velcdn.com/images/sungwoo_dev/post/3e6bd326-9687-4fbf-b6c7-9dd0cb720d3b/image.png" width="500">

<ul>
<li>Value Type: <code>Axis2D(Vector2D)</code>  </li>
<li>Trigger: <code>Cumulative</code> (누적값)  </li>
</ul>
<hr>
<h2 id="2-마우스-축-값-매핑">2) 마우스 축 값 매핑</h2>
<img src="https://velog.velcdn.com/images/sungwoo_dev/post/e1494562-da69-40a8-abe0-6c65bf3438d7/image.png" width="600">

<ul>
<li><code>IMCGame</code>에 <code>MouseX</code>, <code>MouseY</code> 입력축 추가  </li>
<li>Swizzle(YXZ)로 축 값 변환  <ul>
<li>X축: 마우스 좌우 (오른쪽 +1, 왼쪽 -1)  </li>
<li>Y축: 마우스 상하 (위 +1, 아래 -1)  </li>
</ul>
</li>
</ul>
<hr>
<h2 id="3-입력-클래스-세팅">3) 입력 클래스 세팅</h2>
<img src="https://velog.velcdn.com/images/sungwoo_dev/post/4dbd89a4-0bb2-40cc-8a23-8a051ab2ec41/image.png" width="600">

<ul>
<li><code>Input.h</code> → <code>UGameInput</code>에 변수 추가<pre><code class="language-cpp">FVector2D mCameraRotation;

</code></pre>
</li>
</ul>
<img src="https://velog.velcdn.com/images/sungwoo_dev/post/bc70252b-b4fb-47e5-98eb-2c67e0b2f5b7/image.png" width="600">

<ul>
<li><code>Input.cpp</code>에 <code>CameraRotationAction</code> 등록</li>
</ul>
<hr>
<h2 id="4-플레이어-캐릭터에-바인딩">4) 플레이어 캐릭터에 바인딩</h2>
<img src="https://velog.velcdn.com/images/sungwoo_dev/post/d9fa1163-1195-4e5f-97e5-e670283cdb31/image.png" width="600">

<ul>
<li>캐릭터에서 키 바인딩 추가</li>
</ul>
<img src="https://velog.velcdn.com/images/sungwoo_dev/post/5b5d60bf-9c99-43b5-a648-b80fd9e8d822/image.png" width="600">

<ul>
<li><code>CameraRotationKey</code> 함수 등록</li>
</ul>
<hr>
<h2 id="5-스프링암-회전-제어">5) 스프링암 회전 제어</h2>
<img src="https://velog.velcdn.com/images/sungwoo_dev/post/ca67def0-9c9e-478d-9322-10eb17ae3b23/image.png" width="600">

<ul>
<li>마우스 입력값으로 <strong>스프링암 회전</strong> 제어</li>
<li>위/아래 각도는 제한을 걸어야 뒤집히지 않는다</li>
</ul>
<img src="https://velog.velcdn.com/images/sungwoo_dev/post/eeba7244-dcfc-47b1-9115-3d09714529fb/image.png" width="600">

<pre><code class="language-cpp">void APlayerCharacter::CameraRotationKey(const FInputActionValue&amp; Value)
{
    FVector2D Axis = Value.Get&lt;FVector2D&gt;();

    FRotator NewRot = SpringArm-&gt;GetRelativeRotation();
    NewRot.Yaw   += Axis.X;
    NewRot.Pitch += Axis.Y;
    NewRot.Pitch = FMath::Clamp(NewRot.Pitch, -80.f, 80.f); // 각도 제한

    SpringArm-&gt;SetRelativeRotation(NewRot);
}</code></pre>
<hr>
<h2 id="6-animinstance-변수-추가">6) AnimInstance 변수 추가</h2>
<img src="https://velog.velcdn.com/images/sungwoo_dev/post/14c942a0-b4fd-45d2-a902-799a94a98f34/image.png" width="600">

<p><code>PlayerDefaultAnimInstance.h</code></p>
<pre><code class="language-cpp">float mAimLookUp;
float mAimSide;

void SetAimInfo(float LookUp, float Side);</code></pre>
<img src="https://velog.velcdn.com/images/sungwoo_dev/post/17b6e6e0-8e17-4036-9f2a-ca70b65f5f25/image.png" width="600">

<ul>
<li>SetAimInfo에서 변수 세팅</li>
</ul>
<img src="https://velog.velcdn.com/images/sungwoo_dev/post/23207c6f-b2d9-4702-b383-651b9062f210/image.png" width="600">

<ul>
<li>LookUp 범위를 <code>-90~270</code>에서 <code>-180~180</code>으로 보정</li>
</ul>
<hr>
<h2 id="7-마우스-회전-적용-확인">7) 마우스 회전 적용 확인</h2>
<img src="https://velog.velcdn.com/images/sungwoo_dev/post/c9edb59d-0240-4c24-aa9f-4649a5b1847d/image.png" width="800">

<p>컴파일 후 플레이 → 마우스 회전이 정상 적용된다.</p>
<hr>
<h2 id="8-aim-offset-생성">8) Aim Offset 생성</h2>
<img src="https://velog.velcdn.com/images/sungwoo_dev/post/a94288d0-6257-42c5-9c3e-b28f16c633ba/image.png" width="640">

<ul>
<li>Aim Offset 생성</li>
<li>BlendSpace와 유사하지만, <strong>고개 방향 전용 블렌딩 클래스</strong></li>
</ul>
<img src="https://velog.velcdn.com/images/sungwoo_dev/post/8401f87e-d0ba-4130-b824-c08390911fd3/image.png" width="640">

<ul>
<li>BlendSpace처럼 축과 샘플 애니메이션 배치</li>
</ul>
<img src="https://velog.velcdn.com/images/sungwoo_dev/post/46e0ef8f-3421-47e6-bd21-7831c8a58ed1/image.png" width="600">

<ul>
<li>캐릭터가 안 보이면 Preview Base Pose를 바꿔준다</li>
</ul>
<hr>
<h2 id="9-애님-그래프-연결">9) 애님 그래프 연결</h2>
<img src="https://velog.velcdn.com/images/sungwoo_dev/post/083f10d1-d3e2-404a-bb6a-4e812ffd276d/image.png" width="600">

<ul>
<li>Jump 스테이트(최종 포즈) → Aim Offset 노드 연결</li>
<li>OutputPose로 연결 마무리</li>
</ul>
<hr>
<h2 id="10-마무리">10) 마무리</h2>
<ul>
<li><p><strong>입력 → 스프링암 회전 → AnimInstance 전달 → AimOffset 블렌딩</strong> 흐름</p>
</li>
<li><p>결과: 마우스 방향에 맞춰 캐릭터 고개가 자연스럽게 회전한다</p>
</li>
<li><p>확장:</p>
<ul>
<li>상체 전용 블렌딩(Layered Blend per Bone) 적용 → 이동 중에도 고개 회전 유지</li>
<li>ADS(조준) 시스템 구현 시 활용 가능</li>
</ul>
</li>
</ul>
<hr>
]]></description>
        </item>
        <item>
            <title><![CDATA[C++ 개념 정리]]></title>
            <link>https://velog.io/@sungwoo_dev/C-%EA%B3%B5%EB%B6%80</link>
            <guid>https://velog.io/@sungwoo_dev/C-%EA%B3%B5%EB%B6%80</guid>
            <pubDate>Thu, 04 Sep 2025 05:14:33 GMT</pubDate>
            <description><![CDATA[<h1 id="공부-방법">공부 방법</h1>
<ol start="0">
<li>올바른 타이핑 방법을 연습한다.  </li>
<li>간단한 개념을 이해한다.  </li>
<li>예제를 따라 해본다.  </li>
<li>문제가 생기면 해결한다. (문제 해결하는 과정이 핵심이다)  </li>
<li>예제를 내 맘대로 바꿔본다.  </li>
</ol>
<p>프로그래밍을 배우는 이유는 <strong>무언가를 만들고 싶어서</strong>이다.  </p>
<hr>
<h1 id="프로그래밍-언어란">프로그래밍 언어란?</h1>
<p>프로그램이란 컴퓨터가 원하는 기능을 수행해 주는 것. 이를 위해서는 <strong>계산적 사고(컴퓨팅 사고)</strong> 가 필요하다.  </p>
<h2 id="기계어-machine-language">기계어 (Machine Language)</h2>
<ul>
<li>0, 1 이진수를 사용한다.  </li>
<li>직접 사용하는 경우는 거의 없다.  </li>
</ul>
<h2 id="어셈블리-언어">어셈블리 언어</h2>
<ul>
<li>기계어를 직접 쓰기 힘들어서 등장.  </li>
</ul>
<h2 id="고수준-언어-high-level-languages">고수준 언어 (High-Level Languages)</h2>
<ul>
<li>C, C++, Pascal, Java, Javascript, Perl, Python 등  </li>
<li>사람이 이해하기 쉬운 문법을 사용한다.  </li>
</ul>
<hr>
<h1 id="컴파일러와-인터프리터">컴파일러와 인터프리터</h1>
<h2 id="컴파일러-compiler">컴파일러 (Compiler)</h2>
<p><img src="https://velog.velcdn.com/images/sungwoo_dev/post/d41f46cf-cff8-4137-b93a-43c3b4acf0b8/image.png" alt="컴파일러">  </p>
<ul>
<li>전체 코드를 번역해 <strong>실행 파일(exe)</strong> 생성  </li>
<li>실행 파일은 하드웨어에 보관되어 실행됨  </li>
</ul>
<h2 id="인터프리터-interpreter">인터프리터 (Interpreter)</h2>
<p><img src="https://velog.velcdn.com/images/sungwoo_dev/post/cbede077-9475-431e-aa10-6ccbd7287055/image.png" alt="인터프리터">  </p>
<ul>
<li>실행 파일을 생성하지 않음  </li>
<li>한 줄씩 읽고 바로 실행 → 실행 속도 느림  </li>
<li>대표적 언어: Python  </li>
</ul>
<hr>
<h1 id="이식성-portability">이식성 (Portability)</h1>
<p><img src="https://velog.velcdn.com/images/sungwoo_dev/post/17cc9a2b-87b4-4436-baf2-669c00adab73/image.png" alt="이식성">  </p>
<ul>
<li>OS마다 실행 파일 호환이 안 될 수 있다. (예: Windows ↔ Linux)  </li>
<li>장기적으로는 가능해지고 있음.  </li>
</ul>
<hr>
<h1 id="cc-언어-소개">C/C++ 언어 소개</h1>
<p><img src="https://velog.velcdn.com/images/sungwoo_dev/post/803f8b91-97ba-407e-88ad-f275d90f356a/image.png" alt="C/C++">  </p>
<ul>
<li>C++은 <strong>객체 지향 언어</strong>  </li>
<li>중요한 것은 언어 기능을 다 아는 것이 아니라 <strong>쓸모있는 기능을 제대로 쓰는 것</strong>  </li>
</ul>
<hr>
<h1 id="프로그래밍-과정">프로그래밍 과정</h1>
<ol>
<li><strong>문제 정의</strong> – 무엇을 만들고 싶은지, 세부 기능을 잘게 쪼갠다.  </li>
<li><strong>해법 설계</strong> – 어떻게 만들지 구상한다.  </li>
<li><strong>구현</strong> – 생각한 대로 프로그램을 작성한다.  </li>
<li><strong>컴파일</strong> – 코드를 기계어로 번역한다.<br><img src="https://velog.velcdn.com/images/sungwoo_dev/post/23ca2333-74b4-45b8-80c3-669b541f4221/image.png" alt="컴파일 과정">  </li>
<li><strong>링킹</strong> – 여러 오브젝트 파일을 하나로 합친다.<br><img src="https://velog.velcdn.com/images/sungwoo_dev/post/a8d56c7e-19fa-4db2-b738-86edf962bdb7/image.png" alt="링킹 과정"><br>→ 요즘은 직접 작성하는 코드보다 <strong>오픈소스를 잘 가져다 쓰는 능력</strong>이 중요하다.  </li>
<li><strong>테스트 &amp; 디버깅</strong> – 실행해보고 문제를 고친다.  </li>
</ol>
<hr>
<h1 id="컴퓨터-메모리-기본-구조">컴퓨터 메모리 기본 구조</h1>
<p><img src="https://velog.velcdn.com/images/sungwoo_dev/post/674506af-d143-451a-bf6d-7866d7b519fd/image.png" alt="메모리 구조">  </p>
<ul>
<li>메모리에 데이터를 저장 → CPU가 연산 → 다시 메모리에 저장  </li>
<li>자주 쓰는 데이터는 캐시(Cache)에 저장해 속도를 높인다.  </li>
</ul>
<h2 id="메모리-접근-방식">메모리 접근 방식</h2>
<p><img src="https://velog.velcdn.com/images/sungwoo_dev/post/fbf29baa-324b-4a08-b94b-83bd274818a9/image.png" alt="RAM">  </p>
<ul>
<li><strong>RAM (Random Access Memory)</strong> → 주소만 알면 바로 접근 가능  </li>
<li><strong>순차 접근</strong>(테이프 방식)은 현대 컴퓨터에서 사용하지 않음  </li>
</ul>
<h2 id="메모리-단위">메모리 단위</h2>
<p><img src="https://velog.velcdn.com/images/sungwoo_dev/post/5b163aa4-5179-49b0-b3ea-ecb7bbd13839/image.png" alt="비트">  </p>
<ul>
<li><strong>비트(bit)</strong>: 0 또는 1 (Binary Digit)  </li>
<li>여러 비트가 모여 바이트, 워드 등을 구성  </li>
</ul>
<h2 id="cpu와-메모리-통신">CPU와 메모리 통신</h2>
<p><img src="https://velog.velcdn.com/images/sungwoo_dev/post/f8f958db-d03e-481d-83f7-715e9316db38/image.png" alt="CPU와 메모리">  </p>
<ul>
<li><strong>주소 버스(address bus)</strong>: 데이터가 어디 있는지  </li>
<li><strong>데이터 버스(data bus)</strong>: 실제 데이터  </li>
<li><strong>제어 버스(control bus)</strong>: 읽기/쓰기 신호  </li>
<li>이 구조 때문에 <strong>포인터(pointer)</strong> 가 존재한다.  </li>
</ul>
<hr>
<h1 id="통합개발환경ide---visual-studio">통합개발환경(IDE) - Visual Studio</h1>
<h2 id="프로젝트-관리">프로젝트 관리</h2>
<p><img src="https://velog.velcdn.com/images/sungwoo_dev/post/f6bdf7f5-0cd1-4986-8921-ff60eec129e3/image.png" alt="프로젝트 관리">  </p>
<ul>
<li>Location(저장 위치)을 잘 관리해야 한다.  </li>
</ul>
<h2 id="프로젝트-생성">프로젝트 생성</h2>
<p><img src="https://velog.velcdn.com/images/sungwoo_dev/post/08cdda33-626b-464c-bb80-a926aab7d92d/image.png" alt="프로젝트 생성">  </p>
<ul>
<li>Windows, Console 크게 차이 없음 (Console은 텍스트 출력용)  </li>
</ul>
<p><img src="https://velog.velcdn.com/images/sungwoo_dev/post/6c700c47-97e1-453c-b4a1-4d86e1bb0117/image.png" alt="Precompiled Header">  </p>
<ul>
<li><strong>Precompiled Header</strong> → 멀티플랫폼 코드에서는 사용 X (리눅스에서 컴파일 불가)  </li>
</ul>
<h2 id="컴파일--링킹-과정">컴파일 &amp; 링킹 과정</h2>
<p><img src="https://velog.velcdn.com/images/sungwoo_dev/post/047f4220-47ac-4666-9bc4-d8984c3ff134/image.png" alt="컴파일"><br><img src="https://velog.velcdn.com/images/sungwoo_dev/post/dbabe7a2-ef9a-44d6-b95b-28d035a81e57/image.png" alt="링킹">  </p>
<h2 id="프로젝트-폴더-구조">프로젝트 폴더 구조</h2>
<p><img src="https://velog.velcdn.com/images/sungwoo_dev/post/a9b205f8-ee12-4a31-b673-8086663edf0b/image.png" alt="폴더 구조">  </p>
<ul>
<li>상위 폴더 열면…<br><img src="https://velog.velcdn.com/images/sungwoo_dev/post/21730fec-80ad-48de-abde-0d8bab435d51/image.png" alt="상위 폴더">  </li>
<li><code>Debug → obj</code> 폴더 확인 가능  </li>
</ul>
<h2 id="명령어-실행">명령어 실행</h2>
<p><img src="https://velog.velcdn.com/images/sungwoo_dev/post/9f1f6d7c-5712-4ce1-8a9e-6b7d08623581/image.png" alt="명령 실행">  </p>
<ul>
<li>원래는 이런 <strong>커맨드(Command)</strong> 로 실행했음  </li>
</ul>
<hr>
<h1 id="디버그-빌드-vs-릴리즈-빌드">디버그 빌드 vs 릴리즈 빌드</h1>
<p><img src="https://velog.velcdn.com/images/sungwoo_dev/post/c09b75b1-8922-4bbd-9ed5-f729dac531c1/image.png" alt="빌드 차이">  </p>
<ul>
<li><strong>디버그 빌드</strong>: 디버깅 정보 포함, 최적화 OFF → 실행 크기 큼, 속도 느림  </li>
<li><strong>릴리즈 빌드</strong>: 불필요한 정보 제거, 최적화 ON → 용량 작음, 실행 속도 빠름  </li>
</ul>
<hr>
<h1 id="통합-개발환경의-기본적인-사용법---윈도우즈리눅스-codeblocks">통합 개발환경의 기본적인 사용법 - 윈도우즈/리눅스 Code::Blocks</h1>
<h2 id="codeblocks-다운로드">Code::Blocks 다운로드</h2>
<p><img src="https://velog.velcdn.com/images/sungwoo_dev/post/0b96aefc-2f57-4d5c-b937-048847535577/image.png" alt="다운로드">  </p>
<ul>
<li>Code::Blocks는 <strong>GCC 컴파일러</strong>를 사용하므로 별도 다운로드 필요  </li>
</ul>
<p><img src="https://velog.velcdn.com/images/sungwoo_dev/post/87139902-f1fc-41a4-9c25-deedc994963b/image.png" alt="디버거 설치">  </p>
<ul>
<li>디버거(Debugger)도 함께 설치해야 함  </li>
</ul>
<hr>
<h2 id="codeblocks-첫-실행">Code::Blocks 첫 실행</h2>
<p><img src="https://velog.velcdn.com/images/sungwoo_dev/post/7f8c586c-239c-4f78-b8de-e41d1b147d17/image.png" alt="첫 화면">  </p>
<ul>
<li>처음 실행 시 어떤 컴파일러를 쓸지 설정해야 함  </li>
<li><strong>프로젝트 단위로 시작</strong>하는 것이 편리  </li>
</ul>
<p><img src="https://velog.velcdn.com/images/sungwoo_dev/post/346b5876-17f5-463f-a7bd-ae9b9bebde3f/image.png" alt="프로젝트 생성"><br><img src="https://velog.velcdn.com/images/sungwoo_dev/post/7279b820-4990-4359-8dc0-3ff5630f9a17/image.png" alt="경로 설정">  </p>
<ul>
<li>프로젝트 이름과 경로 지정  </li>
</ul>
<p><img src="https://velog.velcdn.com/images/sungwoo_dev/post/23b6f712-6bcc-4735-b774-48b204dcdedd/image.png" alt="디버그 설정">  </p>
<ul>
<li>빌드 모드에서 <strong>Debug/Release 설정</strong> 가능  </li>
</ul>
<hr>
<h2 id="codeblocks-디버깅">Code::Blocks 디버깅</h2>
<p><img src="https://velog.velcdn.com/images/sungwoo_dev/post/0bad8e26-d0a8-4d98-9968-0cd3892bb421/image.png" alt="디버깅1"><br><img src="https://velog.velcdn.com/images/sungwoo_dev/post/2bf86720-c2aa-443c-be1b-ebf528ef6a6f/image.png" alt="디버깅2">  </p>
<ul>
<li>코드 실행 중 문제를 추적하고 고치는 과정 → <strong>디버깅(Debugging)</strong>  </li>
</ul>
<hr>
<h1 id="비주얼-스튜디오---솔루션-프로젝트-관리">비주얼 스튜디오 - 솔루션, 프로젝트 관리</h1>
<h2 id="솔루션과-프로젝트-구조">솔루션과 프로젝트 구조</h2>
<ul>
<li>솔루션  <ul>
<li>프로젝트1  </li>
<li>프로젝트2  </li>
<li>... ...  </li>
</ul>
</li>
<li>다양한 언어 혼합 가능  </li>
<li>계층 구조: <strong>솔루션(.sln) &gt; 프로젝트 &gt; 소스파일(.cpp, .h 등)</strong>  </li>
</ul>
<h2 id="빌드와-실행">빌드와 실행</h2>
<ul>
<li>솔루션 밑에 여러 프로젝트가 있을 경우, 빌드 후 생성된 exe 및 필요한 파일들이 솔루션 디렉토리 밑으로 이동<br><img src="https://velog.velcdn.com/images/sungwoo_dev/post/da01b185-e162-429f-adb6-fe8360ea8771/image.png" alt="빌드 파일">  </li>
<li>해당 위치는 속성에서 변경 가능  </li>
</ul>
<h2 id="실행-프로젝트-선택">실행 프로젝트 선택</h2>
<p><img src="https://velog.velcdn.com/images/sungwoo_dev/post/ee8b1214-c1b0-4e69-91f4-3d7ac801c60c/image.png" alt="실행 프로젝트">  </p>
<ul>
<li>프로젝트가 여러 개일 경우, <strong>볼드체</strong>로 표시된 프로젝트가 실행 대상  </li>
</ul>
<h2 id="하위-시스템-설정">하위 시스템 설정</h2>
<p><img src="https://velog.velcdn.com/images/sungwoo_dev/post/f0e5c3cb-26e3-4d70-a151-5e0a9b14a61c/image.png" alt="하위 시스템">  </p>
<ul>
<li>프로젝트 우클릭 → 속성 → 링커 → 시스템 → <strong>하위 시스템(콘솔)</strong> 설정  </li>
</ul>
<h2 id="관리-팁">관리 팁</h2>
<ul>
<li>솔루션 파일은 직접 수정하지 말 것  </li>
<li>IDE에서 프로젝트 제거 → Source 폴더 삭제 → 리빌드  </li>
</ul>
<hr>
<h1 id="비주얼-스튜디오-2022-기본적인-사용법">비주얼 스튜디오 2022 기본적인 사용법</h1>
<p><img src="https://velog.velcdn.com/images/sungwoo_dev/post/9f0b68fe-f159-48c2-8428-bb20cfbce73a/image.png" alt="VS2022"><br><img src="https://velog.velcdn.com/images/sungwoo_dev/post/0f64401a-8fbc-4d6d-be2d-ff566db0302e/image.png" alt="옵션">  </p>
<ul>
<li>메뉴: <strong>툴 → 옵션</strong>  </li>
<li><strong>F7</strong>: 컴파일 오류 확인용 &quot;빌드만&quot; 실행 (프로그램 실행은 안 됨)  </li>
</ul>
<hr>
]]></description>
        </item>
        <item>
            <title><![CDATA[블랜드 스페이스]]></title>
            <link>https://velog.io/@sungwoo_dev/%EB%B8%94%EB%9E%9C%EB%93%9C-%EC%8A%A4%ED%8E%98%EC%9D%B4%EC%8A%A4</link>
            <guid>https://velog.io/@sungwoo_dev/%EB%B8%94%EB%9E%9C%EB%93%9C-%EC%8A%A4%ED%8E%98%EC%9D%B4%EC%8A%A4</guid>
            <pubDate>Tue, 02 Sep 2025 05:40:14 GMT</pubDate>
            <description><![CDATA[<hr>
<h1 id="ue5-warrior-콤보-조건부-처리--블렌드-스페이스-이동-구현">UE5 Warrior 콤보 조건부 처리 &amp; 블렌드 스페이스 이동 구현</h1>
<ol>
<li>콤보가 되었을 때만 노티파이를 호출하는 공격 시스템</li>
<li>블렌드 스페이스를 이용해 자연스러운 이동 애니메이션 구현</li>
</ol>
<hr>
<h2 id="1-콤보-조건부-노티파이">1) 콤보 조건부 노티파이</h2>
<img src="https://velog.velcdn.com/images/sungwoo_dev/post/93b77c2d-2e81-4897-acb4-0d83e8ce0434/image.png" width="640">

<ul>
<li><code>PlayerDefaultAnimInstance.h</code>에 변수 추가<pre><code class="language-cpp">bool mAttackEnable = false; // 공격 중인지
bool mAttackCombo  = false; // 콤보 가능 여부
</code></pre>
</li>
</ul>
<hr>
<h3 id="1-1-기본-동작">1-1) 기본 동작</h3>
<img src="https://velog.velcdn.com/images/sungwoo_dev/post/33d38130-05db-4441-a628-2e44e09a1530/image.png" width="640">

<ul>
<li>이 상태에서는 <strong>공격 1회만 가능</strong>하다.</li>
</ul>
<img src="https://velog.velcdn.com/images/sungwoo_dev/post/7ec48c73-c099-4b51-9af2-8a7847b0e6bd/image.png" width="500">

<ul>
<li><code>PlayerDefaultAnimInstance.cpp</code>에도 초기값을 false로 세팅한다.</li>
</ul>
<hr>
<h3 id="1-2-콤보-활성화-로직">1-2) 콤보 활성화 로직</h3>
<img src="https://velog.velcdn.com/images/sungwoo_dev/post/1b905785-cd77-467f-9333-cf73d855816a/image.png" width="500">

<p>콤보를 허용하면</p>
<img src="https://velog.velcdn.com/images/sungwoo_dev/post/d6e86ffd-f900-4eec-9f84-31a035d072fb/image.png" width="640">

<ul>
<li><p><code>else if</code>로 <strong>콤보 공격</strong> 처리:</p>
<ul>
<li><code>mAttackSectionIndex</code> 증가</li>
<li>배열 개수에 맞게 모듈러 연산</li>
<li>다음 섹션으로 Jump</li>
<li><code>mAttackCombo = false;</code></li>
</ul>
</li>
</ul>
<hr>
<h3 id="1-3-몽타주-종료-시-처리">1-3) 몽타주 종료 시 처리</h3>
<img src="https://velog.velcdn.com/images/sungwoo_dev/post/849dfa7a-26f1-4835-8861-7434cdd82cd6/image.png" width="500">

<p>몽타주 종료 시 <code>mAttackCombo = false;</code></p>
<img src="https://velog.velcdn.com/images/sungwoo_dev/post/fae6d37d-43e4-4956-a15c-e0b9ac497f6d/image.png" width="640">

<p>컴파일 후 실행 → 정상적으로 <strong>자동 콤보 동작</strong> 확인</p>
<ul>
<li>모션 갯수가 달라도 배열 크기에 맞춰 자동 반복 가능</li>
</ul>
<hr>
<h2 id="2-이동-중-공격-개선-방안">2) 이동 중 공격 개선 방안</h2>
<p>현재 문제: 이동하면서 공격 모션이 부자연스럽다.
해결 방법은 두 가지:</p>
<ol>
<li>공격 시 이동 중지</li>
<li>하체는 이동, 상체는 공격 (Layered Blend Per Bone)</li>
</ol>
<hr>
<h2 id="3-블렌드-스페이스로-자연스러운-이동-구현">3) 블렌드 스페이스로 자연스러운 이동 구현</h2>
<p>현재는 <strong>Run 애니메이션만 사용</strong>해서 모든 방향이 앞으로 달리는 모션만 나온다.
→ Kwang 모션에는 방향별 모션이 있으니 블렌드 스페이스로 묶어주자.</p>
<img src="https://velog.velcdn.com/images/sungwoo_dev/post/2fb4271f-6eb2-42da-b113-1da785288b1e/image.png" width="600">

<hr>
<h3 id="3-1-블렌드-스페이스-생성">3-1) 블렌드 스페이스 생성</h3>
<img src="https://velog.velcdn.com/images/sungwoo_dev/post/88673219-ea4f-4d81-bdbb-38b6b817bec1/image.png" width="600">
<img src="https://velog.velcdn.com/images/sungwoo_dev/post/84e25af7-9ef8-4e39-8bd2-0ca1e8c045ac/image.png" width="600">

<ul>
<li>BlendSpace1D: 축 1개</li>
<li>BlendSpace: 축 2개</li>
</ul>
<img src="https://velog.velcdn.com/images/sungwoo_dev/post/fc37330c-7ee4-4df9-b5ec-3b9ba455ec09/image.png" width="800">

<p><code>BSWarriorIdleAndRun</code> 블렌드 스페이스 생성 (스켈레톤 지정)</p>
<hr>
<h3 id="3-2-블렌드-스페이스-세팅">3-2) 블렌드 스페이스 세팅</h3>
<img src="https://velog.velcdn.com/images/sungwoo_dev/post/71a1dc70-d127-4e8b-87a8-198c5c227afb/image.png" width="600">

<p>에디터 화면</p>
<img src="https://velog.velcdn.com/images/sungwoo_dev/post/59fa7e8b-f8b7-4a66-b1dc-be88005ed7ca/image.png" width="500">

<p>축 설정 예시</p>
<ul>
<li>가로축: Dir, -180.0 ~ 180.0, 그리드 4</li>
<li>세로축: Speed, 0 ~ 600(캐릭터 최대 이동속도), 그리드 1</li>
</ul>
<img src="https://velog.velcdn.com/images/sungwoo_dev/post/afb1a974-76f8-4711-a3e3-60292d02931b/image.png" width="500">

<p>샘플 이름표시 가능</p>
<img src="https://velog.velcdn.com/images/sungwoo_dev/post/e7b450d0-7e23-42ca-826e-419c25a97f02/image.png" width="800">

<p>Ctrl 누른 상태에서 아이콘 이동 → 블렌딩 미리보기 가능</p>
<hr>
<h3 id="3-3-애님-블루프린트-연결">3-3) 애님 블루프린트 연결</h3>
<img src="https://velog.velcdn.com/images/sungwoo_dev/post/9ad1384f-2fb1-482e-a2af-08c551b37d4f/image.png" width="600">

<p>AnimBlueprint → Locomotion 연결 해제
새 스테이트 머신 생성 → Ground Locomotion 연결</p>
<img src="https://velog.velcdn.com/images/sungwoo_dev/post/dd19b260-809a-448a-ac9c-27e40cea106a/image.png" width="600">

<p>Locomotion1에 Entry → IdleAndRun 스테이트 추가</p>
<img src="https://velog.velcdn.com/images/sungwoo_dev/post/f4dadcb8-c570-4c05-a279-071abc3857b5/image.png" width="600">

<p>IdleAndRun 스테이트에 <code>BSWarriorIdleAndRun</code> 배치</p>
<hr>
<h3 id="3-4-c-변수-연동">3-4) C++ 변수 연동</h3>
<img src="https://velog.velcdn.com/images/sungwoo_dev/post/b62fa9a0-f13b-4ec8-918e-a7ca63cab489/image.png" width="600">

<p>AnimInstance에 변수 추가</p>
<pre><code class="language-cpp">UPROPERTY(VisibleAnywhere, BlueprintReadOnly)
float MoveSpeed;

UPROPERTY(VisibleAnywhere, BlueprintReadOnly)
float Dir;</code></pre>
<img src="https://velog.velcdn.com/images/sungwoo_dev/post/8c98ff81-b6b5-4a56-9d13-37ee66a40530/image.png" width="600">

<p>PlayerCharacter.cpp → MoveKey 함수에서 방향값 계산</p>
<img src="https://velog.velcdn.com/images/sungwoo_dev/post/d1059eb9-3581-47b6-9ed2-27b8bd07e4ae/image.png" width="600">

<p>AnimInstance에 <code>SetDir</code> 함수 선언 및 정의</p>
<hr>
<h3 id="3-5-애님-블루프린트-변수-세팅">3-5) 애님 블루프린트 변수 세팅</h3>
<img src="https://velog.velcdn.com/images/sungwoo_dev/post/20247605-c431-463b-8639-ab17711e6e0e/image.png" width="600">

<p>컴파일 후 변수 연결 가능</p>
<img src="https://velog.velcdn.com/images/sungwoo_dev/post/947a8bfe-01b6-4979-9a20-343fa61dcbba/image.png" width="600">

<p>변수 세팅 예시</p>
<img src="https://velog.velcdn.com/images/sungwoo_dev/post/ac8f2243-a4fd-4b2f-864f-e855e18164df/image.png" width="800">

<p>정상 동작 확인</p>
<hr>
<h2 id="4-마무리">4) 마무리</h2>
<ul>
<li><p><strong>공격</strong>: mAttackEnable / mAttackCombo 플래그로 콤보 노티파이를 조건부 호출</p>
</li>
<li><p><strong>이동</strong>: 블렌드 스페이스로 방향+속도 기반 자연스러운 러닝 구현</p>
</li>
<li><p><strong>개선 포인트</strong></p>
<ol>
<li>공격 중 이동은 상체/하체 분리 블렌딩으로 자연스럽게</li>
<li>콤보 입력은 노티파이 윈도우(AnimNotifyState)로 정확한 입력 타이밍 제어 가능</li>
</ol>
</li>
</ul>
<hr>
]]></description>
        </item>
        <item>
            <title><![CDATA[애니메이션 몽타주]]></title>
            <link>https://velog.io/@sungwoo_dev/%EC%95%A0%EB%8B%88%EB%A9%94%EC%9D%B4%EC%85%98-%EB%AA%BD%ED%83%80%EC%A3%BC</link>
            <guid>https://velog.io/@sungwoo_dev/%EC%95%A0%EB%8B%88%EB%A9%94%EC%9D%B4%EC%85%98-%EB%AA%BD%ED%83%80%EC%A3%BC</guid>
            <pubDate>Mon, 01 Sep 2025 07:12:54 GMT</pubDate>
            <description><![CDATA[<hr>
<h2 id="0-라이브-코딩과-디버깅">0) 라이브 코딩과 디버깅</h2>
<ul>
<li>F5 디버깅은 실행이 꺼졌다 재시작되므로, 실행 중 수정하려면 라이브 코딩을 켠다.</li>
<li>끄는 법: 편집 &gt; 에디터 개인설정 &gt; 라이브 코딩 &gt; 활성화 체크 해제</li>
</ul>
<hr>
<h2 id="1-공격-몽타주-생성">1) 공격 몽타주 생성</h2>
<img src="https://velog.velcdn.com/images/sungwoo_dev/post/b77e5d0f-8493-410d-a89c-608f5b7ce432/image.png" width="800">

<p>애니메이션 &gt; 애니메이션 몽타주</p>
<img src="https://velog.velcdn.com/images/sungwoo_dev/post/9aa02aa0-086a-4615-b3e3-643161f00594/image.png" width="520">

<p>사용할 스켈레톤 선택</p>
<img src="https://velog.velcdn.com/images/sungwoo_dev/post/ab97bc88-9e18-4c90-b402-30c20bc9bb48/image.png" width="520">

<p>AMWarriorAttack 몽타주 생성</p>
<img src="https://velog.velcdn.com/images/sungwoo_dev/post/48c7c1a9-58e3-4f58-a647-60a564b11dad/image.png" width="800">

<p>PrimaryAttack 시리즈 애니메이션을 드래그해 배치하면, 링크되어 있으면 순차 재생된다.</p>
<hr>
<h2 id="2-슬롯그룹과-t-포즈-이슈">2) 슬롯/그룹과 T-포즈 이슈</h2>
<img src="https://velog.velcdn.com/images/sungwoo_dev/post/d4b4856e-7eca-4a10-ba30-6abd8625b156/image.png" width="800">

<p>창 &gt; 애님 슬롯 매니저</p>
<img src="https://velog.velcdn.com/images/sungwoo_dev/post/9cf397fa-781e-4b60-9c02-9ba2353a11b3/image.png" width="800">

<p>슬롯 추가 &gt; Attack</p>
<img src="https://velog.velcdn.com/images/sungwoo_dev/post/09f419c7-08b9-4de8-a119-9c18e279136b/image.png" width="520">

<p>세팅 후 T-포즈가 보이면</p>
<img src="https://velog.velcdn.com/images/sungwoo_dev/post/4dd699f4-d2f4-4f35-9220-6ef6e5987bf5/image.png" width="520">

<p>기존 섹션 이름이 Default라서 헷갈릴 수 있다 → 섹션 이름을 바꾸면 정상 재생</p>
<img src="https://velog.velcdn.com/images/sungwoo_dev/post/9fc6c203-7959-410c-8892-10187d11d092/image.png" width="520">

<p>추가 팁</p>
<ul>
<li>AnimGraph에 Attack 슬롯 노드가 경로상에 없으면 몽타주가 출력되지 않는다.</li>
<li>몽타주의 슬롯 이름과 AnimGraph Slot 노드 이름이 다르면 재생되지 않는다.</li>
<li>스켈레톤이 다른 애셋을 섞으면 T-포즈가 뜬다.</li>
</ul>
<hr>
<h2 id="3-섹션-분할과-링크">3) 섹션 분할과 링크</h2>
<img src="https://velog.velcdn.com/images/sungwoo_dev/post/b647334f-3593-4f1f-af2f-6552b82d0896/image.png" width="800">

<p>Attack1~4 섹션 생성</p>
<img src="https://velog.velcdn.com/images/sungwoo_dev/post/520374d8-9a2d-4501-a2ad-975fdce2e47d/image.png" width="640">

<p>섹션 링크 상태 확인</p>
<img src="https://velog.velcdn.com/images/sungwoo_dev/post/1fd18856-908f-49cb-99b6-ad6bbeb7c99b/image.png" width="640">

<p>독립 재생하려면 섹션 링크를 끊는다</p>
<img src="https://velog.velcdn.com/images/sungwoo_dev/post/f8ed6913-7e19-4e09-9b6c-3276a9307517/image.png" width="640">

<p>섹션 이름 가이드</p>
<ul>
<li>간단: Attack1, Attack2, Attack3, Attack4</li>
<li>확장: Attack_Air, Attack_Down, Attack_Heavy 등 컨텍스트를 이름에 담아두면 유지보수에 유리</li>
</ul>
<hr>
<h2 id="4-슬롯-노드와-animgraph-경로">4) 슬롯 노드와 AnimGraph 경로</h2>
<img src="https://velog.velcdn.com/images/sungwoo_dev/post/a8c1eeaa-2428-431c-ab9a-79abe79fd457/image.png" width="640">

<p>특정 슬롯의 몽타주만 재생하도록 Slot 노드를 사용한다</p>
<img src="https://velog.velcdn.com/images/sungwoo_dev/post/41536236-fb6f-41c4-8a5f-e105c9abf753/image.png" width="640">

<p>DefaultSlot 개념</p>
<img src="https://velog.velcdn.com/images/sungwoo_dev/post/9b50244d-ea70-48f3-a417-2ffd7cb9560a/image.png" width="640">

<p>Slot에 재생 중인 몽타주가 없으면 Source 포즈가 그대로 통과한다</p>
<img src="https://velog.velcdn.com/images/sungwoo_dev/post/84c81073-ff0a-4a2d-bf14-ef2b456b8dfa/image.png" width="800">

<p>AnimGraph에 Attack 슬롯 배치</p>
<img src="https://velog.velcdn.com/images/sungwoo_dev/post/52180dce-af06-4040-a627-cf912939625e/image.png" width="800">

<p>같은 경로 구조라면 보이는 변화는 없을 수 있다</p>
<img src="https://velog.velcdn.com/images/sungwoo_dev/post/2fe0b43f-b830-44a5-8897-b0077d41c096/image.png" width="640">

<p>개선 팁</p>
<ul>
<li>상체만 공격 모션, 하체는 이동 유지하려면 Slot 위에 Layered blend per bone을 배치하고 Spine_01 기준으로 분기한다.</li>
<li>몽타주가 전신을 잠식해 움직임이 딱딱해지는 문제를 줄일 수 있다.</li>
</ul>
<hr>
<h2 id="5-animinstance-변수와-에디터-셋업">5) AnimInstance 변수와 에디터 셋업</h2>
<img src="https://velog.velcdn.com/images/sungwoo_dev/post/0f1a9f9b-e507-4b63-9c5d-cc68dd6cc3c6/image.png" width="800">

<p>AttackMontage 변수 선언</p>
<img src="https://velog.velcdn.com/images/sungwoo_dev/post/03d29c8b-fadc-4517-b7f9-61a8f23bdda3/image.png" width="800">

<p>클래스 디폴트에서 AMWarriorAttack 할당</p>
<img src="https://velog.velcdn.com/images/sungwoo_dev/post/f49ce19d-48c8-4797-8048-6912a35d2481/image.png" width="800">

<p>입력 처리 위치 예시(엔핸스드 인풋과 연동)</p>
<img src="https://velog.velcdn.com/images/sungwoo_dev/post/8107f880-6aba-41d1-b919-46a3b41ba882/image.png" width="600">

<p>APlayerCharacter.h에 NormalAttack 가상함수</p>
<img src="https://velog.velcdn.com/images/sungwoo_dev/post/cfbb91c0-e3ee-44d2-85c5-7cd746cc2880/image.png" width="600">

<p>cpp 기본 구현</p>
<img src="https://velog.velcdn.com/images/sungwoo_dev/post/10fb78ee-19ea-467a-805f-4c93e4a9f694/image.png" width="600">

<p>Warrior.h 오버라이드</p>
<img src="https://velog.velcdn.com/images/sungwoo_dev/post/52bc24df-7c40-4586-89f7-648d4f9eb831/image.png" width="600">

<p>Warrior.cpp 구현</p>
<img src="https://velog.velcdn.com/images/sungwoo_dev/post/fbea332a-4c01-4362-9be7-3304d3498351/image.png" width="800">

<p>APlayerCharacter.cpp NormalAttack 구현 확인</p>
<p>핵심 포인트</p>
<ul>
<li>mAnimInst 타입은 UPlayerDefaultAnimInstance* 로 캐스팅해두면 멤버 함수 접근이 편하다.</li>
<li>AttackMontage가 비어 있으면 재생되지 않는다.</li>
</ul>
<hr>
<h2 id="6-playattack-구현과-섹션-배열">6) PlayAttack 구현과 섹션 배열</h2>
<img src="https://velog.velcdn.com/images/sungwoo_dev/post/2665eeac-9d80-40b3-836e-72317b57873e/image.png" width="800">

<p>헤더에 PlayAttack 선언</p>
<img src="https://velog.velcdn.com/images/sungwoo_dev/post/56a89779-98dd-4603-82d6-026c1887b697/image.png" width="600">

<p>cpp 정의</p>
<img src="https://velog.velcdn.com/images/sungwoo_dev/post/02928a79-60b1-453d-a536-08bfd3ad8f7a/image.png" width="600">

<p>AnimInstance 유효 체크</p>
<img src="https://velog.velcdn.com/images/sungwoo_dev/post/ad00a116-af72-40f5-883a-014c070b8783/image.png" width="600">

<p>Montage_Play, Montage_JumpToSection 사용</p>
<img src="https://velog.velcdn.com/images/sungwoo_dev/post/2d72d49e-6d89-4164-8dff-40b75e234877/image.png" width="800">

<p>섹션 이름 배열 + 현재 인덱스</p>
<img src="https://velog.velcdn.com/images/sungwoo_dev/post/409467fd-a635-4d94-a91c-d6e4404cf4f8/image.png" width="800">

<p>Attack Section Name 가시화</p>
<img src="https://velog.velcdn.com/images/sungwoo_dev/post/d31c1b12-cc86-4838-b7f8-7faf84ad710e/image.png" width="600">

<p>Attack1~4 이름 지정</p>
<img src="https://velog.velcdn.com/images/sungwoo_dev/post/f25f217b-ada8-4dc7-a38d-350156bcd8e8/image.png" width="600">

<p>배열에 이름이 잘 들어간 상태</p>
<img src="https://velog.velcdn.com/images/sungwoo_dev/post/ecdb0313-e6fe-4920-9fdc-2b3701d2657e/image.png" width="800">

<p>PlayAttack에서 섹션 점프</p>
<p>샘플 코드</p>
<pre><code class="language-cpp">// PlayerDefaultAnimInstance.h
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category=&quot;Attack&quot;)
TObjectPtr&lt;UAnimMontage&gt; AttackMontage;

UPROPERTY(EditAnywhere, BlueprintReadWrite, Category=&quot;Attack&quot;)
TArray&lt;FName&gt; AttackSectionNames = { &quot;Attack1&quot;,&quot;Attack2&quot;,&quot;Attack3&quot;,&quot;Attack4&quot; };

int32 AttackIndex = 0;

UFUNCTION(BlueprintCallable) void PlayAttack();
UFUNCTION() void OnMontageEnded(UAnimMontage* Montage, bool bInterrupted);</code></pre>
<pre><code class="language-cpp">// PlayerDefaultAnimInstance.cpp
void UPlayerDefaultAnimInstance::PlayAttack()
{
    if (!AttackMontage || AttackSectionNames.Num() == 0) return;

    if (!Montage_IsPlaying(AttackMontage))
    {
        Montage_Play(AttackMontage, 1.0f);
        Montage_JumpToSection(AttackSectionNames[AttackIndex], AttackMontage);

        FOnMontageEnded End;
        End.BindUObject(this, &amp;UPlayerDefaultAnimInstance::OnMontageEnded);
        Montage_SetEndDelegate(End, AttackMontage);
    }
}

void UPlayerDefaultAnimInstance::OnMontageEnded(UAnimMontage* Montage, bool bInterrupted)
{
    if (!bInterrupted) AttackIndex = 0;
}</code></pre>
<hr>
<h2 id="7-이동-중-어색함과-지상-조건">7) 이동 중 어색함과 지상 조건</h2>
<img src="https://velog.velcdn.com/images/sungwoo_dev/post/47f4a144-22b1-4497-bb1b-624d47fa604f/image.png" width="800">

<p>이동 중 전신 몽타주가 들어오면 부자연스럽다 → 지상에서만 허용</p>
<img src="https://velog.velcdn.com/images/sungwoo_dev/post/e7a7fad4-ad99-40e8-8a55-0d196057a408/image.png" width="640">

<p>지상 체크 예시</p>
<pre><code class="language-cpp">void APlayerCharacter::NormalAttack()
{
    if (GetCharacterMovement() &amp;&amp; GetCharacterMovement()-&gt;IsMovingOnGround())
    {
        if (AnimInst) AnimInst-&gt;PlayAttack();
    }
}</code></pre>
<p>개선 팁</p>
<ul>
<li>상체 전용 Layered blend per bone + Attack Slot로 하체 이동 유지</li>
<li>몽타주 Root Motion을 쓰면 이동/회전이 함께 걸릴 수 있으니 프로젝트 목적에 맞춰 비활성/활성 결정</li>
</ul>
<hr>
<h2 id="8-콤보·히트-판정-노티파이">8) 콤보·히트 판정 노티파이</h2>
<img src="https://velog.velcdn.com/images/sungwoo_dev/post/c22667b7-9831-4294-9d2f-4a4e5643a2b1/image.png" width="800">

<p>히트 타이밍과 콤보 전환 타이밍이 필요</p>
<img src="https://velog.velcdn.com/images/sungwoo_dev/post/e11d4f11-a414-4c4f-b210-4d4280862051/image.png" width="800">

<p>애니메이션 더블클릭으로 타임라인 열기</p>
<img src="https://velog.velcdn.com/images/sungwoo_dev/post/9eae1eca-6a96-4021-90e9-59ab2625fb4f/image.png" width="560">

<p>공격판정 노티파이 Attack 생성</p>
<img src="https://velog.velcdn.com/images/sungwoo_dev/post/6322cbc1-3514-458a-8440-c653cfd1a435/image.png" width="800">

<p>무기 스윙 위치에 충돌체 또는 트레이스 적용 권장</p>
<img src="https://velog.velcdn.com/images/sungwoo_dev/post/3e17439a-7ac9-4c0e-a12d-226496988b5a/image.png" width="800">

<p>AttackCombo 노티파이도 각 섹션에 배치</p>
<img src="https://velog.velcdn.com/images/sungwoo_dev/post/092263ac-7f60-4340-ac6e-c5a7dba19df1/image.png" width="560">

<p>몽타주 종료 콜백 함수 선언</p>
<img src="https://velog.velcdn.com/images/sungwoo_dev/post/b223b88a-3e52-4bbd-9d30-588128b11f90/image.png" width="560">

<p>cpp 정의</p>
<img src="https://velog.velcdn.com/images/sungwoo_dev/post/9cf1e7b7-a987-4fd6-8c10-746274822eee/image.png" width="560">

<p>MontageEnd 호출 시점</p>
<img src="https://velog.velcdn.com/images/sungwoo_dev/post/095e4b35-a612-43e5-8a71-c207bbe42bc0/image.png" width="560">

<p>Interrupted true/false 의미</p>
<img src="https://velog.velcdn.com/images/sungwoo_dev/post/f979ad72-0747-4227-9afd-1e37991a5215/image.png" width="560">

<p>AnimNotify 함수 선언</p>
<img src="https://velog.velcdn.com/images/sungwoo_dev/post/f6e9171a-15d8-4d70-bcf2-017f9d44afdd/image.png" width="800">

<p>cpp 구현</p>
<p>노티파이 샘플</p>
<pre><code class="language-cpp">// PlayerDefaultAnimInstance.h
UFUNCTION() void AnimNotify_Attack();      // 히트 판정
UFUNCTION() void AnimNotify_AttackCombo(); // 콤보 인덱스 증가</code></pre>
<pre><code class="language-cpp">// PlayerDefaultAnimInstance.cpp
void UPlayerDefaultAnimInstance::AnimNotify_Attack()
{
    // 무기 소켓 경로로 짧은 스윙 트레이스 추천
    // UKismetSystemLibrary::SphereTraceMultiForObjects(...) 등
}

void UPlayerDefaultAnimInstance::AnimNotify_AttackCombo()
{
    AttackIndex = (AttackIndex + 1) % AttackSectionNames.Num();
}</code></pre>
<p>고급 팁</p>
<ul>
<li>입력 타이밍을 통제하려면 노티파이 대신 노티파이 윈도우(AnimNotifyState)로 콤보 입력 가능 구간을 열고 닫는다.</li>
<li>사용자가 콤보 키를 해당 구간에 입력하면 다음 섹션을 <code>Montage_SetNextSection</code>으로 자연스럽게 이어줄 수 있다.</li>
</ul>
<hr>
<h2 id="9-입력-시스템-연결엔핸스드-인풋">9) 입력 시스템 연결(엔핸스드 인풋)</h2>
<p>기본 흐름</p>
<ul>
<li>프로젝트 시작 시 EnhancedInputLocalPlayerSubsystem에 IMC(매핑 컨텍스트) 추가</li>
<li>IA_Attack을 액션으로 만들고 캐릭터에서 바인딩</li>
<li>바인딩된 함수에서 NormalAttack 호출</li>
</ul>
<p>예시</p>
<pre><code class="language-cpp">void APlayerCharacter::SetupPlayerInputComponent(UInputComponent* PlayerInputComponent)
{
    if (UEnhancedInputComponent* EIC = Cast&lt;UEnhancedInputComponent&gt;(PlayerInputComponent))
    {
        EIC-&gt;BindAction(IA_Attack, ETriggerEvent::Started, this, &amp;APlayerCharacter::Attackkey);
    }
}

void APlayerCharacter::Attackkey(const FInputActionValue&amp; Value)
{
    UE_LOG(LogTemp, Log, TEXT(&quot;Attackkey Enter&quot;));
    NormalAttack();
}</code></pre>
<hr>
<h2 id="10-super-호출은-언제-필요한가">10) Super 호출은 언제 필요한가</h2>
<p>문제 상황 요약</p>
<ul>
<li>Warrior에서 <code>Super::NormalAttack();</code>를 호출하니 동작했고, 호출하지 않으면 동작하지 않았다.</li>
</ul>
<p>이유</p>
<ul>
<li>현재 설계에서 부모 APlayerCharacter의 <code>NormalAttack()</code>이 실제 재생 로직(<code>mAnimInst-&gt;PlayAttack()</code>)을 가지고 있다.</li>
<li>자식이 부모를 호출하지 않으면 재생 로직이 실행되지 않는다.</li>
</ul>
<p>정리</p>
<ul>
<li>Super 호출은 항상 필요한 게 아니라, 부모가 필수 로직을 가지고 있을 때만 필요하다.</li>
<li>대안 1: 부모를 순수 가상 함수로 두고 재생 로직을 전부 자식이 갖는다.</li>
<li>대안 2: 재생은 AnimInstance 쪽으로만 몰고, 캐릭터 계층에서는 입력 신호 전달만 한다.</li>
</ul>
<p>권장 구조</p>
<ul>
<li>입력 → 캐릭터 → AnimInstance.PlayAttack()</li>
<li>재생의 단일 책임을 AnimInstance로 모아 테스트/유지보수 난이도를 낮춘다.</li>
</ul>
<hr>
<h2 id="11-유지보수-팁과-체크리스트">11) 유지보수 팁과 체크리스트</h2>
<p>체크리스트</p>
<ul>
<li>AttackMontage 변수에 실제 몽타주 할당됐나</li>
<li>AnimGraph 경로에 Attack 슬롯 노드가 있나</li>
<li>슬롯 이름이 몽타주 슬롯과 정확히 같나(대소문자 주의)</li>
<li>스켈레톤이 일치하나</li>
<li>섹션 이름 오타 없는가</li>
<li>이동 중 공격 자연스럽게 하려면 상체 전용 블렌딩을 썼나</li>
<li>루트모션을 의도대로 켜거나 껐나</li>
</ul>
<p>성능/안정성</p>
<ul>
<li>섹션 이름은 FString 대신 FName 사용</li>
<li>델리게이트 바인딩은 중복되지 않도록 관리</li>
<li>ComboIndex는 범위를 모듈로 연산으로 안정화</li>
</ul>
<p>테스트 팁</p>
<ul>
<li>애님 프리뷰에서 Section Jump 미리보기</li>
<li>노티파이 타이밍은 프레임 스텝으로 미세 조정</li>
<li>디버그용 DrawDebugSphere/Line으로 히트 영역 시각화</li>
</ul>
<hr>
<h2 id="12-마무리">12) 마무리</h2>
<p>한 개의 몽타주로 4콤보를 깔끔하게 관리하려면</p>
<ul>
<li>섹션을 잘게 나누고 링크는 필요할 때만</li>
<li>슬롯/AnimGraph 경로를 정확히 배치</li>
<li>AnimInstance 중심의 PlayAttack, 콤보 인덱스, EndDelegate, 노티파이를 조합</li>
<li>상체 전용 블렌딩과 콤보 윈도우로 퀄리티 업</li>
</ul>
]]></description>
        </item>
    </channel>
</rss>