[이득우의 언리얼] 10. 언리얼 컨테이너 라이브러리 - TArray, TSet

2026. 2. 7. 15:10·기타/[강의] 이득우의 언리얼 프로그래밍 Part1

개요

이번 강의에서는 Unreal Engine이 자체 제작한 컨테이너 라이브러리(UCL - Unreal Container Library)의 대표적인 자료구조인 TArray와 TSet의 내부 구조와 활용 방법을 학습합니다. 각 컨테이너의 장단점을 파악하여 상황에 맞게 효과적으로 활용하는 방법을 익힙니다.

 

학습 목표

- Unreal 컨테이너 라이브러리 TArrayy와 TSet의 내부 구조 이해

- 두 컨테이너의 장단점 파악 및 적절한 활용 방법 학습

- 디버그 빌드를 통한 메모리 구조 직접 확인

- 알고리즘 라이브러리 활용 방법 습득

 


 

Unreal 컨테이너 라이브러리 vs C++ STL

 


 

TArray - 동적 배열

개념 및 특징

: TArray는 크기를 동적으로 조절할 수 있는 가변 배열 자료구조입니다.

장점
- 메모리 연속 배치로 캐시 효율이 좋음
- 인덱스를 통한 빠른 접근이 가능 O(1)
- 고속 순회 가능
- 끝에 요소 추가 빠름 O(1)

단점
- 중간 삽입/삭제 비용 큼 O(n)
- 검색 느림 O(n)
- 데이터 많을수록 검색/수정 작업 지연

 

사용 시기

- 순차적 데이터 저장

- 인덱스 기반 빠른 접근 필요

- 순회가 빈번한 경우

- 검색/삭제가 적은 경우

 

내부 구조

TArray<int32> 구조

메모리:  [1][2][3][4][5][6][7][8][9][10]
         ↑
    GetData() - 시작 포인터

특징:
- 빈틈 없는 연속 메모리
- 인덱스 기반 O(1) 접근
- 끝에 추가: O(1)
- 중간 삽입/삭제: O(n) - 전체 재배치

 

선언 및 기본 사용

더보기
// 선언
TArray<int32> IntArray;        // 비어있는 배열
TArray<FString> StringArray;   // 문자열 배열
TArray<UObject*> ObjectArray;  // UObject 포인터 배열

// 초기화
TArray<int32> InitializedArray = {1, 2, 3, 4, 5};

// 메모리 미리 할당
TArray<int32> ReservedArray;
ReservedArray.Reserve(100);    // 100개 공간 미리 확보

// 기본값으로 채우기
TArray<int32> FilledArray;
FilledArray.Init(0, 10);       // 0으로 10개 채우기

요소 추가

더보기
// Add - 외부에서 생성 후 복사
TArray<FString> Strings;
FString NewString = TEXT("Hello");
Strings.Add(NewString);        // 복사 발생

// Emplace - 배열 내부에서 직접 생성
Strings.Emplace(TEXT("World")); // 복사 없음, 더 효율적

// Append - 여러 요소 한번에 추가
TArray<int32> Source = {1, 2, 3};
TArray<int32> Target = {4, 5, 6};
Target.Append(Source);         // Target: {4, 5, 6, 1, 2, 3}

요소 접근

더보기
TArray<int32> Numbers = {10, 20, 30, 40, 50};

// 인덱스 연산자
int32 First = Numbers[0];         // 10
Numbers[0] = 100;                 // 수정 가능

// Top (마지막 요소)
int32 Last = Numbers.Top();       // 50

// Last (마지막 요소, Top과 동일)
int32 AlsoLast = Numbers.Last();  // 50

// 범위 체크
if (Numbers.IsValidIndex(10))     // false
{
    // 안전하게 접근
}

// 포인터 접근 (C 스타일)
int32* Data = Numbers.GetData();
for (int32 i = 0; i < Numbers.Num(); ++i)
{
    UE_LOG(LogTemp, Log, TEXT("%d"), Data[i]);
}

순회

더보기
TArray<FString> Names = {TEXT("Alice"), TEXT("Bob"), TEXT("Charlie")};

// Range-based for loop (권장)
for (const FString& Name : Names)
{
    UE_LOG(LogTemp, Log, TEXT("Name: %s"), *Name);
}

