2026-03-23 TIL (20일차)
C++
디버깅 팁
노란색 화살표와 중단점
- 현재 상태: 노란색 줄 및 중단점이 가리키는 지점은 실행되지 않은 상태입니다.
- 실행 범위: 노란색 줄 및 중단점이 위쪽 줄까지는 실행이 완료되어 메모리에 값이 반영된 상태이고, 실행할 다음 명령어를 대기하고 있는 상태입니다.
중단점 관리 단축키
Ctrl+Shift+F9: 현재 프로젝트에 설정된 모든 중단점을 삭제합니다.
Debug vs Release
| 구분 | Debug (디버그) 모드 | Release (릴리즈) 모드 |
|---|---|---|
| 최적화 | 최적화를 거의 하지 않음 | 컴파일러가 강력하게 최적화함 |
| 디버깅 가시성 | 모든 변수와 실행 줄을 정확히 추적 가능 | 실행 줄이 제멋대로 튀거나 변수 값이 안 보임 |
| 코드 처리 | 작성한 코드를 그대로 실행함 | 의미 없는 코드(사용되지 않는 변수 등)를 무시하거나 삭제함 |
Release 모드에서 디버깅이 힘든 이유
릴리즈 모드에서는 실행 속도를 높이기 위해 컴파일러가 코드를 재배치합니다.
이 과정에서 디버깅 시점에 최적화되어 값을 확인할 수 없습니다라는 메시지가 뜨거나, 실행 줄이 건너뛰어지는 현상이 발생합니다.
Unreal
리플렉션 (Reflection)
프로그램이 실행 중에 자기 자신의 구조(클래스, 변수, 함수 등)를 관찰하고 수정할 수 있는 기능입니다. 거울(Reflection)에 자신을 비춰보는 것과 같습니다.
C++의 한계와 언리얼의 해결책
원래 순수 C++은 컴파일이 끝나면 변수 이름이나 함수 이름 같은 ‘메타데이터’를 모두 지워버립니다. (성능을 위해서 기계어만 남깁니다.)
언리얼 엔진은 이를 극복하기 위해 UHT (Unreal Header Tool)라는 자체 프로그램을 만들어, 컴파일 직전에 코드를 분석하고 필요한 정보를 따로 수집합니다.
사용법(매크로)
우리가 코드에 붙이는 단어들이 리플렉션 시스템에 등록하라는 뜻 입니다.
UCLASS(): 이 클래스 정보를 언리얼 엔진에 등록UPROPERTY(): 이 변수 정보를 언리얼 에디터에 보여주고 관리UFUNCTION(): 이 함수를 블루프린트에서 부를 수 있게 함
레플리케이션 (Replication)
멀티플레이어 게임에서 서버의 데이터와 상태를 클라이언트(플레이어들)에게 복제(동기화)해 주는 네트워크 시스템입니다.
핵심 원리
언리얼 엔진의 멀티플레이어는 ‘데디케이티드 서버(Dedicated Server)’ 방식을 기본으로 합니다. 서버가 절대적인 권한을 가지고 게임의 진짜 세상을 시뮬레이션하며, 클라이언트는 서버가 보내주는 화면을 보는 모니터 역할을 합니다.
- 액터 레플리케이션: 서버에서 스폰된 몬스터를 클라이언트 화면에도 똑같이 생성합니다.
- 프로퍼티 레플리케이션: 서버에서 플레이어의 체력 변수(
HP)가 깎이면, 접속한 모든 클라이언트의 화면에서도 해당 플레이어의 체력을 깎습니다. - RPC (Remote Procedure Call): 내 컴퓨터에서 함수를 실행했는데, 실제 작동은 서버 컴퓨터에서 일어나게 하는 마법 같은 원격 함수 호출 기능입니다.
언리얼 3개 시스템
| 시스템 명칭 | 핵심 역할 (무엇을 하는가?) | 비유 | 주요 작동 키워드 |
|---|---|---|---|
| Garbage Collection (GC) | 메모리 자동 관리. 더 이상 사용되지 않는 객체(쓰레기)를 찾아내어 자동으로 메모리에서 삭제합니다. | 청소부 | 생명 주기, 메모리 누수 방지 |
| Reflection | 런타임 정보 수집. 객체의 이름, 변수, 함수 등의 정보(메타데이터)를 기록하고 열람할 수 있게 합니다. | 신분증명서 (거울) | UCLASS, UPROPERTY, 블루프린트 연동 |
| Replication | 네트워크 동기화. 서버의 상태 변화를 접속한 모든 클라이언트에게 전달하여 똑같은 화면을 보게 합니다. | 방송국 (복제기) | 멀티플레이, 동기화, RPC |
세 가지 시스템은 관계
이 세 가지 시스템은 ‘리플렉션(Reflection)’을 중심축으로 하여 연결되어 있습니다. 리플렉션이 없다면 GC와 레플리케이션도 존재할 수 없습니다.
리플렉션 ➡️ 가비지
가비지 컬렉터가 메모리를 지우기 전에 “이 객체를 누군가 쓰고 있나?”를 판단해야 합니다. 이때 리플렉션 시스템(UPROPERTY)을 통해 등록된 변수들만 GC가 추적할 수 있습니다.
- 만약 언리얼 객체(
UObject)를 만들고UPROPERTY()매크로를 붙이지 않는다면? GC는 그 객체를 “아무도 안 쓰는 쓰레기”로 인지하고 메모리에서 삭제해 버려 게임에 충돌이 일어납니다!
리플렉션 ➡️ 레플리케이션
서버가 수많은 데이터 중 어떤 것을 클라이언트에게 보내야 할지(동기화할지) 어떻게 알까요?
이것 역시 리플렉션을 통해 지정합니다. 변수 위에UPROPERTY(Replicated)라고 적어두면, 리플렉션 시스템이 이 정보를 읽어서 네트워크 시스템에 이 변수 값이 바뀌면 클라이언트들에게 전해 달라고 알려줍니다.
요약
- Reflection은 언리얼 엔진이 데이터를 인식하게 만드는 도로입니다.
- Garbage Collection은 그 도로 위를 달리며 안 쓰는 데이터를 치우는 청소차입니다.
- Replication은 그 도로를 이용해 다른 컴퓨터로 데이터를 배달하는 택배차입니다.
Unreal Header Tool(UHT)
UHT 정의
UHT은 에픽게임즈에서 자체적으로 만든 코드 분석 및 자동 작성 프로그램입니다.
- 필요 이유: 순수 C++ 언어 자체는 프로그램이 실행 중일 때 자신의 변수나 함수 정보를 알 수 있는 기능(리플렉션)이 없습니다.
- UHT의 역할: 이 한계를 극복하기 위해, 우리가 작성한 C++ 헤더 파일(
.h)을 진짜 C++ 컴파일러가 읽기 전에 UHT가 먼저 읽어보고, 리플렉션과 네트워크 동기화 등에 필요한 추가 C++ 코드들을 대신 작성해 줍니다.
예시
UPROPERTY만 붙여놓으면, UHT가 그걸 보고 엔진이 이해할 수 있는 수백 줄의 진짜 코드를 뒤에서 만들어주는 느낌입니다.
UHT의 작동 원리
비주얼 스튜디오에서 F5(디버그)나 Ctrl + Shift + B(빌드)를 누르면 다음과 같은 순서로 돌아갑니다.
- UBT 실행: 전체적인 빌드 과정을 총괄하는 매니저가 실행됩니다.
- UHT 호출: UBT가 컴파일을 하기 전에 UHT를 먼저 부릅니다.
- 헤더 파일 스캔: UHT가 우리 프로젝트의 모든 헤더 파일(
.h)을 쫙 스캔하면서UCLASS(),UPROPERTY(),UFUNCTION()같은 매크로들을 찾습니다. - 자동 코드 생성: 찾은 매크로들을 바탕으로
[클래스명].generated.h와[클래스명].gen.cpp라는 파일을 자동으로 생성하여 프로젝트 폴더 깊숙한 곳(Intermediate 폴더)에 저장합니다. - C++ 컴파일: UHT의 작업이 모두 끝나면, 비로소 일반적인 C++ 컴파일러(Visual Studio MSVC)가 우리가 짠 코드와 UHT가 만들어준 코드를 합쳐서 최종 게임(실행 파일)으로 만들어냅니다.
매크로
C++ 컴파일러 입장에서 UCLASS(), UPROPERTY() 같은 매크로들은 안이 텅텅 빈 아무 내용이 없습니다.
하지만 UHT의 기준에는 다릅니다. UHT는 이 매크로들을 자신한테 정의되어 있는 내용으로 인식하고 수집합니다.
주의사항
언리얼 C++ 많이 겪는 빌드 에러의 90%는 UHT가 만들어낸 이 파일 때문에 발생합니다.
#include "MyClass.generated.h"의 위치우리가 클래스를 생성하면 헤더 파일 맨 아래쪽에
#include "클래스명.generated.h"가 자동으로 적혀있습니다. 이 줄은 반드시 해당 헤더 파일의#include목록 중 “가장 마지막(맨 아래)”에 위치해야 합니다.
- 이유: UHT가 코드를 자동 생성할 때, 자신이 만든 코드를 바로 저 위치에 끼워 넣도록 설계되어 있기 때문입니다.
- 만약 저 밑에 다른
#include를 적어버리면, UHT가 코드를 끼워 넣다가 순서가 꼬여서 치명적인 컴파일 에러를 발생시킵니다.