오늘의 할일
1. 코드카타 정리
2. 세션 정리
3. 궁금증 해결
길이가 n이고, "수박수박수박수...."와 같은 패턴을 유지하는 문자열을 리턴하는 함수
#include <string>
#include <vector>
using namespace std;
string solution(int n) {
string answer = "";
for (int i = 0; i < n; i++) {
if ( i % 2 == 0) {
answer += "수";
} else {
answer += "박";
}
}
return answer;
}
AI가 작성한 코드
#include <string>
using namespace std;
string solution(int n) {
string answer = "";
// 1. 메모리 재할당 방지 (선택 사항이지만 성능 최적화에 좋음)
// 한글은 보통 1글자에 3바이트를 차지하므로, n * 3 크기만큼 미리 공간을 확보해 둡니다.
answer.reserve(n * 3);
// 2. 반복 횟수를 절반(n / 2)으로 줄여 "수박"을 통째로 이어 붙입니다.
for (int i = 0; i < n / 2; ++i) {
answer += "수박";
}
// 3. n이 홀수일 경우, 마지막에 짝을 맞추지 못한 "수"만 한 번 더해줍니다.
if (n % 2 != 0) {
answer += "수";
}
return answer;
}
이 방식을 선호하는 이유
1. 연산 횟수 감소: n번 반복하며 짝홀수(% 2)를 매번 계산하던 것을, n / 2번 반복으로 확 줄였습니다.
비주얼 스튜디오와 언리얼을 연동하는 과정을 진행하고 폴더의 구조를 알아보았다

이렇게 비주얼 스튜디오 상에 보이는 구조가 솔루션 구조
프로젝트 폴더 구조와는 조금 다름

Engine 폴더 : 언리얼 엔진 자체 소스 코드와 리소스가 담긴 곳
Games 폴더 : 프로젝트 코드(Source , Config , .uproject 파일 등)
Programs 폴더 : 엔진 동작에 필요한 유틸리티 프로그램이나 서버 모듈
Rules 폴더 : 엔진과 게임 등 각 모듈의 빌드 규칙을 정의해 놓은 파일
Visualizers 폴더 : 언리얼 엔진의 복잡한 자료구조를 보다 읽기 쉽게 시각화할 수 있도록 도와주는 설정 파일
Games 폴더 안에는 프로젝트 이름이 보이는데 그걸 드롭다운하면 여러가지가 있음
이때 프로젝트 이름이 담긴 폴더가 프로젝트 루트라고 함. 게임 개발에 필요한 모든 리소스와 설정 파일이 담겨 있음
Config 폴더
.ini 파일을 통해 에디터와 게임의 초기 상태를 지정
DefaultEditor.ini : 에디터 환경 설정 (뷰포트, UI 등)
DefaultEngine.ini : 엔진 전반 설정 (렌더링, 네트워킹 등)
DefaultGame.ini : 게임플레이 관련 설정 (게임 모드, 플레이어 컨트롤러 클래
스 등)
DefaultInput.ini : 키보드, 마우스, 패드 등의 기본 입력
Source 폴더
실제 C++ 소스 코드( .cpp , .h ).
프로젝트 최초 생성 시에는 프로젝트명.cpp , 프로젝트명.h 등만 있지만,
새 클래스를 만들수록 점차 늘어남
빌드 설정 관련 주요 파일도 포함
프로젝트명.Build.cs : 해당 프로젝트에 필요한 모듈, 라이브러리, 종속성 등 빌드 설정 파일
프로젝트명.Target.cs , 프로젝트명Editor.Target.cs : 각각 게임 실행용, 에디터용 빌드 방식