// 수정 가능한 순회
for (FString& Name : Names)
{
    Name += TEXT("_Modified");
}

// 인덱스 기반 순회
for (int32 i = 0; i < Names.Num(); ++i)
{
    UE_LOG(LogTemp, Log, TEXT("%d: %s"), i, *Names[i]);
}

// 반복자 사용
for (auto It = Names.CreateConstIterator(); It; ++It)
{
    UE_LOG(LogTemp, Log, TEXT("Iterator: %s"), **It);
}

검색

더보기
TArray<int32> Numbers = {10, 20, 30, 40, 50};

// Contains - 요소 존재 확인
bool bHas30 = Numbers.Contains(30);  // true

// Find - 인덱스 찾기
int32 Index = Numbers.Find(30);      // 2 (인덱스)
if (Index != INDEX_NONE)
{
    UE_LOG(LogTemp, Log, TEXT("Found at index: %d"), Index);
}

// FindByPredicate - 조건으로 검색
auto* Found = Numbers.FindByPredicate([](int32 Val)
{
    return Val > 35;
});
if (Found)
{
    UE_LOG(LogTemp, Log, TEXT("Found: %d"), *Found);  // 40
}

삽입 및 삭제

더보기
TArray<int32> Numbers = {1, 2, 3, 4, 5};

// Insert - 중간 삽입 (비용 큼)
Numbers.Insert(99, 2);  // 인덱스 2에 99 삽입
// 결과: {1, 2, 99, 3, 4, 5}

// Remove - 값으로 제거 (첫 번째만)
Numbers.Remove(99);
// 결과: {1, 2, 3, 4, 5}

// RemoveAt - 인덱스로 제거
Numbers.RemoveAt(0);    // 첫 번째 제거
// 결과: {2, 3, 4, 5}

// RemoveAll - 조건으로 모두 제거
Numbers.RemoveAll([](int32 Val)
{
    return Val % 2 == 0;  // 짝수 모두 제거
});
// 결과: {3, 5}

// Pop - 마지막 제거
int32 Popped = Numbers.Pop();  // 5 반환, 배열에서 제거

// Empty - 모두 제거
Numbers.Empty();

정렬

더보기
TArray<int32> Numbers = {5, 2, 8, 1, 9};

// 기본 정렬 (오름차순)
Numbers.Sort();  // {1, 2, 5, 8, 9}

// 내림차순 정렬
Numbers.Sort([](int32 A, int32 B)
{
    return A > B;
});

// 구조체 정렬
struct FStudent
{
    FString Name;
    int32 Score;
};

TArray<FStudent> Students;
// ... 데이터 추가 ...

Students.Sort([](const FStudent& A, const FStudent& B)
{
    return A.Score > B.Score;  // 점수 높은 순
});

유틸리티 함수

더보기
TArray<int32> Numbers = {1, 2, 3, 4, 5};

// Num - 요소 개수
int32 Count = Numbers.Num();  // 5

// IsEmpty - 비어있는지
bool bEmpty = Numbers.IsEmpty();  // false

// SetNum - 크기 조정
Numbers.SetNum(3);    // {1, 2, 3}
Numbers.SetNum(5, 0); // {1, 2, 3, 0, 0}

// AddUnique - 중복 방지 추가 (검색 발생, 비효율적)
Numbers.AddUnique(2); // 이미 있으므로 추가 안 됨
Numbers.AddUnique(6); // 없으므로 추가됨

// Reset - 메모리 유지하고 비우기
Numbers.Reset();

// Empty - 메모리까지 해제
Numbers.Empty();

// Shrink - 여유 메모리 제거
Numbers.Shrink();

 


 

TSet - 집합

개념 및 특징

: TSet은 중복 없는 데이터 집합을 저장하는 자료구조입니다.

장점
- 빠른 검색 O(1)
- 빠른 삽입 O(1)
- 빠른 삭제 O(1)
- 중복 자동 제거
- 빠른 순회 가능 (동적 배열 기반)

단점
- 순서 보장 안 됨
- 인덱스 접근 비효율적
- 메모리에 빈틈 발생 가능

 

사용 시기

- 중복 제거 필요

- 빠른 검색 필요

- 데이터 존재 여부만 확인

- 순서가 중요하지 않을 때

 

내부 구조

TSet<int32> 구조 (해시 테이블 + 동적 배열)

