Unreal만의 데이터 타입 1 (기본 | 문자열 | 컨테이너)

김여울·2025년 7월 13일
0

내일배움캠프

목록 보기
42/114

📍 2025. 07. 08 수업

Unreal만의 데이터 타입

C++은 오래된 언어라 기본 데이터 타입의 크기가 환경마다 달라질 수 있는 단점이 있다.
그래서 언리얼 엔진에서는 int32, uint8 처럼 크기가 명확한 고정 타입을 만들어 사용한다.
이렇게 하면 메모리 최적화와 디버깅 효율이 높아지고, 불필요한 메모리 낭비나 형 변환도 줄일 수 있다.
또한 타입을 통일하면 형변환으로 인한 예상치 못한 버그를 줄이고, 성능도 일관되게 안정적으로 유지된다.

1. 기본 데이터 타입

1.1 int

타입 이름크기 (비트)크기 (바이트)부호 여부값 범위 예시
int881signed-128 ~ 127
uint881unsigned0 ~ 255
int16162signed-32,768 ~ 32,767
uint16162unsigned0 ~ 65,535
int32324signed-2,147,483,648 ~ 2,147,483,647
uint32324unsigned0 ~ 4,294,967,295
int64648signed매우 큼 (~±9경 수준)
uint64648unsigned매우 큼 (~18경 수준)

a. 뒤에 붙은 숫자는?

  • bit 단위
  • int8, uint8 은 8 bit = 1 byte = char

b. int32 = int

  • 가장 많이 쓰인다
  • C++의 int 단위랑 같음 (4 byte)
    • 4 * 8 = 32

c. u의 유무

  • int8
    • 부호 있는(signed) 8비트 정수 (-128 ~ 127)
  • uint8
    • 부호 없는(unsigned) 8비트 정수 (0 ~ 255)
      → 양수만 저장 가능 (대신 더 넓은 범위의 양수 저장 가능)
int8  HealthChange = -10;   // 가능
uint8 MaxHealth   = 255;    // 가능
uint8 Damage      = -10;    // ❌ 음수라서 오류

1.2 bool

a. bool = uint8

bool도 사용하지만 주로 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
};

b. Bit Field 연산자

uint8 bSomething : 1;	// 1 bit만 사용할 거야

💡 bool 변수임을 알려주기 위해 변수명 앞에 b 붙이기

c. uint8 vs bool 사용 위치와 목적

구분사용 위치사용 타입이유
메모리 절약헤더 파일 (클래스 선언부)uint8구조체/클래스의 데이터 레이아웃을 최적화하기 위해
가독성 & 기능소스 파일 (.cpp 내부 로직)bool실제 로직 처리 시 가독성 좋고 bool 변수라는 의미 전달 명확해서 사용

1.3 float & double

이미 국제 표준으로 4 byte, 8 byte 정해져 있기 때문에 신경쓰지 않는다.


2. 문자열 데이터 타입

비교를 위해 정리하자면,
C++의 문자열 관련 데이터 타입은 char, char*, std::string 이 있다.

  • char는 한 글자를 저장할 때 사용
  • char* 는 C 스타일 문자열을 가리키는 포인터
  • std::string 은 문자열을 다루기 위한 C++ 표준 클래스

2.1 FName

a. 가벼운 문자열 (경량화)

  • 문자열을 숫자로 매핑해서 저장하는 구조체
    → 내부적으로 저장된 인덱스 값을 이용하니까 문자열보다 빠르게 동작하니까 성능도 좋음
  • 동일한 텍스트를 만들어 사용해도 데이터 테이블에 딱 한 번만 저장됨
    → 메모리 절약 가능

b. 글로벌 네임 테이블 (Global Name Table)

  • FName 사용됨
    → 문자열 자체는 전역 네임 테이블이라는 곳에 딱 한 번만 저장됨 (중복 생성 방지)
    → 이후 동일한 문자열이 생기면 그냥 그걸 참조함 (복사 아님!)
    → 이름이 여러 군데 쓰여도 실제 문자열은 메모리에 한 번만 존재

c. 식별자 역할

오브젝트 이름, 애셋 경로, 본 이름 등에 쓰여 식별자 역할을 한다.

2.2 FText

a. 지역화 (Localization)

