본문 바로가기
C#

Vector3 pos = new Vector3() 의 이해와 대안

by Oz Driver 2025. 3. 10.

 

 

유니티에서 Vector3는 struct(구조체)로 정의되어 있기 때문에 값 타입으로 동작합니다.

따라서 new Vector3(); 를 사용할 때의 동작 방식은 일반적인 참조 타입(class)과 다릅니다.

이번 글에서는 Vector3가 스택 메모리에서 어떻게 처리되는지 살펴보겠습니다.

 

new Vector3() 의 의미

Vector3 pos = new Vector3();

 

이 코드는 스택(Stack) 메모리에서 Vector3 공간을 만들고, 기본값(0,0,0)으로 초기화한 후, pos 변수에 복사하는 과정을 포함합니다.

 

제일 먼저 new Vector3(); 가 실행되면서 스택(Stack) 메모리에 Vector3 구조체 공간을 생성하고, 기본값(0,0,0) 으로 초기화 합니다.

그 후 Vector3 pos 변수 선언에 의해 스택의 다른 위치에 pos 변수를 생성합니다.

그리고 대입 연산자 = 를 만나 초기화된 Vector3 값을 pos 변수에 복사하는 과정으로 진행됩니다. 

즉, Vector3 는 값 타입이므로 pos 에 복사될 때 새로운 메모리 공간이 사용됩니다.

그리고 값을 하나하나 복사를 하는데, 이것을 깊은 복사라고 합니다. 

 

 

굳이 new 를 써야 할까?

Vector3 는 구조체(struct) 이기 때문에 기본적으로 스택에 할당되는 값 타입입니다.
그렇다면 굳이 new Vector3() 라는 구문을 사용해야 할까요?
결론부터 말하자면, new 는 힙 할당을 위한 것이 아니라 초기화를 위한 문법입니다.
C# 에서는 값 타입을 사용할 때 반드시 모든 필드를 초기화해야 하며, new 를 쓰면 (0, 0, 0) 같은 기본값으로 자동 초기화해줍니다.

예를 들어 아래와 같이 Vector3 를 선언만 하고 사용하면 컴파일 오류가 발생합니다:

Vector3 pos;
Debug.Log(pos); // 오류 : 초기화되지 않은 변수 사용


반면 new 를 사용하면

Vector3 pos = new Vector3(); // 기본값으로 초기화


또는 모든 필드를 직접 초기화할 수도 있습니다:

Vector3 pos;
pos.x = 1;
pos.y = 2;
pos.z = 3; // 모든 필드가 초기화되면 사용 가능


하지만 new 를 쓰면 단 한 줄로 명확하게 의도를 표현할 수 있어 편리하고 가독성도 좋습니다.

 

 

더 효율적인 대안 : Vector3.zero 

new Vector3() 를 사용하여 기본값 (0, 0, 0) 을 스택에 생성하는 대신, Vector3.zero 를 사용할 수도 있습니다.

// 스택에 임시 저장할 필요 없이 사용
Vector3 pos = Vector3.zero;
 

Vector3.zero 는 Vector3 구조체의 static 멤버로, 기본값 (0, 0, 0) 을 미리 제공합니다.
따라서 불필요한 메모리 할당을 피하고, 성능을 향상시킬 수 있습니다.

Vector3.zero 의 내부 구현은 다음과 같습니다.

public static readonly Vector3 zeroVector = new Vector3(0, 0, 0);

 

Vector3.zero 를 사용하면 메모리 절약과 성능 향상에 유리합니다. 특히 기본값이 필요할 때 새로 할당하지 않아도 되므로 더 효율적입니다.

 

 

값 타입(struct) 과 참조 타입(class) 의 차이

값 타입(struct)

Vector3 같은 struct는 값을 직접 저장 값이 할당될 때 복사가 일어남 (새로운 메모리 공간 생성) 독립적인 메모리 공간을 가지므로, 다른 변수에 영향을 주지 않음

 

참조 타입(class)

GameObject 같은 class는 메모리에 객체를 만들고, 참조(주소)만 저장 다른 변수에 할당될 때 주소만 복사되므로, 같은 객체를 공유 하나의 변수를 변경하면, 동일한 객체를 가리키는 다른 변수도 영향을 받음

 

예제 코드로 비교하기

값 타입(struct) 복사 예제

Vector3 a = new Vector3(1, 2, 3);
Vector3 b = a; // 값이 복사됨 (새로운 메모리 공간 할당)
b.x = 10;

Debug.Log(a.x); // 1 (a는 변경되지 않음)
Debug.Log(b.x); // 10 (b만 변경됨)

 

b = a; 할당 시, 새로운 Vector3가 생성되어 값이 복사됨.

b를 변경해도 a는 영향을 받지 않음.

 

참조 타입(class) 예제

class Player {
    public int health;
}

Player p1 = new Player();
Player p2 = p1; // 주소(참조)가 복사됨 (같은 객체를 가리킴)
p2.health = 50;

Debug.Log(p1.health); // 50 (p1과 p2가 같은 객체를 공유하므로 변경됨)

 

p2 = p1; 할당 시, 주소가 복사되므로 같은 객체를 가리킴.

p2를 변경하면 p1도 영향을 받음.

 

 

마무리

Vector3 같은 struct는 값 타입이므로 new 연산자로 생성된 후, 스택의 변수에 값이 복사됨 복사본이 생성되므로, 원본을 변경해도 새로운 변수에는 영향을 주지 않습니다.

class (참조 타입) 과 다르게, struct 는 값을 직접 저장하며, 참조가 아니라 복사로 동작하게 됩니다.

 

정리하자면,

Vector3 pos = new Vector3(); 는 "스택에 공간을 만들고 값을 초기화한 후, 변수에 복사하는 과정" 입니다.

 

 

첨언하자면 ...

Vector3 를 사용할 때, new 로 할당을 하는 코드를 보면서 c++ 에 익숙한 사람들은 뭔가 불안한 느낌이 들었을 지도 모르겠습니다. ( 제가 그랬거든요. ㅠ  왜냐하면 new 의 무서움을 잘 알고 있기 때문이죠. )

사실 이 글을 쓰게된 이유도 이것 때문이었습니다. 

 

void SomeFunction()
{
    Vector3 position = new Vector3(1f, 2f, 3f); 
    Debug.Log(position); // 여기서는 정상 출력됨
} 

// 여기서 position은 더 이상 존재하지 않음 (스택에서 제거됨)

 

하지만,  Vector3 는 구조체이기 때문에 힙 메모리가 아닌 스택 메모리에 할당되고, 따라서 블럭 { } 이 끝나면 GC ( 가비지 컬렉터 ) 와 무관하게 자동으로 제거됩니다. 

그러니 이제는 막연한 불안감에서 벗어나도 됩니다. !!