2026-04-06 TIL (30일차)
C++
• 프로그래머스 (콜라츠 추측)
원인 분석
- 홀수일 때의 연산 (
temp * 3 + 1) 콜라츠 추측의 규칙 중 홀수일 경우 3을 곱하고 1을 더합니다. 시작 숫자가 800만에 가까운 수이거나, 반복 과정에서 값이 계속 커지는 패턴을 만나면,int가 담을 수 있는 최대치를 넘겨버립니다. - 오버플로우(Overflow) 발생
- 무한 루프의 늪 값이 음수로 변해버렸기 때문에
temp == 1이라는 탈출 조건을 만족할 수 없게 되고, 오답을 내게 된 것입니다.
해결 방법: long long
숫자가 아무리 커지더라도 안전하게 담을 수 있도록, int보다 큰 자료형인 long long을 사용하는 것입니다.
1
2
3
4
5
// 수정 전
// int temp = num;
// 수정 후
long long temp = num;
Unreal
• C++ 프로젝트 폴더 구조
핵심 폴더
Config: 프로젝트의 기본 환경 설정, 단축키(Input), 그래픽 세팅 값(.ini)들이 저장되는 폴더입니다.Content: 블루프린트, 3D 모델링, 텍스처, 사운드, 맵(레벨) 등 게임의 모든 에셋(Asset)이 들어가는 가장 중요한 폴더입니다. (언리얼 에디터의 Content Browser와 연결되어 있음)Source: 직접 작성한 C++ 소스 코드(.cpp,.h)가 저장되는 폴더입니다.[프로젝트명].uproject: 언리얼 프로젝트 실행 파일입니다. 더블 클릭하면 언리얼 에디터가 켜집니다.
임시 및 자동 생성 폴더
Binaries: C++ 코드를 컴파일해서 만들어진 실행 파일(.exe,.dll)이 들어있습니다.Intermediate: 컴파일 과정에서 생성되는 중간(임시) 파일들이 모이는 곳입니다. (엔진에 알 수 없는 에러가 꼬였을 때 이 폴더를 지우고 다시 빌드하면 해결되는 경우가 많음.)DerivedDataCache: 텍스처나 셰이더 등을 매번 계산하지 않고 빠르게 로딩하기 위해 엔진이 미리 변환해둔 캐시 파일입니다. 용량을 가장 많이 잡아먹습니다.Saved: 에디터 자동 저장 파일, 백업, 크래시 로그, 로컬 세이브 데이터 등이 저장됩니다. (에러 로그가 저장되는 파일).vs: 비주얼 스튜디오의 로컬 설정 및 임시 캐시를 보관하는 숨김 폴더입니다.
주요 설정 및 실행 파일
[프로젝트명].sln: 비주얼 스튜디오 솔루션 파일입니다. C++ 코드를 작성할 때 이 파일을 엽니다..vsconfig: 비주얼 스튜디오에서 이 프로젝트를 열기 위해 필요한 컴포넌트 설치 정보가 담긴 파일입니다.
• 솔루션 구조
Engine (엔진 코어)
- 역할: 언리얼 엔진 자체의 소스 코드와 리소스가 담긴 곳입니다.
- 특징: 에디터 작동 및 엔진의 핵심 코어 관련 코드가 포함되어 있습니다. 엔진 자체를 뜯어고치는 고도화된 작업이 아니라면, 일반적인 개발 단계에서는 거의 수정하거나 열어볼 일이 없습니다.
Games
- 역할: 직접 만든 프로젝트 코드가 모여 있는 가장 핵심적인 폴더입니다.
- 특징: 내부를 열어보면 윈도우 탐색기에서 봤던
Source,Config폴더와.uproject파일 등이 들어있습니다. 앞으로 C++로 작성하는 모든 게임 로직과 클래스는 무조건 이 폴더 안에서 다루게 됩니다.
Programs (유틸리티)
- 역할: 언리얼 엔진 동작과 빌드에 필요한 유틸리티 프로그램(UnrealHeaderTool 등)이나 독립적인 서버 모듈이 담겨 있습니다.
- 특징: 일반적인 게임 클라이언트 개발 단계에서는 거의 볼 일이 없습니다.
Rules (빌드 규칙)
- 역할: 엔진과 게임 등 각 모듈의 빌드 규칙(Build Rules)을 정의해 놓은 파일(
Target.cs등)들이 모여 있습니다. - 특징: 다른 모듈과의 의존성 설정, 플러그인 활성화 여부, 빌드 타겟 등을 제어할 때 사용되는 일종의 설계도 모음집입니다.
Visualizers (디버깅 보조)
- 역할: Visual Studio 디버거를 사용할 때 도움을 주는 시각화 설정 파일(
.natvis)이 들어 있습니다. - 특징: 언리얼 엔진 특유의 복잡한 자료구조(
FVector,FString,TArray등)를 디버깅 창에서 사람이 읽고 이해하기 쉽게 변환해 주는 폴더입니다.
• 언리얼 엔진 C++ 빌드 파이프라인
“C++ 파일 수정 ➡️ 빌드 (컴파일 + 링크) ➡️ DLL 파일 ➡️ 언리얼 에디터”
1. C++ 파일 수정 (코드 작성)
- 과정: Visual Studio 등에서 프로그래머가 게임 로직(캐릭터 이동, 스킬 구현 등)이 담긴
.cpp와.h파일을 수정하고 저장합니다. - 특징: 이 코드는 사람이 읽을 수 있는 언어(Human-readable)이므로 컴퓨터는 아직 이해하지 못합니다.
2. 빌드 (컴파일 + 링크)
- 과정: 비주얼 스튜디오에서 빌드를 실행하면, 언리얼 엔진의 전용 빌드 도구인 UBT(Unreal Build Tool)가 작동합니다.
- 컴파일(Compile): 우리가 짠 C++ 코드를 컴퓨터가 알아들을 수 있는 기계어(
0과1)로 번역하여 목적 파일(.obj)들을 만듭니다. - 링크(Link): 번역된 여러 개의 조각 파일들과 언리얼 엔진의 기본 기능(라이브러리)들을 하나의 완성된 결과물로 연결하고 묶어줍니다.
- 컴파일(Compile): 우리가 짠 C++ 코드를 컴퓨터가 알아들을 수 있는 기계어(
3. DLL 파일 생성 (중요한 차이점)
- 과정: 링크 과정이 끝나면 단독 실행 파일인
.exe가 아니라, 동적 링크 라이브러리인.dll(Dynamic Link Library) 파일이 만들어집니다. .exe가 아닌 이유: 우리가 작업하는 ‘언리얼 에디터(UnrealEditor.exe)’ 자체가 실행 파일이기 때문입니다. 우리가 작성한 게임 코드는 에디터라는 프로그램 안에 부품처럼 끼워져서 돌아가는 ‘모듈(Module)’ 형태로 만들어져야 하므로.dll로 추출됩니다.
4. 언리얼 에디터 반영 (라이브 코딩 / 핫 리로드)
- 과정: 생성된 새로운 따끈따끈한
.dll파일을 언리얼 에디터가 실시간으로 받아들여서 메모리에 올립니다. - 결과: 엔진을 껐다가 다시 켤 필요 없이, 방금 비주얼 스튜디오에서 수정한 코드가 에디터 상의 캐릭터나 시스템에 즉각적으로 반영됩니다. 이 강력한 기능을 라이브 코딩(Live Coding) 또는 핫 리로드(Hot Reload)라고 부릅니다.
요약
“우리가 작성하는 C++ 게임 프로젝트는 그 자체로 독립적인 프로그램이 아니라, 언리얼 에디터라는 거대한 공장에 납품하는 부품(
.dll)을 만드는 과정이다. 그래서 코드를 수정하고 빌드하면, 에디터가 새 부품을 실시간으로 갈아 끼워주는 것이다!”
• 빌드 구성 종류
| 빌드 구성명 | 실행 환경 | 특징 및 주요 용도 |
|---|---|---|
DebugGame | Standalone (독립 실행) | 게임 로직만 디버그 정보를 포함하고, 엔진 자체는 최적화된 상태로 빌드합니다. 언리얼 에디터 없이 독립된 실행 파일 환경에서 내 게임 로직을 효과적으로 디버깅할 때 사용합니다. (엔진 코드는 디버깅 제한적) |
DebugGame Editor | Editor (에디터용) | 에디터 환경에서 게임 로직을 디버깅하기 가장 편한 설정입니다. 에디터 플레이(PIE) 중에 C++ 로직을 세밀하게 추적하거나 중단점(Breakpoint)을 걸고 테스트할 때 사용합니다. |
Development | Standalone (독립 실행) | 디버그 정보를 최소화해 실행 속도를 높인 개발용 빌드입니다. 에디터 없이 독립 실행 파일 환경에서 전반적인 테스트 및 개발을 진행하는 단계에서 주로 쓰입니다. |
Development Editor(기본 모드) | Editor (에디터용) | 에디터에서 개발·테스트를 원활히 할 수 있는 기본 빌드 모드입니다. 언리얼의 핵심 기능인 라이브 코딩(Live Coding)과의 궁합이 가장 좋으며, 초·중급자를 포함해 평상시 가장 많이 사용하는 세팅입니다. |
Shipping | Standalone (독립 실행) | 최종 사용자에게 배포(Release)할 때 사용하는 릴리스 빌드입니다. 모든 디버그 정보를 완전히 제거하여, 용량을 줄이고 성능 최적화가 극대화됩니다. |
• 전체 솔루션 빌드 vs. 부분 빌드
전체 솔루션 빌드
엔진, 유틸리티, 게임 모듈 등 솔루션 내의 모든 프로젝트를 통째로 묶어서 빌드하는 방식입니다.
- 실행 방법
- 비주얼 스튜디오 상단 메뉴:
빌드(Build)➡️솔루션 빌드(Build Solution) - 단축키:
Ctrl + Shift + B
- 비주얼 스튜디오 상단 메뉴:
- 사용 시기
- 프로젝트를 생성하고 가장 처음 빌드할 때
- 언리얼 엔진 자체의 소스 코드를 수정했을 때
- 빌드가 꼬여서 엔진 전체 파일을 갱신해야 할 때
- 특징: 규모가 방대한 언리얼 엔진의 특성상 모든 모듈을 검사하므로 빌드 시간이 오래 걸릴 수 있습니다.
부분 빌드
무거운 엔진이나 다른 모듈은 무시하고, 우리가 작성한 게임 프로젝트 코드(내 게임 로직)만 타겟으로 지정해 빠르게 빌드하는 방식입니다.
- 실행 방법
- 비주얼 스튜디오 우측 솔루션 탐색기(Solution Explorer) 창에서
Games폴더 안에 있는 내 프로젝트 이름(루트 폴더)을 우클릭 ➡️빌드(Build)클릭
- 사용 시기
- 일반적인 개발 상황에서 C++ 게임 로직(캐릭터 이동, 스킬 등)만 수정했을 때
- 특징: 내가 수정한 게임 코드만 뽑아서 컴파일하므로 전체 솔루션 빌드에 비해 속도가 빠르고 효율적입니다.
• 언리얼 C++ 빌드가 꼬였을 때
언리얼 엔진에서 C++ 코드를 수정했는데 에디터에 적용되지 않거나, 알 수 없는 컴파일 에러가 쏟아질 때 복구하는 방법입니다.
1단계: 임시 및 캐시 폴더 삭제
프로젝트 폴더로 이동하여, 엔진이 빌드 과정에서 만들어낸 임시 찌꺼기 파일들을 직접 날려줍니다.
- 삭제해야 할 폴더 목록:
BinariesIntermediateDerivedDataCacheSaved
2단계: Visual Studio 솔루션 파일 재생성
- 프로젝트의
.uproject파일을 우클릭합니다. - 메뉴에서
Generate Visual Studio project files를 클릭하여.sln파일을 새롭게 갱신합니다.
3단계: Visual Studio 클린 빌드
폴더를 지우고 Generate를 했는데도 문제가 여전하다면, 비주얼 스튜디오 내부에서 확실하게 청소를 해줘야 합니다.
- 방금 새로 생성된
.sln솔루션 파일을 Visual Studio로 엽니다. - 상단 메뉴에서
빌드(Build)➡️솔루션 정리(Clean Solution)를 클릭하여 혹시라도 남아있을 기존 빌드 산출물을 완벽하게 정리합니다. - 정리가 완료되면, 다시
빌드(Build)➡️솔루션 빌드(Build Solution)를 클릭하여 처음부터 깨끗하게 재빌드를 수행합니다. - 에러 원인 파악: 만약 빌드가 실패한다면 하단의 출력(Output) 창과 오류 목록(Error List)을 열어 실제 코드의 어느 부분에서 문제가 터졌는지 확인하고 수정합니다.
• 언리얼 오브젝트(UObject)
언리얼 엔진에서 C++ 클래스를 생성할 때 기본이 되는 개념은 UObject입니다.
- 개념: C++의 ‘추상 클래스’와 비슷한 느낌으로, 언리얼 엔진에 존재하는 거의 모든 클래스의 최상위 부모(Base Class) 역할을 합니다.
- 존재 이유: 일반적인 순수 C++ 클래스와 달리,
UObject를 상속받은 클래스는 언리얼 엔진이 제공하는 강력한 관리 기능(메모리 자동 관리(Garbage Collection), 블루프린트 연동(Reflection), 데이터 저장(Serialization))의 보호를 받을 수 있게 됩니다.
• Public 헤더와 Private CPP 분리되는 이유
언리얼 에디터에서 C++ 클래스를 생성할 때 Public으로 설정하면, 헤더 파일(.h)은 Public 폴더에, 소스 파일(.cpp)은 Private 폴더에 각각 나뉘어서 생성됩니다.
- 모듈 아키텍처
Public폴더 (.h): 외부로 공개하는 설명서입니다.- 우리 프로젝트뿐만 아니라, 다른 모듈(플러그인 등)에서도 이 클래스의 존재를 알고 접근(Include)할 수 있도록 완전히 열어두는 공간입니다.
Private폴더 (.cpp): 내부에 숨겨두는 실제 작동 로직입니다.- 외부에서는 이 클래스가 무슨 기능을 가졌는지만 알면 되지, 그 기능이 구체적으로 어떻게 코딩되어 있는지까지 알 필요는 없습니다.
- 이렇게 구현부를
Private에 숨겨두면(캡슐화), 코드를 수정하더라도 다른 모듈들에게 영향을 덜 주게 되어 컴파일 속도가 빨라지고 코드가 안전해집니다.
• 언리얼 C++ 클래스 완벽하게 삭제하는 방법
언리얼 엔진은 블루프린트와 달리, 에디터 내에서 C++ 클래스를 우클릭해서 ‘Delete’로 지우는 것을 허용하지 않습니다. 찌꺼기가 남아 빌드가 꼬이는 것을 막기 위해 반드시 아래의 방법을 따라야 합니다.
- Visual Studio에서 제거
- 파일 탐색기에서 직접 삭제: 윈도우 탐색기를 열고 프로젝트의
Source폴더로 직접 들어가서 삭제할 클래스의.h파일과.cpp파일을 찾아 완전히 삭제합니다. 추가적인 부분 - Generate Project Files:
.uproject파일을 우클릭하고Generate Visual Studio project files를 눌러 솔루션 파일 장부에서 삭제된 파일의 기록을 지워줍니다. - 빌드 후 실행: 새롭게 갱신된
.sln파일을 열어Build Solution을 진행한 뒤, 에디터를 실행하면 삭제가 완료된걸 확인 가능합니다.
• 언리얼 액터와 루트 컴포넌트
C++로 빈 액터 클래스를 만들고 레벨에 드래그해서 올리면, 내가 마우스로 놓은 위치가 아니라 **월드의 원점(0, 0, 0)으로 이동합니다. **
원인
- 빈 액터 그 자체는 그저 껍데기일 뿐, 월드 내의 좌표를 담을 수 있는 공간이 없습니다.
- 액터가 3D 공간에서 물리적인 좌표계를 인식하고 제 기능을 하려면, 반드시 최소 하나 이상의 컴포넌트를 가져야 하며, 이를 Root Component라고 부릅니다.
해결책
- 보통 C++에서 액터를 설계할 때는
USceneComponent를 생성하여 루트 컴포넌트로 지정합니다. - 이유:
USceneComponent는 화면에 렌더링되는 시각적 요소(메시 등)나 충돌체(콜리전) 없이, 오직 위치(Transform) 정보만 가지고 있는 가장 가벼운 컴포넌트입니다. 이를 기준점으로 삼고, 그 아래에 자식으로 다른 컴포넌트(메시, 파티클 등)를 추가해야 조립하기 좋기 때문입니다.
• 스태틱 메시 컴포넌트의 구조
액터의 외형을 담당하는 UStaticMeshComponent는 크게 2가지 구성 요소를 가집니다.
- Static Mesh: 눈에 보이는 3D 모델링 에셋 자체의 형태를 할당하는 속성입니다.
- Materials: 3D 모델의 표면에 입히는 재질, 색상, 질감 데이터입니다.
SetMaterial 함수 호출 시 인덱스(Index)가 필요한 이유
C++에서 메시에 머티리얼을 적용할 때 SetMaterial(0, MyMaterial) 처럼 앞에 인덱스를 작성해야 합니다.
- 이유: 3D 모델(메시) 하나에는 머티리얼 슬롯이 여러 개 존재할 수 있기 때문입니다.
• 커스텀 로그
DECLARE_LOG_CATEGORY_EXTERN을 사용하면 커스텀 로그 카테고리(예: LogPlayer, LogInventory)를 만들어 원하는 로그만 필터링해서 볼 수 있습니다.
1
2
3
4
5
6
.h 파일
DECLARE_LOG_CATEGORY_EXTERN(LogMyCharacter, Log, All);
.cpp 파일
DEFINE_LOG_CATEGORY(LogMyCharacter);
게임 전반적으로 쓰이는 로그 카테고리를 프로젝트의 메인 헤더 파일([프로젝트명].h)이나 공용 유틸리티 헤더에 만들어 두고 씁니다.
• 언리얼 Actor 라이프 사이클
Actor는 생성 ➡️ 초기화 ➡️ 월드 배치 ➡️ 실행(Tick) ➡️ 제거라는 흐름을 가집니다.
- 생성 및 초기화
생성자
- 호출 시점: C++ 클래스 객체가 메모리에 생성될 때 딱 한 번 호출.
- 상태: 아직 월드(World)에 완전히 등록되지 않은 상태입니다.
- 주요 역할:
CreateDefaultSubobject를 사용한 컴포넌트 생성.- 변수들의 기본값 초기화.
- 주의: 월드나 다른 액터를 참조하면 크래시가 날 수 있습니다. 단순한 ‘할당’만 해야 합니다.
PostInitializeComponents()
- 호출 시점: 액터에 붙은 모든 컴포넌트가 생성 및 초기화를 마친 직후 호출.
- 상태: 내 액터의 부품(컴포넌트)들이 모두 준비 완료된 상태입니다.
- 주요 역할: 컴포넌트 간의 의존성 설정 (예: A 컴포넌트가 B 컴포넌트를 참조해야 할 때 이곳에서 연결).
- 게임 플레이
BeginPlay()
- 호출 시점: 게임이 본격적으로 시작되거나, 런타임 중
SpawnActor로 새 액터가 스폰될 때 딱 한 번 호출. - 상태: 월드와 다른 액터들이 모두 준비된 상태입니다.
- 주요 역할:
- 다른 액터, AI, 게임 모드 등과의 상호작용 시작.
- 타이머 시작, 델리게이트(Delegate) 이벤트 바인딩.
Tick(float DeltaTime)
- 호출 시점: 매 프레임마다 지속적으로 호출. (설정에서
PrimaryActorTick.bCanEverTick = true;켜기 필수) - 주요 역할: 캐릭터 이동, 부드러운 카메라 추적, 실시간 물리 연산 등.
- 주의: 매 프레임 실행되므로 무거운 연산을 넣으면 프레임 드랍의 주범이 됩니다. 가급적 이벤트(Event/Delegate) 방식으로 대체하는 것이 성능에 좋습니다.
- 소멸 및 정리
Destroyed()
- 호출 시점:
Destroy()함수를 호출하여 액터를 수동으로 파괴할 때 그 직전에 호출. - 특징: 수동 삭제 시 마지막 정리 코드를 넣기 좋으나, 레벨이 넘어가거나 게임이 강제 종료될 때는 호출을 건너뛸 수 있습니다. (절대적 보장 X)
EndPlay(const EEndPlayReason::Type EndPlayReason)
- 호출 시점: 파괴, 레벨 전환, 에디터(게임) 종료 등 액터가 월드에서 퇴장할 때 무조건 호출.
- 특징: 매개변수(
EndPlayReason)를 통해 왜 죽었는지(파괴된 건지, 레벨이 끝난 건지) 이유를 알 수 있습니다. - 주요 역할 (반드시 정리해야 할 자원들):
- 타이머 해제:
GetWorldTimerManager().ClearTimer(...) - 메모리 해제:
new등으로 동적 할당한 리소스가 있다면 여기서delete. - 델리게이트 해제: 외부에 바인딩해 둔 이벤트 연결 끊기.
- 타이머 해제:
• Super::의 의미
- 개념:
Super는 내 클래스의 ‘부모 클래스’를 가리키는 언리얼 엔진의 특수한 키워드입니다. - 동작:
Super::BeginPlay()라고 적으면, 내 코드를 실행하기 전에 부모 클래스에 구현되어 있는BeginPlay()로직을 먼저 실행하라는 뜻이 됩니다.
써야 하는 이유
언리얼 엔진이 미리 만들어둔 거대한 클래스(AActor, ACharacter 등)를 상속받아 만듭니다.
부모 클래스의 BeginPlay()나 Tick() 내부에는 단순히 빈칸이 아니라, 물리 엔진 적용, 컴포넌트 활성화, 네트워킹 초기화 등 엔진이 게임을 굴리기 위해 반드시 필요한 ‘기초 공사 코드’들이 적혀 있습니다.
우리가 함수를 오버라이드(재정의)해버리면 부모의 코드가 내 코드로 완전히 덮어씌워지기 때문에, “원래 하시던 기초 공사 먼저 하고, 그 다음에 작성한 코드를 실행한다”라고 알려주는 것이 바로 Super:: 호출입니다.
빼먹는다면 어떻게 될까?
- 캐릭터가 중력을 무시하고 허공에 떠 있거나 조작 키가 먹히지 않습니다.
- 충돌체(Collision)가 작동하지 않아 벽을 뚫고 지나갑니다.
- 최악의 경우, 엔진 내부에서 필요한 초기화가 안 되어 언리얼 에디터가 크래시(Crash)를 내며 튕겨버립니다.