지역화를 지원하는 화면에 출력될 텍스트 전용 타입 (다국어 지원)

b. UI

UI 만들 때 FText로 미리 만들어 두기

2.3 FString

a. 조작 가능한 유일한 문자열 클래스

대소문자 변환, 부분 문자열 파싱, 역순 처리 등 문자열을 쉽게 바꿀 수 있도록 메서드(함수)가 이미 구현되어 있다. 이 메서드들은 엑셀에서 사용하는 함수랑 비슷한 것 같다.

파싱 문자열을 추출하거나 잘라내거나 수정함

// 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"

b. 내부적으로 TCHAR 배열을 포함하고 있는 클래스

  • FString 자체는 TCHAR로 변환해야 출력 가능
    → 접근하기 위해서는 역참조 연산자 사용해야 함
    → 내부의 문자열 포인터(TCHAR*)를 꺼내주기
FString Message = TEXT("게임 시작");
int32 Score = 120;

UE_LOG(LogTemp, Warning, TEXT("메시지: %s"), *Message); // * 역참조 연산자 꼭 쓰기
UE_LOG(LogTemp, Log, TEXT("점수: %d"), Score); // int는 역참조 필요 없음

c. 현지화 미지원

현지화 안 할 거 확실하면 UI에 FString 써도 되지만 다국어 지원 예정이면 무조건 FText 쓰기

* TEXT("")를 써야 하는 이유

  • C++ 기본 문자열("Hello")은 ASCII 기반 char이라 한글 등 다국어 지원이 불안정함
  • 언리얼은 UTF-16 기반의 TCHAR를 사용해 다국어 및 플랫폼 호환성을 보장함
  • TEXT("문자열") 은 문자열 리터럴을 현재 빌드 설정에 맞게 자동 변환해주는 매크로
    → 그래서 언리얼에서는 항상 TEXT("") 로 문자열을 감싸야 안전함

a. 문자 타입 비교

타입크기특징다국어 지원
char1바이트 (8bit)ASCII 전용, 한글 불가
wchar_t보통 2바이트 (16bit)
※ 플랫폼 따라 4바이트
유니코드 기반, 한글/이모지 가능
TCHAR빌드 설정에 따라 char 또는 wchar_t로 결정언리얼 전용 문자 타입

b. 빌드 설정에 따른 TCHAR 동작

빌드 설정에 따라 char 쓸 지 wchar_t 쓸 지 알아서 잘 함

빌드 설정TCHAR 실체설명
멀티바이트(MBCS)char영어 위주, ASCII 전용
유니코드(UNICODE)wchar_t기본 설정, 다국어 지원

* 문자열 타입 정리

항목FNameFTextFString
용도내부 식별자, 빠른 비교용UI 출력, 다국어 지원일반 문자열 처리, 로직용
비교 속도아주 빠름 (숫자 인덱스 비교)느림 (로컬라이즈 정보 포함)보통 (문자열 직접 비교)
메모리가장 가벼움가장 무거움보통
지역화지원 안 함지원 (다국어 전환 가능)지원 안 함
사용 예시태그, 애님 섹션, 키 이름버튼 텍스트, 대사, 설명 등파일 경로, 이름 조합, 임시 문자열
에디터 노출보기 힘듦 (Raw 이름)사용자 친화적 표시 + 번역 지원일반 문자열로 표시

3. 컨테이너 데이터 타입

앞에 있는 T 는 템플릿의 약자
템플릿 어떤 자료형이 들어오든 내부 로직은 하나의 코드로 공통 처리할 수 있게 한다.
T를 사용하면 TArray<int32>TArray<FVector> 든 동일한 방식으로 배열 로직을 수행할 수 있다.

3.1 TArray

a. 구조 & 저장 방식

  • 가변 배열 (Dynamic Array)
  • STL의 std::vector와 유사
  • 인덱스로 접근 가능 ([0], [1] 등)
  • 연속된 메모리에 저장되어 캐시 효율이 좋음

b. 특징 & 사용 목적

장점

  • 연속된 메모리로 구성돼 캐시 효율이 높음
    캐시 효율 CPU가 메모리에서 데이터를 빠르게 읽어올 수 있는 정도
  • 임의 접근(index) 빠름
  • 순차 순회가 빠르고 성능 좋음
  • 게임에서 중요한 캐시 지역성에 유리함
    TArray나 struct 같은 연속된 메모리
    → 공간 지역성이 좋음
    → 루프나 반복 처리에서 CPU 캐시 히트율이 높아짐 → 빠름!