초기: [1][2][3][4][5][6][7][8][9][10]
Remove(2,4,6,8,10):
      [1][X][3][X][5][X][7][X][9][X]
            ↑ Invalid (빈틈)

Add(2,4,6,8,10):
      [1][10][3][8][5][6][7][4][9][2]
            ↑ 빈틈을 역순으로 채움
            
특징:
- 해시 테이블로 O(1) 검색
- 동적 배열 기반으로 빠른 순회
- 삭제 시 빈틈 발생 (Invalid 상태)
- 추가 시 빈틈 우선 채움 (역순)
- 재구축 없음 (삭제 후)

 

STL set vs Unreal TSet

 

선언 및 기본 사용

더보기
// 선언
TSet<int32> IntSet;
TSet<FString> StringSet;
TSet<UObject*> ObjectSet;

// 초기화 리스트
TSet<int32> InitializedSet = {1, 2, 3, 4, 5};

요소 추가

더보기
TSet<int32> Numbers;

// Add - 추가 (중복 무시)
Numbers.Add(10);  // 추가됨
Numbers.Add(20);  // 추가됨
Numbers.Add(10);  // 이미 있으므로 무시됨

// Emplace - 직접 생성
Numbers.Emplace(30);

// Append - 다른 TSet 병합
TSet<int32> OtherSet = {40, 50};
Numbers.Append(OtherSet);  // 집합 합집합

검색

더보기
TSet<int32> Numbers = {10, 20, 30, 40, 50};

// Contains - 존재 확인 (매우 빠름, O(1))
bool bHas30 = Numbers.Contains(30);  // true

// Find - 포인터 반환
int32* Found = Numbers.Find(30);
if (Found)
{
    UE_LOG(LogTemp, Log, TEXT("Found: %d"), *Found);
}
else
{
    UE_LOG(LogTemp, Log, TEXT("Not found"));
}

// ⚠️ 잘못된 패턴 (비효율적, 2번 검색)
if (Numbers.Contains(30))  // 1번 검색
{
    int32 Value = *Numbers.Find(30);  // 2번 검색
}

// ✅ 올바른 패턴 (1번 검색)
if (int32* Value = Numbers.Find(30))
{
    UE_LOG(LogTemp, Log, TEXT("Value: %d"), *Value);
}

순회

더보기
TSet<FString> Names = {TEXT("Alice"), TEXT("Bob"), TEXT("Charlie")};

// Range-based for loop
for (const FString& Name : Names)
{
    UE_LOG(LogTemp, Log, TEXT("Name: %s"), *Name);
}

// ⚠️ 순서는 보장되지 않음!

// TArray로 변환 후 순회
TArray<FString> NameArray = Names.Array();
for (int32 i = 0; i < NameArray.Num(); ++i)
{
    UE_LOG(LogTemp, Log, TEXT("%d: %s"), i, *NameArray[i]);
}

삭제

더보기
TSet<int32> Numbers = {1, 2, 3, 4, 5};

// Remove - 요소 제거
Numbers.Remove(3);  // O(1)

// 여러 개 제거
Numbers.Remove(1);
Numbers.Remove(2);

// Empty - 모두 제거
Numbers.Empty();

// Reset - 메모리 유지하고 비우기
Numbers.Reset();

 

중요: 삭제 후 메모리에 빈틈(Invalid) 남음, 추가 시 자동으로 채워짐

 

집합 연산

더보기
TSet<int32> SetA = {1, 2, 3, 4, 5};
TSet<int32> SetB = {4, 5, 6, 7, 8};

// 합집합 (Union)
TSet<int32> Union = SetA.Union(SetB);
// {1, 2, 3, 4, 5, 6, 7, 8}

// 교집합 (Intersection)
TSet<int32> Intersect = SetA.Intersect(SetB);
// {4, 5}

// 차집합 (Difference)
TSet<int32> Difference = SetA.Difference(SetB);
// {1, 2, 3}

유틸리티 함수

더보기
TSet<int32> Numbers = {1, 2, 3, 4, 5};

// Num - 요소 개수
int32 Count = Numbers.Num();  // 5

// IsEmpty - 비어있는지
bool bEmpty = Numbers.IsEmpty();  // false

// Shrink - 빈틈 제거
Numbers.Shrink();

 


 