파일 수정 -> 빌드 (컴파일 + 링크) -> DLL(동적 라이브러리) 파일 생성 -> 언리얼 에디터에서 가져감
이 과정을 계속 반복
빌드 설정 방법
빌드 구성(Configuration)과 플랫폼(Platform)에 Development Editor& Win64를 설정
빌드 방법
전체 빌드보다는 부분 빌드 추천 -> 빌드 시간 줄이기
TestProject2를 시작 프로제트로 설정하기
라이브 코딩으로 에디터 연결 종료 안해도 결과를 확인할 수 있도록 할 수 있지만 아직 많이 업데이트가 안돼서 원래 빌드하던 방식을 추천(종료 후 빌드 -> 재실행)
<빌드 꼬인 거 해결하는 방법>
C++ 코드를 수정하고 빌드를 완료 하였음에도 불구하고, 언리얼 에디터에서 반영
이 안 되는 경우
리소스 프로젝트를 다운받고 그 안에 있는 매터리얼들을 프로젝트에 옮겨옴(Migrate)
Maps 폴더 생성 후 FIle - New Level - Basic 으로 새 레벨을 생성
-> 그리고 Edit - Project Settings - Maps - Editor Startup Map, Game Default Map에 기본 맵을 지정 (이름은 MainLevel)
Object Vs Actor
Object(= UObject)
UObject : 언리얼 엔진에서 모든 클래스의 최상위 부모 클래스
스스로 월드(레벨)에 배치될 수 없고 주로 데이터나 로직만 담당
ex) 플레이어 능력치(HP, 경험치), AI 정보, 게임 설정 값, 임시 계산 로직 등
주로 화면에 보이지 않는 데이터나 로직
Actor (= AActor)
AActor : UObject를 상속한 클래스, 월드에 배치(Spawn) 가능
위치, 회전, 크기 등 공간적 정보 포함됨/ 여러 컴포넌트를 추가로 붙일 수 있음
ex) 실제 게임 세계에서 보이고 상호작용하는 캐릭터, 적 몬스터, 무기, 조명, 파티클 효과 등
Actor 클래스 생성
1. Tools - New C++ Class - Actor - Public - 생성 (클래스 이름은 Item)
2. 자동 빌드가 진행되는데 빌드가 완료되면 비주얼 스튜디오에서 프로젝트가
변경되었다는 메시지가 나타나면, Reload All 선택
3. Public 폴더 아래에 Item.h , Private 폴더 아래에 Item.cpp 파일이 생성
배치는 드래그 앤 드롭으로 할 수 있음 -> Outliner로 확인
Actor 클래스 삭제
1. 언리얼 에디터 종료하고 비주얼 스튜디오에서 클래스 제거(h 와 .cpp 파일)
2. 프로젝트 폴더에서 .h , .cpp 파일을 직접 삭제
💡 항상 에디터 종료 → 솔루션 Remove → 물리 파일 삭제 → (Visual Studio 빌드) → 에디터 재실행 순서 지키기!!!
#pragma once //동일 헤더가 프로젝트 곳곳에서 여러번 포함되더라도 컴파일러가 한 번만 처리
#include "CoreMinimal.h" //필수 기본 헤더
#include "GameFramework/Actor.h" //AActor 클래스 선언을 사용 가능하게 함
#include "Item.generated.h" //언리얼 헤더툴 (UHT)이 자동 생성하는 코드를 포함하는 것으로, 항상 헤더의 마지막 줄에 위치
UCLASS() //클래스를 리플렉션 시스템에 등록하는 매크로
class SPARTAPROJECT_API AItem : public AActor //AActor 를 상속받아 AItem 클래스를 정의한다는 의미
{
GENERATED_BODY() //클래스를 리플렉션 시스템에 등록하는 매크로
public:
// 생성자
AItem();
protected:
// 액터가 월드에 스폰 (배치된) 직후 한 번만 호출
virtual void BeginPlay() override;
// 매 프레임마다 호출
virtual void Tick(float DeltaTime) override;
};
#include "Item.h" //항상 첫 줄에! 짝 헤더를 가장 먼저 include
// 생성자 구현부
AItem::AItem()
{
PrimaryActorTick.bCanEverTick = true;
}
// BeginPlay() 구현부
void AItem::BeginPlay()
{
// 부모 클래스(AActor)의 BeginPlay()를 먼저 호출
Super::BeginPlay();
}
// Tick() 구현부
void AItem::Tick(float DeltaTime)
{
// 부모 클래스(AActor)의 Tick() 먼저 호출
Super::Tick(DeltaTime);
}
#pragma once
#include "CoreMinimal.h"
#include "GameFramework/Actor.h"
#include "Item.generated.h"
UCLASS()
class SPARTAPROJECT_API AItem : public AActor
{
GENERATED_BODY()
public:
AItem();
protected:
// 루트 컴포넌트를 나타내는 Scene Component 포인터
USceneComponent* SceneRoot;
// Static Mesh Component 포인터
UStaticMeshComponent* StaticMeshComp;
};
#include "Item.h"
AItem::AItem()
{
// Scene Component를 생성하고 루트로 설정
SceneRoot = CreateDefaultSubobject<USceneComponent>(TEXT("SceneRoot"));
SetRootComponent(SceneRoot); //루트 컴포넌트를 SceneRoot로 설정
// Static Mesh Component를 생성하고 Scene Component에 Attach
StaticMeshComp = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("StaticMesh"));
StaticMeshComp->SetupAttachment(SceneRoot); //StaticMeshComp 를 SceneRoot에 부착
}
⭐ 필요한 함수(BeginPlay, Tick)가 없거나 미사용 상태라면, 헤더와 .cpp 파일에서 제거해도 됨
여기서 사용된 함수
//컴포넌트를 생성하고 초기화할 때 사용하는 함수
//템플릿 타입 <T> 로 생성할 컴포넌트의 유형을 지정
//"StaticMesh" , "SceneRoot" 는 각 컴포넌트의 식별 이름
//TEXT()는 문자열을 유니코드로 처리하기 위한 것
CreateDefaultSubobject<T>(TEXT(""))
Static Mesh와 Material이 있어야 배치했을 때 시각적으로 확인 가능!!
컨텐트 브라우저에서 원하는 Static Mesh와 Material에 우클릭을 하고 copy Reference를 해서 붙여넣기 -> 주소만 남겨놓고 다 삭제
#include "Item.h"
AItem::AItem()
{
SceneRoot = CreateDefaultSubobject<USceneComponent>(TEXT("SceneRoot"));
SetRootComponent(SceneRoot);
StaticMeshComp = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("StaticMesh"));
StaticMeshComp->SetupAttachment(SceneRoot);
//'/Game/Resources/Props/SM_Chair.SM_Chair'
//'/Game/Resources/Materials/M_Metal_Gold.M_Metal_Gold'
//⭐ Static Mesh를 코드에서 설정
static ConstructorHelpers::FObjectFinder<UStaticMesh> MeshAsset(TEXT("/Game/Resources/Props/SM_Chair.SM_Chair"));
if (MeshAsset.Succeeded()) {
StaticMeshComp->SetStaticMesh(MeshAsset.Object);
}
//⭐ Material을 코드에서 설정
static ConstructorHelpers::FObjectFinder<UMaterial> MaterialAsset(TEXT("/Game/Resources/Materials/M_Metal_Gold.M_Metal_Gold"));
if (MaterialAsset.Succeeded()) {
StaticMeshComp->SetMaterial(0, MaterialAsset.Object);
}
}
여기에 쓰인 함수들
//특정 리소스를 경로 기반으로 로드하는 클래스
ConstructorHelpers::FObjectFinder<T>
//리소스의 경로
TEXT("/Game/Resources/Props/SM_Chair.SM_Chair")
//지정된 경로에서 리소스를 성공적으로 찾았는지 확인
.Succeeded()
//성공적으로 로드된 Static Mesh를 StaticMeshComp에 설정
//여기서 Material은 인덱스를 꼭 붙여줘야함/ 첫번째 머티리얼 슬롯 (Index 0)
SetStaticMesh() , SetMaterial()
protected:
USceneComponent* SceneRoot;
UStaticMeshComponent* StaticMeshComp;
// BeginPlay 함수를 다시 선언
virtual void BeginPlay() override;
};
void AItem::BeginPlay()
{
Super::BeginPlay();
// BeginPlay 호출 시점을 로그로 확인
UE_LOG(LogTemp, Warning, TEXT("My Log!"));
}