단점

  • 중간 삽입/삭제가 느림 (뒤 요소들을 이동시켜야 함)
    • 맨 끝 원소 추가는 빠름
  • 배열의 용량 초과 시 재할당 + 복사 비용 발생
    • TArray는 처음에 정해진 용량만큼 메모리를 잡아둠
    • 요소를 계속 Add()하다가 용량을 초과함
      → 더 큰 메모리를 새로 할당
      → 기존 데이터를 복사해서 옮김
      → 성능 비용이 크기 때문에 자주 일어나면 느려짐
    • 밥그릇이 작으면 큰 그릇으로 옮기고, 밥도 다시 옮겨 담아야 한다 → 이게 재할당 + 복사
      💡 많이 추가할 걸 안다면 Reserve() 로 미리 공간 확보하기
      Numbers.Reserve(100);  // 미리 메모리 확보해서 재할당 줄임

c. 캐시 관련 개념 정리

용어설명
캐시 지역성 (Cache Locality)데이터가 메모리에 어떻게 배치돼 있느냐에 따라 캐시 효율이 달라지는 개념
시간 지역성 (Temporal Locality)최근에 사용한 데이터는 곧 또 사용될 가능성이 높음
공간 지역성 (Spatial Locality)현재 접근한 데이터 주변에 있는 데이터도 같이 사용될 가능성이 높음
캐시 효율 (Cache Efficiency)CPU가 메모리에서 데이터를 가져올 때 캐시에서 성공적으로 가져온 비율
캐시 히트 (Cache Hit)CPU가 찾는 데이터가 이미 캐시 안에 있어서 바로 사용 가능한 경우
캐시 미스 (Cache Miss)CPU가 찾는 데이터가 캐시에 없어서 RAM이나 디스크까지 접근해야 하는 경우

김치볶음밥을 예시로 캐시 이해하기

  • 냉장고에 김치 있음 → 바로 꺼냄 = 캐시 히트
  • 김치 없어서 마트 감 = 캐시 미스

d. TArray 메세드

// 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];    // 안전한 접근
}

3.2 TMap

a. 자료구조 형태

해시 테이블 기반 (STL의 unordered_map처럼 동작)

- unordered_map
key-value 쌍을 저장하고 key를 기준으로 빠르게 검색할 수 있다.

- 해시 테이블
key를 숫자(해시값)로 바꿔서 바로 해당 위치에 빠르게 저장하거나 찾는 방식

unordered_map<string, int> Score;
Score["Alice"] = 100;
  • "Alice"라는 키를 해시 함수를 통해 숫자로 변환
  • 그 숫자에 해당하는 메모리 슬롯에 값을 저장
  • 다음에 "Alice"를 검색할 때 다시 같은 숫자 계산해서 바로 접근
  • 일반 리스트나 배열은 값을 찾으려면 순차 검색해야 하지만 해시 테이블은 계산 한 번으로 위치를 바로 찾음

b. 저장 & 구조

  • Key-Value 쌍 저장 (예: 이름 → 점수)
  • Key는 중복 불가, 마지막 값으로 덮어씀
    → Key는 유일해야 함
  • 내부적으로 해시 테이블 기반

c. 특징 & 사용 목적

  • 빠른 검색 가능 (Key를 해시값으로 변환해 바로 접근)
  • 중복 Key 불가, 마지막 값으로 덮어씀
    → Key는 유일해야 함
  • (예) 캐릭터 이름 → 체력, 아이템 이름 → 개수

d. TMap 메세드

// 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 반환 → 직접 처리해야 함
사용 목적값 수정/추가할 때 많이 씀값이 있는지 확인만 하고 싶을 때 유용
안전성조심 안 하면 불필요한 데이터 생길 수 있음안전하게 접근 가능

3.3 TSet

a. 구조 & 저장 방식

  • 해시 테이블 기반 (STL의 unordered_set 와 유사)
  • 값만 저장, Key 없음
  • 중복 불가, 순서 없음

