📍 2025. 07. 08 수업
C++은 오래된 언어라 기본 데이터 타입의 크기가 환경마다 달라질 수 있는 단점이 있다.
그래서 언리얼 엔진에서는 int32, uint8 처럼 크기가 명확한 고정 타입을 만들어 사용한다.
이렇게 하면 메모리 최적화와 디버깅 효율이 높아지고, 불필요한 메모리 낭비나 형 변환도 줄일 수 있다.
또한 타입을 통일하면 형변환으로 인한 예상치 못한 버그를 줄이고, 성능도 일관되게 안정적으로 유지된다.
| 타입 이름 | 크기 (비트) | 크기 (바이트) | 부호 여부 | 값 범위 예시 |
|---|---|---|---|---|
int8 | 8 | 1 | signed | -128 ~ 127 |
uint8 | 8 | 1 | unsigned | 0 ~ 255 |
int16 | 16 | 2 | signed | -32,768 ~ 32,767 |
uint16 | 16 | 2 | unsigned | 0 ~ 65,535 |
int32 | 32 | 4 | signed | -2,147,483,648 ~ 2,147,483,647 |
uint32 | 32 | 4 | unsigned | 0 ~ 4,294,967,295 |
int64 | 64 | 8 | signed | 매우 큼 (~±9경 수준) |
uint64 | 64 | 8 | unsigned | 매우 큼 (~18경 수준) |
int8, uint8 은 8 bit = 1 byte = charint32 = intint 단위랑 같음 (4 byte)int8 uint8 int8 HealthChange = -10; // 가능
uint8 MaxHealth = 255; // 가능
uint8 Damage = -10; // ❌ 음수라서 오류
uint8bool도 사용하지만 주로 uint8 로 사용한다.
→ 효율적이게 1 bit 만 사용할 수 있다.
// 이렇게 쓰면 안 됨!!
struct BadExample
{
bool bIsActive; // 1 byte
int32 Score; // 4 byte
};
// 메모리 배치하면
[bool][padding][padding][padding][int32]
1B 1B 1B 1B 4B
// → 총 8바이트나 차지함
// 3 byte 메모리 낭비 발생
// 헤더파일에선 uint8 쓰자
struct GoodExample
{
uint8 bIsActive; // 1 byte (= bool 역할)
int32 Score; // 4 byte
};
uint8 bSomething : 1; // 1 bit만 사용할 거야
💡 bool 변수임을 알려주기 위해 변수명 앞에 b 붙이기
| 구분 | 사용 위치 | 사용 타입 | 이유 |
|---|---|---|---|
| 메모리 절약 | 헤더 파일 (클래스 선언부) | uint8 | 구조체/클래스의 데이터 레이아웃을 최적화하기 위해 |
| 가독성 & 기능 | 소스 파일 (.cpp 내부 로직) | bool | 실제 로직 처리 시 가독성 좋고 bool 변수라는 의미 전달 명확해서 사용 |
이미 국제 표준으로 4 byte, 8 byte 정해져 있기 때문에 신경쓰지 않는다.
비교를 위해 정리하자면,
C++의 문자열 관련 데이터 타입은 char, char*, std::string 이 있다.
char는 한 글자를 저장할 때 사용char* 는 C 스타일 문자열을 가리키는 포인터std::string 은 문자열을 다루기 위한 C++ 표준 클래스오브젝트 이름, 애셋 경로, 본 이름 등에 쓰여 식별자 역할을 한다.
지역화를 지원하는 화면에 출력될 텍스트 전용 타입 (다국어 지원)
UI 만들 때 FText로 미리 만들어 두기
대소문자 변환, 부분 문자열 파싱, 역순 처리 등 문자열을 쉽게 바꿀 수 있도록 메서드(함수)가 이미 구현되어 있다. 이 메서드들은 엑셀에서 사용하는 함수랑 비슷한 것 같다.
파싱 문자열을 추출하거나 잘라내거나 수정함
// 1. 대소문자 변환
FString Text = TEXT("Unreal Engine");
FString Upper = Text.ToUpper(); // "UNREAL ENGINE"
FString Lower = Text.ToLower(); // "unreal engine"
// 2. 파싱
FString Text = TEXT("Player_001");
// 2-1. 부분 문자열 추출 (Mid: 중간 잘라내기)
FString ID = Text.Mid(7); // "001"
// 2-2. 치환 (Replace)
FString Changed = Text.Replace(TEXT("Player"), TEXT("Enemy")); // "Enemy_001"
// 2-3. 앞뒤 공백 제거 (Trim)
FString Spaced = TEXT(" Hello World ");
FString Trimmed = Spaced.TrimStartAndEnd(); // "Hello World"
// 2-4. 문자열 붙이기 (Append)
FString Name = TEXT("Stage");
Name.Append(TEXT("_Boss")); // "Stage_Boss"
// 2-5. 문자열 나누기 (Parse into parts)
FString Original = TEXT("Sword/Legendary");
FString Left, Right;
Original.Split(TEXT("/"), &Left, &Right); // Left = "Sword", Right = "Legendary"
// 3. 역순 처리
FString Text = TEXT("Hello");
Text.ReverseString(); // Text == "olleH"