로그 카테고리를 Logtemp라고 지정하고 문구를 출력하라고 했을 때 실행하면 Output Log에 지정한 출력 문구가 뜨는데 filter에서 Logtemp만 뜨게 할 수 있음 -> 만약 Logtemp가 없다면 출력 문구가 안 떴다고 의심해야함
-> 고유한 카테고리 만들어서 디버깅 됐는지 확인 용도로 사용하면 좋음
로그 수준(Log Level):
Display : 일반적인 실행 흐름이나 상태 확인 메시지 (흰색)
Warning : 예상치 못한 동작이나 잠재적인 문제 (노란색)
Error : 즉시 수정이 필요한 심각한 문제 (빨간색)
Item.h 상단(inclue 바로 밑)에 "LogSparta"로 로그 카테고리 선언
DECLARE_LOG_CATEGORY_EXTERN(LogSparta, Warning, All);
DEFINE_LOG_CATEGORY(LogSparta);
💡로그 카테고리를 다른 파일에서 쓰려면 선언한 헤더파일을 include해야함.
💡로그 카테고리를 여럿이 공유하기 때문에, 별도의 공용 헤더에 선언해 두는 경우가 많음
Actor 라이프 사이클 : Actor가 게임 도중에 언제든지 생성(Spawn)되고, 필요 없어지면 파괴(Destroy)되는 과정
Actor 라이프 사이클을 이해하면 초기화 시점을 보다 쉽게 결정할 수 있고, 성능을 최적화하거나(Tick 함수), 리소스 정리를 잘 할 수 있음
순서 : 생성 → 초기화 → 월드 배치 → Tick(실행) → 제거
// 생성자 - 메모리에 생김. 딱 한번 호출. -> 컴포넌트를 생성해서 붙여주는 작업
// PostInitializeComponents() - 컴포넌트가 완성된 직후 호출. 컴포넌트끼리 데이터 주고 받기, 상호작용 -> 월드에 등장하기 전에 작업해줘야 할 것들
// BeginPlay() - 배치 직후
// Tick(float DeltaTime) - 매 프레임마다 호출됨. -> 캐릭터 이동 등 자연스러운 움직임들, 열심히 활동하는 함수
// Destroyed() - 삭제가 되기 직전에 호출 -> 리소스 정리
// EndPlay() - 게임 종료, 파괴 (Destroyed()), 레벨 전환 등등
protected:
USceneComponent* SceneRoot;
UStaticMeshComponent* StaticMeshComp;
virtual void PostInitializeComponents() override;
virtual void BeginPlay() override;
virtual void Tick(float DeltaTime) override;
virtual void Destroyed() override;
virtual void EndPlay(const EEndPlayReason:: Type EndPlayReason) override;
AItem::AItem()
{
UE_LOG(LogSparta, Warning, TEXT("%s Constructor"), *GetName());
}
void AItem::PostInitializeComponents() {
Super::PostInitializeComponents();
UE_LOG(LogSparta, Warning, TEXT("%s PostInitializeComponents"), *GetName());
}
void AItem::BeginPlay() {
Super::BeginPlay();
UE_LOG(LogSparta, Warning, TEXT("%s BeginPlay"), *GetName());
}
void AItem::Tick(float DeltaTime) {
Super::Tick(DeltaTime);
}
void AItem::Destroyed() {
UE_LOG(LogSparta, Warning, TEXT("%s Destroyed"), *GetName());
Super::Destroyed();
}
void AItem::EndPlay(const EEndPlayReason::Type EndPlayReason) {
UE_LOG(LogSparta, Warning, TEXT("%s EndPlay"), *GetName());
Super::EndPlay(EndPlayReason);
}
Class Type 옵션에 대한 추가 설명
EndPlay vs. Destroyed
EndPlay: 액터가 월드에서 사라지는 모든 상황(게임 종료, 레벨 전환, Destroy 호출 등)에 대해 호출
Destroyed: 보통 Destroy() 함수가 명시적으로 불렸을 때만 호출, 게임 종료나 레벨 언로드 시에는 호출되지 않을 수 있음