b. 특징 & 사용 목적

  • Contains()로 빠른 포함 여부 확인
  • 중복 없이 값들을 저장하고 싶을 때
  • 특정 값이 있는지 빠르게 확인하고 싶을 때
    (예) 중복 없는 ID 목록, 활성 상태 유닛 모음 등

c. TSet 메서드

// 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();

* C++ STL 컨테이너 vs 언리얼 컨테이너 라이브러리 (UCL)

항목STL (C++)UCL (언리얼)
설계 목적범용 C++ 코드용언리얼 게임 개발용 최적화
가비지 컬렉션❌ 없음UObject 연동됨
리플렉션❌ 없음USTRUCT, UCLASS와 연동
사용 방식표준 C++ 문법언리얼 전용 매크로 및 문법
메모리 관리new/delete 기반엔진 전용 메모리 관리 시스템 사용

⚠️ 이름은 비슷하지만 실제 동작 방식/구조는 다르다!

컨테이너STL 구조언리얼 구조
vectorTArray✅ 동적 배열 (비슷함)✅ 동적 배열 (GC/리플렉션 지원 포함)
setTSet🔴 레드블랙 트리 기반 (정렬됨)🟣 해시 테이블 기반 (정렬 안 됨, 빠른 검색)
mapTMap🔴 레드블랙 트리 기반 (Key 정렬됨)🟣 해시 테이블 기반 (Key 정렬 없음, 빠른 접근)
  • vectorTArray : 거의 비슷 (동적 배열)
  • setTSet : 이름은 같지만 트리 vs 해시 구조
  • mapTMap : 정렬 가능한 트리 vs 빠른 검색용 해시

* 시간 복잡도

a. 시간 복잡도

어떤 연산(추가, 검색 등)이 데이터 크기(n)에 따라 얼마나 오래 걸리는지를 수학적으로 나타냄
📎 Learn Big O notation in 6 minutes 📈

  • x축 : 데이터 개수 n (입력 크기) → 오른쪽으로 갈수록 데이터 많아짐
  • y축 : 처리 시간 (step 수, 얼마나 느려지는지) → 위로 갈수록 속도 느려짐

표기법의미예시 알고리즘 / 동작
🟢 초록 계열 – 빠른 알고리즘
O(1)상수 시간- 배열 인덱스 접근
- 링크드리스트 앞에 삽입
O(log n)로그 시간- 이진 탐색 (Binary Search)
🟡 노랑 계열 – 중간 속도
O(n)선형 시간- 배열 전체 순회
- 링크드리스트 탐색
O(n log n)준선형 시간- 퀵 정렬
- 머지 정렬
- 힙 정렬
🔴 빨강 계열 – 느린 알고리즘
O(n²)이차 시간- 버블 정렬
- 삽입 정렬
- 선택 정렬
O(n!)팩토리얼 시간- 외판원 문제 (TSP)
- 모든 경우의 순열 탐색

b. 언리얼 컨테이너 시간 복잡도

컨테이너하는 일시간 복잡도설명
TArray맨 뒤에 추가O(1)바로 뒤에 넣으니까 빠름 (단, 가득 차면 느려짐 O(n))
값 찾기O(n)하나씩 다 비교함 (느림)
인덱스로 접근O(1)[3], [10] 이런 식으로 바로 접근 가능 (빠름)
TMap추가, 삭제, 검색평균 O(1)
최악 O(n)
TSet처럼 동작, 대신 Key-Value로 저장함
TSet추가, 삭제, 검색평균 O(1)
최악 O(n)
해시 테이블이라 빠름, 근데 충돌 나면 느려질 수도 있음
  • 평균 O(1) : 보통은 빠르게 작동함
  • 최악 O(n) : 아주 드물게 충돌 때문에 느려질 수 있음
// 예시
TMap<FString, int32> MyMap;
MyMap.Add("Alice", 100);
MyMap.Add("Bob", 80);
MyMap.Add("Eve", 70);
  • MyMap["Alice"] 찾는 건 보통 한 번에 감 → O(1)
  • 만약 "Eve"랑 "Bob"이 해시 충돌 나서 같은 곳에 있으면 그 안에서 비교하면서 찾아야 함 → 최악 O(n)

언리얼 공식 문서

📎 FName
📎 FText
📎 FString
📎 TArray: 언리얼 엔진의 배열
📎 TMap
📎 TSet

0개의 댓글