Slack (여유 메모리)

: 요소 추가마다 메모리를 재할당하면 오버헤드가 발생하기 때문에 언리얼 컨테이터 라이브러리에는 Slack이라는 개념이 구현되어 있다. 요소를 추가할 때 요소에 해당하는 메모리만큼 확보하는 것이 아닌 해당 메모리의 여유분을 미리 확보해둔다고 한다. 미리 넉넉하게 할당하여 성능을 향상하는 효과가 있다. 삭제 시에는 메모리를 즉시 해제하지 않는 방법으로 메모리의 여유를 둔다.

TArray<int32> Numbers;
Numbers.Reserve(100);  // 100개 공간 확보
Numbers.Add(1);        // 1개만 사용

// Num(): 1 (실제 요소)
// Max(): 100 (할당된 용량)
// Slack: 99 (여유 공간)

// 여유 공간 제거
Numbers.Shrink();      // Max를 Num에 맞춤

 


 

예제

MyGameInstance.h

더보기
#pragma once

#include "CoreMinimal.h"
#include "Engine/GameInstance.h"
#include "MyGameInstance.generated.h"

UCLASS()
class UNREALCONTAINER_API UMyGameInstance : public UGameInstance
{
    GENERATED_BODY()
    
public:
    virtual void Init() override;
};

MyGameInstance.cpp - TArray

더보기
#include "MyGameInstance.h"
#include "Algo/Accumulate.h"

void UMyGameInstance::Init()
{
    Super::Init();
    
    UE_LOG(LogTemp, Log, TEXT("===== TArray 예제 시작 ====="));
    
    // 1. TArray 생성 및 채우기
    TArray<int32> IntArray;
    for (int32 i = 1; i <= 10; ++i)
    {
        IntArray.Add(i);
    }
    // IntArray: {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
    
    // 2. 짝수 제거
    IntArray.RemoveAll([](int32 Val)
    {
        return Val % 2 == 0;
    });
    // IntArray: {1, 3, 5, 7, 9}
    
    // 3. 짝수 다시 추가
    IntArray += {2, 4, 6, 8, 10};
    // IntArray: {1, 3, 5, 7, 9, 2, 4, 6, 8, 10}
    
    // 4. 메모리 직접 복사로 비교 배열 생성
    TArray<int32> CompareArray;
    CompareArray.AddUninitialized(IntArray.Num());
    FMemory::Memcpy(
        CompareArray.GetData(), 
        IntArray.GetData(), 
        IntArray.Num() * sizeof(int32)
    );
    
    // 5. 동일성 확인
    ensure(IntArray == CompareArray);
    
    // 6. 알고리즘 라이브러리 사용
    int32 Sum = Algo::Accumulate(IntArray, 0);  // 55
    
    int32 ManualSum = 0;
    for (int32 Num : IntArray)
    {
        ManualSum += Num;
    }
    
    ensure(Sum == ManualSum);
    
    UE_LOG(LogTemp, Log, TEXT("배열 합계: %d"), Sum);
    UE_LOG(LogTemp, Log, TEXT("===== TArray 예제 종료 ====="));
}

 

MyGameInstance.cpp - TSet

더보기
void UMyGameInstance::Init()
{
    Super::Init();
    
    UE_LOG(LogTemp, Log, TEXT("===== TSet 예제 시작 ====="));
    
    // 1. TSet 생성 및 채우기
    TSet<int32> IntSet;
    for (int32 i = 1; i <= 10; ++i)
    {
        IntSet.Add(i);
    }
    
    // 2. 짝수 제거 (하나씩)
    IntSet.Remove(2);
    IntSet.Remove(4);
    IntSet.Remove(6);
    IntSet.Remove(8);
    IntSet.Remove(10);
    // IntSet: {1, 3, 5, 7, 9} (내부적으로 빈틈 존재)
    
    // 3. 짝수 다시 추가 (빈틈을 역순으로 채움)
    IntSet.Add(2);
    IntSet.Add(4);
    IntSet.Add(6);
    IntSet.Add(8);
    IntSet.Add(10);
    // IntSet: {1, 10, 3, 8, 5, 6, 7, 4, 9, 2} (순서 보장 안 됨)
    
    // 4. 검색 성능 테스트
    bool bFound = IntSet.Contains(5);  // 매우 빠름 (O(1))
    UE_LOG(LogTemp, Log, TEXT("5 존재: %s"), bFound ? TEXT("예") : TEXT("아니오"));
    
    // 5. TArray로 변환
    TArray<int32> ConvertedArray = IntSet.Array();
    UE_LOG(LogTemp, Log, TEXT("변환된 배열 크기: %d"), ConvertedArray.Num());
    
    UE_LOG(LogTemp, Log, TEXT("===== TSet 예제 종료 ====="));
}