FString Message = TEXT("게임 시작");
int32 Score = 120;
UE_LOG(LogTemp, Warning, TEXT("메시지: %s"), *Message); // * 역참조 연산자 꼭 쓰기
UE_LOG(LogTemp, Log, TEXT("점수: %d"), Score); // int는 역참조 필요 없음
현지화 안 할 거 확실하면 UI에 FString 써도 되지만 다국어 지원 예정이면 무조건 FText 쓰기
TEXT("문자열") 은 문자열 리터럴을 현재 빌드 설정에 맞게 자동 변환해주는 매크로TEXT("") 로 문자열을 감싸야 안전함| 타입 | 크기 | 특징 | 다국어 지원 |
|---|---|---|---|
char | 1바이트 (8bit) | ASCII 전용, 한글 불가 | ❌ |
wchar_t | 보통 2바이트 (16bit) ※ 플랫폼 따라 4바이트 | 유니코드 기반, 한글/이모지 가능 | ✅ |
TCHAR | 빌드 설정에 따라 char 또는 wchar_t로 결정 | 언리얼 전용 문자 타입 | ✅ |
빌드 설정에 따라 char 쓸 지 wchar_t 쓸 지 알아서 잘 함
| 빌드 설정 | TCHAR 실체 | 설명 |
|---|---|---|
| 멀티바이트(MBCS) | char | 영어 위주, ASCII 전용 |
| 유니코드(UNICODE) | wchar_t | 기본 설정, 다국어 지원 |
| 항목 | FName | FText | FString |
|---|---|---|---|
| 용도 | 내부 식별자, 빠른 비교용 | UI 출력, 다국어 지원 | 일반 문자열 처리, 로직용 |
| 비교 속도 | 아주 빠름 (숫자 인덱스 비교) | 느림 (로컬라이즈 정보 포함) | 보통 (문자열 직접 비교) |
| 메모리 | 가장 가벼움 | 가장 무거움 | 보통 |
| 지역화 | 지원 안 함 | 지원 (다국어 전환 가능) | 지원 안 함 |
| 사용 예시 | 태그, 애님 섹션, 키 이름 | 버튼 텍스트, 대사, 설명 등 | 파일 경로, 이름 조합, 임시 문자열 |
| 에디터 노출 | 보기 힘듦 (Raw 이름) | 사용자 친화적 표시 + 번역 지원 | 일반 문자열로 표시 |
앞에 있는 T 는 템플릿의 약자
템플릿 어떤 자료형이 들어오든 내부 로직은 하나의 코드로 공통 처리할 수 있게 한다.
T를 사용하면 TArray<int32> 든 TArray<FVector> 든 동일한 방식으로 배열 로직을 수행할 수 있다.
std::vector와 유사[0], [1] 등)장점
캐시 효율 CPU가 메모리에서 데이터를 빠르게 읽어올 수 있는 정도단점
Reserve() 로 미리 공간 확보하기Numbers.Reserve(100); // 미리 메모리 확보해서 재할당 줄임| 용어 | 설명 |
|---|---|
| 캐시 지역성 (Cache Locality) | 데이터가 메모리에 어떻게 배치돼 있느냐에 따라 캐시 효율이 달라지는 개념 |
| 시간 지역성 (Temporal Locality) | 최근에 사용한 데이터는 곧 또 사용될 가능성이 높음 |
| 공간 지역성 (Spatial Locality) | 현재 접근한 데이터 주변에 있는 데이터도 같이 사용될 가능성이 높음 |
| 캐시 효율 (Cache Efficiency) | CPU가 메모리에서 데이터를 가져올 때 캐시에서 성공적으로 가져온 비율 |
| 캐시 히트 (Cache Hit) | CPU가 찾는 데이터가 이미 캐시 안에 있어서 바로 사용 가능한 경우 |
| 캐시 미스 (Cache Miss) | CPU가 찾는 데이터가 캐시에 없어서 RAM이나 디스크까지 접근해야 하는 경우 |
김치볶음밥을 예시로 캐시 이해하기
- 냉장고에 김치 있음 → 바로 꺼냄 = 캐시 히트
- 김치 없어서 마트 감 = 캐시 미스
// 1. Add - 요소 추가
TArray<int32> Scores;
Scores.Add(100);
Scores.Add(200);
// 2. AddUnique - 중복 없이 추가
Scores.AddUnique(100); // 이미 100 있음 → 추가 안 됨
// 3. Insert - 특정 위치에 삽입
Scores.Insert(150, 1); // [100, 150, 200]
// 4. Remove - 해당 값 삭제
Scores.Remove(100); // [150, 200]
// 5. RemoveAt - 인덱스로 삭제
Scores.RemoveAt(0); // [200]
// 6. Empty - 배열 전체 비우기
Scores.Empty(); // []
// 7. Reset - 메모리는 유지하고 내용만 제거
Scores.Reset(); // []
// 8. Append - 여러 값 한 번에 추가
Scores.Append({ 10, 20, 30 }); // [10, 20, 30]
// 9. Num - 요소 개수 확인
int32 Count = Scores.Num(); // 3
// 10. Contains - 특정 값 포함 여부 확인
if (Scores.Contains(20))
{
// 20이 배열에 있음
}
// 11. Find - 값의 인덱스 찾기
int32 Index = Scores.Find(30); // Index = 2
// 12. IsValidIndex - 유효한 인덱스인지 확인
if (Scores.IsValidIndex(1))
{
int32 Value = Scores[1]; // 안전한 접근
}
해시 테이블 기반 (STL의 unordered_map처럼 동작)
- unordered_map
key-value 쌍을 저장하고 key를 기준으로 빠르게 검색할 수 있다.
- 해시 테이블
key를 숫자(해시값)로 바꿔서 바로 해당 위치에 빠르게 저장하거나 찾는 방식
unordered_map<string, int> Score;
Score["Alice"] = 100;
캐릭터 이름 → 체력, 아이템 이름 → 개수// 1. TMap 선언 - Player 이름(Key)과 점수(Value) 저장
TMap<FString, int32> PlayerScores;
// 2. Add - Key-Value 쌍 추가
PlayerScores.Add(TEXT("Alice"), 100);
PlayerScores.Add(TEXT("Bob"), 200);
PlayerScores.Add(TEXT("Charlie"), 150);
// 3. 동일한 Key로 Add하면 기존 값이 덮어써짐
PlayerScores.Add(TEXT("Bob"), 300); // 기존 Bob 점수 200 → 300으로 변경됨
// 4. Contains - Key 존재 여부 확인
if (PlayerScores.Contains(TEXT("Alice")))
{
// 5. [] 연산자 - Key로 값에 접근
int32 AliceScore = PlayerScores[TEXT("Alice")];
UE_LOG(LogTemp, Warning, TEXT("Alice's Score: %d"), AliceScore);
}
// ⚠️ [주의] 없는 Key로 값을 쓰면 자동으로 추가됨
// 예: PlayerScores[TEXT("David")] = 0; // 새로운 키가 생김 → 의도치 않은 데이터 생성될 수 있음
// 6. 전체 순회 - Key, Value 모두 접근
for (const TPair<FString, int32>& Elem : PlayerScores) // const는 읽기 전용
{
UE_LOG(LogTemp, Warning, TEXT("%s's Score: %d"), *Elem.Key, Elem.Value);
}
// 7. Remove - Key로 값 제거
PlayerScores.Remove(TEXT("Charlie"));
// 8. Find - Key로 안전하게 값 조회 (포인터 반환)
int32* FoundScore = PlayerScores.Find(TEXT("Bob"));
if (FoundScore != nullptr)
{
UE_LOG(LogTemp, Warning, TEXT("Bob's Score (Find): %d"), *FoundScore);
}
| 항목 | 5. [] 연산자 (Map[Key]) | 8. Find() 함수 |
|---|---|---|
| Key 존재 여부 | 확인 안 함 → 바로 접근 또는 추가 | 먼저 Key가 있는지 확인함 |
| Key 없을 때 | 자동으로 새 Key 추가 + 기본값 할당됨 (0) | nullptr 반환 → 직접 처리해야 함 |
| 사용 목적 | 값 수정/추가할 때 많이 씀 | 값이 있는지 확인만 하고 싶을 때 유용 |
| 안전성 | 조심 안 하면 불필요한 데이터 생길 수 있음 | 안전하게 접근 가능 |
unordered_set 와 유사)Contains()로 빠른 포함 여부 확인// 1. TSet 선언
TSet<int32> MySet;
// 2. Add - 값 추가
MySet.Add(100);
MySet.Add(200);
// 3. Contains - 값 존재 여부 확인
if (MySet.Contains(100))
{
UE_LOG(LogTemp, Warning, TEXT("100이 존재합니다"));
}
// 4. Remove - 값 제거
MySet.Remove(200);
// 5. Num - 전체 개수 확인
int32 Count = MySet.Num(); // Count == 1
// 6. Empty - 전체 비우기
MySet.Empty();
| 항목 | STL (C++) | UCL (언리얼) |
|---|---|---|
| 설계 목적 | 범용 C++ 코드용 | 언리얼 게임 개발용 최적화 |
| 가비지 컬렉션 | ❌ 없음 | ✅ UObject 연동됨 |
| 리플렉션 | ❌ 없음 | ✅ USTRUCT, UCLASS와 연동 |
| 사용 방식 | 표준 C++ 문법 | 언리얼 전용 매크로 및 문법 |
| 메모리 관리 | new/delete 기반 | 엔진 전용 메모리 관리 시스템 사용 |
⚠️ 이름은 비슷하지만 실제 동작 방식/구조는 다르다!
| 컨테이너 | STL 구조 | 언리얼 구조 |
|---|---|---|
vector ↔ TArray | ✅ 동적 배열 (비슷함) | ✅ 동적 배열 (GC/리플렉션 지원 포함) |
set ↔ TSet | 🔴 레드블랙 트리 기반 (정렬됨) | 🟣 해시 테이블 기반 (정렬 안 됨, 빠른 검색) |
map ↔ TMap | 🔴 레드블랙 트리 기반 (Key 정렬됨) | 🟣 해시 테이블 기반 (Key 정렬 없음, 빠른 접근) |
vector ↔ TArray : 거의 비슷 (동적 배열)set ≠ TSet : 이름은 같지만 트리 vs 해시 구조map ≠ TMap : 정렬 가능한 트리 vs 빠른 검색용 해시어떤 연산(추가, 검색 등)이 데이터 크기(n)에 따라 얼마나 오래 걸리는지를 수학적으로 나타냄
📎 Learn Big O notation in 6 minutes 📈
| 표기법 | 의미 | 예시 알고리즘 / 동작 |
|---|---|---|
| 🟢 초록 계열 – 빠른 알고리즘 | ||
O(1) | 상수 시간 | - 배열 인덱스 접근 - 링크드리스트 앞에 삽입 |
O(log n) | 로그 시간 | - 이진 탐색 (Binary Search) |
| 🟡 노랑 계열 – 중간 속도 | ||
O(n) | 선형 시간 | - 배열 전체 순회 - 링크드리스트 탐색 |
O(n log n) | 준선형 시간 | - 퀵 정렬 - 머지 정렬 - 힙 정렬 |
| 🔴 빨강 계열 – 느린 알고리즘 | ||
O(n²) | 이차 시간 | - 버블 정렬 - 삽입 정렬 - 선택 정렬 |
O(n!) | 팩토리얼 시간 | - 외판원 문제 (TSP) - 모든 경우의 순열 탐색 |
| 컨테이너 | 하는 일 | 시간 복잡도 | 설명 |
|---|---|---|---|
| TArray | 맨 뒤에 추가 | O(1) | 바로 뒤에 넣으니까 빠름 (단, 가득 차면 느려짐 O(n)) |
| 값 찾기 | O(n) | 하나씩 다 비교함 (느림) | |
| 인덱스로 접근 | O(1) | [3], [10] 이런 식으로 바로 접근 가능 (빠름) | |
| TMap | 추가, 삭제, 검색 | 평균 O(1)최악 O(n) | TSet처럼 동작, 대신 Key-Value로 저장함 |
| TSet | 추가, 삭제, 검색 | 평균 O(1)최악 O(n) | 해시 테이블이라 빠름, 근데 충돌 나면 느려질 수도 있음 |
// 예시
TMap<FString, int32> MyMap;
MyMap.Add("Alice", 100);
MyMap.Add("Bob", 80);
MyMap.Add("Eve", 70);
MyMap["Alice"] 찾는 건 보통 한 번에 감 → O(1)최악 O(n)📎 FName
📎 FText
📎 FString
📎 TArray: 언리얼 엔진의 배열
📎 TMap
📎 TSet