핵심 정리
private Rigidbody body;
private void Awake()
{
body = GetComponent<Rigidbody>();
}
private void FixedUpdate()
{
body.AddForce(Vector3.forward * 4f);
}구조 이해
Awake에서 캐싱하는 이유
같은 오브젝트에 붙은 컴포넌트를 참조할 때는 보통 Awake에서 한 번 가져와 필드에 저장합니다. Update나 FixedUpdate 안에서 매 프레임 GetComponent를 반복 호출하면 검색 비용이 매 프레임 발생하고, 코드 의도도 모호해집니다. Awake에서 미리 캐싱해 두면 읽기도 쉽고 의도도 분명합니다. RequireComponent 어트리뷰트를 함께 사용하면 컴포넌트가 없을 때 컴파일 타임이 아닌 Inspector에서 누락을 미리 방지할 수 있습니다.
외부 오브젝트 참조는 SerializeField로
다른 오브젝트의 컴포넌트를 참조해야 할 때는 GetComponent보다 [SerializeField]로 Inspector에서 직접 연결하는 방식이 더 안전한 경우가 많습니다. 런타임에 Find 계열 메서드로 참조를 탐색하면 오브젝트 이름이나 태그에 의존하게 되어 실수 가능성이 높아집니다. 아래처럼 Inspector에서 미리 연결해 두면 의도가 명확하고 변경에도 강합니다.
[SerializeField] private Transform target;
private void LateUpdate()
{
transform.LookAt(target);
}자식 오브젝트와 부모 탐색
같은 오브젝트가 아닌 자식 오브젝트의 컴포넌트가 필요하다면 GetComponentInChildren을 쓸 수 있습니다. 계층 구조가 깊거나 자식이 많을 경우 탐색 비용이 있으므로, 이 경우에도 Awake에서 한 번 가져와 캐싱하는 편이 좋습니다. 반대로 부모 방향으로 탐색이 필요하면 GetComponentInParent를 사용합니다.
체크포인트
| 상황 | 추천 패턴 |
|---|---|
| 같은 오브젝트 컴포넌트 | Awake에서 GetComponent |
| 자식 오브젝트 컴포넌트 | GetComponentInChildren |
| 외부 참조 연결 | SerializeField |
| 질문 | 더 자주 맞는 선택 | 이유 |
|---|---|---|
| 같은 오브젝트 의존성인가 | GetComponent 후 캐싱 | 코드와 프리팹 구조가 같이 보임 |
| 다른 오브젝트를 디자이너가 연결해야 하는가 | [SerializeField] | 씬/프리팹에서 의도를 바로 확인 가능 |
| 자식 계층 탐색이 필요한가 | GetComponentInChildren 후 캐싱 | 탐색 비용과 의도를 한 번에 정리 가능 |
| 필수 컴포넌트 누락을 막아야 하는가 | RequireComponent + 캐싱 | 런타임 null보다 앞에서 막기 쉬움 |
주의할 점
GetComponent를 매 프레임 호출하는 것보다 더 흔한 문제는, 외부 참조까지 전부 GetComponent나 Find로 해결하려는 습관입니다. 같은 오브젝트 의존성과 외부 연결을 구분하세요.
// ❌ 외부 참조까지 런타임 탐색에 의존
private void Start()
{
target = GameObject.Find("Boss").GetComponent<Transform>();
}
// 이름 변경이나 비활성 상태에 취약
// ✅ 외부 참조는 인스펙터 연결
[SerializeField] private Transform target;
// ✅ 같은 오브젝트 의존성만 GetComponent로 캐싱
private Rigidbody body;
private void Awake() => body = GetComponent<Rigidbody>();GetComponent 결과가 없을 수도 있으니, 필수 컴포넌트라면 RequireComponent를 같이 고려하면 실수를 줄일 수 있습니다.
참고 링크
2 sources