'기타 > [강의] 이득우의 언리얼 프로그래밍 Part1' 카테고리의 다른 글

[이득우의 언리얼] 12. 언리얼 엔진의 메모리 관리  (0) 2026.02.09
[이득우의 언리얼] 11. 언리얼 컨테이너 라이브러리 - 구조체, Map  (0) 2026.02.08
[이득우의 언리얼] 9. 언리얼 C++ 설계 - 델리게이트  (0) 2026.02.06
[이득우의 언리얼] 8. 언리얼의 C++ 설계 - 컴포지션  (0) 2026.02.06
[이득우의 언리얼] 7. 언리얼 C++ 설계 - 인터페이스  (0) 2026.02.04
'기타/[강의] 이득우의 언리얼 프로그래밍 Part1' 카테고리의 다른 글
  • [이득우의 언리얼] 12. 언리얼 엔진의 메모리 관리
  • [이득우의 언리얼] 11. 언리얼 컨테이너 라이브러리 - 구조체, Map
  • [이득우의 언리얼] 9. 언리얼 C++ 설계 - 델리게이트
  • [이득우의 언리얼] 8. 언리얼의 C++ 설계 - 컴포지션
Meoyoung's Development Logs
Meoyoung's Development Logs
내가 보려고 만든 블로그
  • Meoyoung's Development Logs
    이게뭐영
    Meoyoung's Development Logs
  • 전체
    오늘
    어제
    • 분류 전체보기 (229)
      • 게임잼 (3)
      • 언리얼 엔진 (53)
        • 꿀 Tip ! (7)
        • 트러블슈팅 (24)
        • 최적화 (0)
        • 캐릭터 (2)
        • VR (1)
        • Lighting (2)
        • 멀티스레드 (2)
        • 문자열 (0)
      • C++ (31)
        • 문법 정리 (8)
        • [서적] Fundamental C++ 프로그래밍 .. (5)
        • [서적] 이것이 C++이다 (11)
        • [서적] Effective C++ (7)
      • C# (1)
        • [서적] 이것이 C#이다 (1)
      • 코딩테스트 (26)
        • 프로그래머스 (6)
        • 알고리듬 (13)
        • 자료구조 (7)
      • 컴퓨터 과학 (27)
        • 운영체제 (11)
        • 데이터베이스 (0)
        • 디자인패턴 (0)
        • 자료구조 (5)
        • 네트워크 (0)
        • 컴퓨터구조 (11)
      • 면접준비 (0)
        • C++ (0)
        • 운영체제 (0)
        • 자료구조 (0)
      • 프로젝트 (25)
        • [팀프로젝트] The Fourth Descenda.. (5)
        • [개인프로젝트] FPS 구현 맛보기 (5)
        • GetOutOf (15)
      • 기타 (54)
        • [강의] 이득우의 언리얼 프로그래밍 Part1 (10)
        • [강의] 이득우의 언리얼 프로그래밍 Part3 (12)
        • [강의] 소울라이크 개발 A-Z (4)
        • [강의] Udemy-2D (5)
        • [서적] 인생 언리얼5 (4)
        • 스파르타코딩클럽 (15)
        • 객체지향프로그래밍 (2)
        • 컴퓨터회로 (2)
  • 블로그 메뉴

    • 홈
    • 태그
    • 방명록
  • 링크

  • 공지사항

  • 인기 글

  • 태그

    셸정렬
    참가후기
    알고리즘
    게임잼
    삽입정렬
    자료구조
    버블정렬
    게임개발
    경북게임잼
    쉘정렬
    선택정렬
  • 최근 댓글

  • 최근 글

  • hELLO· Designed By정상우.v4.10.3
Meoyoung's Development Logs
[이득우의 언리얼] 10. 언리얼 컨테이너 라이브러리 - TArray, TSet
상단으로

티스토리툴바