나만의 작은 도서관
[TIL][C++] 251024 MMO 서버 개발 122일차: [언리얼] UE_LOG() 매크로 함수, GEngine->AddOnScreenDebugMessage() 함수 자세히 알아보기 본문
Today I Learn
[TIL][C++] 251024 MMO 서버 개발 122일차: [언리얼] UE_LOG() 매크로 함수, GEngine->AddOnScreenDebugMessage() 함수 자세히 알아보기
pledge24 2025. 10. 25. 04:28주의사항: 해당 글은 일기와 같은 기록용으로, 다듬지 않은 날것 그대로인 글입니다.
UE_LOG() 매크로 함수
UE_LOG(카테고리, 로그 레벨, 포맷 문자열)
- 언리얼 에디터의 Output Log 창에 로그를 출력하기 위한 매크로 함수. C++에서 std::cout이나 printf 역할을 하는 함수라고 보면 된다. Output Log가 기본 설정인 경우 온갖 로그들이 다 뜨는 바람에 잘 안 쓰게 되는 게 있다.(그냥 AddOnScreenDebugMessage를 쓰고 말지…)
- 근데 이건 설정하면 되긴 한다. 이에 대해선 “UE_LOG를 쓸 때 알아두면 좋은 팁들”에서 따로 다루겠다.
UE_LOG 각 인자들 설명
UE_LOG 첫 번째 인자 - 카테고리
- 로그 메시지를 분류하는 데 사용하는 인자.
- 변수 타입은 “FName”.
- 모든 UE_LOG 호출에는 반드시 카테고리가 필요하다. 시스템에 등록되지 않은 카테고리는 사용할 수 없으며, 카테고리 등록은 카테고리 정의 매크로 함수로 한다.
// AMyActor.cpp
// 해당 cpp 파일에서만 잠깐 사용할 로그라면 전역으로 카테고리를 정의해두면 된다.
DEFINE_LOG_CATEGORY_STATIC(LogMyActor, Log, All); // 모든 로그 레벨 허용
DEFINE_LOG_CATEGORY_STATIC(LogMyActor2, Log, All); // 모든 로그 레벨 허용
DEFINE_LOG_CATEGORY_STATIC(LogMyActor3, Log, All); // 모든 로그 레벨 허용
...
AMyActor::AMyActor()
{
}
카테고리 관련 매크로 함수 종류
// 카테고리를 추가하는 매크로 함수 종류)
// Extern 카테고리 선언(.h에 작성됨), 외부에 선언되어 있어야 사용 가능
DECLARE_LOG_CATEGORY_EXTERN(Name, DefaultVerbosity, CompileTimeVerbosity)
// 일반적인 카테고리 정의(.cpp에 작성됨)
DEFINE_LOG_CATEGORY(Name)
// Static 카테고리 정의(.cpp에 작성됨), 카테고리를 정의한 .cpp 내부에서 사용 가능
DEFINE_LOG_CATEGORY_STATIC(Name, DefaultVerbosity, CompileTimeVerbosity)
// 클래스 기반 카테고리 선언(.h에 작성됨). 잘 안쓴다고 하니 패스
DECLARE_LOG_CATEGORY_CLASS(ClassName, DefaultVerbosity, CompileTimeVerbosity)
// 클래스 기반 카테고리 정의(.cpp에 작성됨). 잘 안쓴다고 하니 패스
DEFINE_LOG_CATEGORY_CLASS(ClassName)
// 인자 설명)
// Name: 로그 카테고리 이름
// DefaultVerbosity: 디폴트 로그 레벨
// CompileTimeVerbosity: 빌드 시 포함할 최대 로그 레벨. 지정한 레벨보다 낮은 로그는 컴파일 타임에서 제거됨.
프로젝트 전반적으로 사용할 카테고리라면?
- 프로젝트 전반적으로 사용할 거라면 전용 파일을 만들어서 관리하는 것이 좋다. 방법은 아래와 같다.
// MyLogCategory.h (카테고리 전용 헤더 파일)
#include "Logging/LogMacros.h"
// 게임 전역에서 사용하는 로그 카테고리 선언
DECLARE_LOG_CATEGORY_EXTERN(LogNetwork, Log, All);
DECLARE_LOG_CATEGORY_EXTERN(LogDatabase, Warning, All);
DECLARE_LOG_CATEGORY_EXTERN(LogAI, Log, All);
DECLARE_LOG_CATEGORY_EXTERN(LogItem, Log, All);
// MyLogCategory.cpp (카테고리 전용 소스 파일)
#include "MyLogCategories.h"
// 각 로그 카테고리 정의
DEFINE_LOG_CATEGORY(LogNetwork);
DEFINE_LOG_CATEGORY(LogDatabase);
DEFINE_LOG_CATEGORY(LogAI);
DEFINE_LOG_CATEGORY(LogItem);
기본으로 제공하는 카테고리가 있다.
- 언리얼 엔진에서 제공하는 기본 카테고리(Ex. LogTemp, LogActor, LogBlueprint, …)들이 있다. 이러한 기본 카테고리를 사용하여 로그를 찍어도 되지만, 프로젝트에 맞게 사용자 정의 카테고리를 만들어서 로그를 찍는 것이 일반적이고 권장되는 방식이다.
// 하지만 귀찮기 때문에 오늘도 LogTemp를 쓴다.
UE_LOG(LogTemp, Log, TEXT("I Love LogTemp Category"))
UE_LOG 두 번째 인자 - 로그 레벨(Verbosity)
- 출력하고자 하는 로그 메시지의 중요도나 심각도를 나타낸다.
- 레벨에 따라 로그 메시지의 색상이 달라지며, 엔진이 해당 메시지를 출력할지 여부를 결정한다.
| 로그 레벨 | 설명 | 용도 | 메세지 색상 |
| Fatal | 프로그램 실행을 즉시 중단시키는 심각한 오류. | 치명적인 오류 발생 시 사용. (크래시를 발생시키며 에디터가 꺼지므로 거의 사용되지 않음) | 빨간색이지만 에디터가 꺼져서 볼 수 없음 |
| Error | 복구 불가능한 문제 또는 즉시 수정해야 하는 오류. | 게임의 기능에 심각한 영향을 미치는 오류. | 빨간색 |
| Warning | 잠재적인 문제나 예상치 못한 상황. | 버그는 아니지만 주의가 필요한 상황. | 주황색 |
| Display | 게임 플레이 중 발생하는 중요한 전환점이나 사건을 기록하기 위해 기록하는 로그 | 레벨 전환, 체크포인트 저장, 중요 데이터 로드 등을 기록할때 사용 | 흰색 |
| Log | 일반적인 정보 기록. 가장 흔히 사용되는 레벨. | 함수 호출, 값 변경 등 일반적인 디버깅. | 흰색 |
| Verbose | 상세한 디버깅 정보. | 특정 기능의 세부 단계 추적. (성능에 영향 가능) | 회색? |
| VeryVerbose | 매우 상세한 모든 디버깅 정보. | 프레임워크 수준의 디버깅. (극히 드물게 사용) | 회색? |
Verbose, VeryVerbose는 기본적으로 출력되지 않는다.
- 이 두 레벨은 매우 상세한 디버깅을 위해 존재하며, 성능 부하가 크기 때문에 기본적으로 출력되지 않도록 설정되어 있다.
- 개인 프로젝트 수준에선 사용할 일이 없는 로그 레벨이라고 생각되어서 추가적인 공부는 하지 않기로 했다.
로그 레벨에 따른 출력 예시
//UE_LOG(LogTemp, Fatal, TEXT("Fatal Color Test")); <= 크래시 남
UE_LOG(LogTemp, Error, TEXT("Error Color Test"));
UE_LOG(LogTemp, Warning, TEXT("Warning Color Test"));
UE_LOG(LogTemp, Display, TEXT("Display Color Test"));
UE_LOG(LogTemp, Log, TEXT("Log Color Test"));
UE_LOG(LogTemp, Verbose, TEXT("Verbose Color Test"));
UE_LOG(LogTemp, VeryVerbose, TEXT("VeryVerbose Color Test"));

UE_LOG 세 번째 인자 - 포맷 문자열
- 표준 C/C++의 printf 함수에서 사용하는 것과 동일한 포맷 지정자 (%s, %d, %f, %p 등)를 사용하여 메시지를 표현한다.
- 문자열 자체는 TEXT() 매크로 함수로 감싸줘야 한다.
- 참고로, TEXT() 매크로 함수에 의해 한글 문자열을 넣어도 잘 출력된다.
int32 Health = 100;
FString PlayerName = TEXT("MyPlayer");
UE_LOG(LogTemp, Log, TEXT("플레이어 %s의 현재 체력: %d"), *PlayerName, Health);
// 주의: FString이나 FName을 %s로 출력할 때는 반드시 * 연산자를 붙여 TCHAR*로 변환해야한다.
조건부 로깅을 하고 싶다면? UE_LOG_IF
- 만약 특정 상황에서만 로그를 찍고 싶다면, if문으로 감쌀 필요 없이 UE_LOG_IF 매크로 함수를 사용하면 된다.
// 조건부 로깅을 하고싶다면 if 문으로 감쌀 필요없이,
if(CurrentValue > Threshold)
{
UE_LOG(LogTemp, Warning, TEXT("임계값 초과! 현재 값: %f"), CurrentValue)
}
// 아래와 같이 UE_LOG_IF를 사용하면 된다.
bool bIsCritical = (CurrentValue > Threshold);
UE_LOG_IF(bIsCritical, LogTemp, Warning, TEXT("임계값 초과! 현재 값: %f"), CurrentValue);
UE_LOG를 쓸 때 알아두면 좋은 팁들
로그 클리어는 오른쪽 마우스 클릭 → Clear Log

- (명령어로 로그를 Clear 할 수 있는지 좀 찾아봤는데, 잘 안 나와서 여기에 적어두지 못했다. 하지만 좀 더 찾아보면 있을 거 같긴 하다.)
Output Log 단축키는 alt+또는
- alt+`는 Output Log 토글 단축키.
- ` 는 3단계 과정이 있는데, cmd 명령창으로 이동 → Output Log 열기 → Output Log 닫기 순서로 실행된다.
Output Log 창 레이아웃에 고정시키기

- Output Log는 게임 플레이 시 자꾸 내려가는 문제가 있는데, 이게 너무 불편하기 때문에 레이아웃에 아예 고정시켜버리고 싶을 때가 많다.
- 불편함을 없애기 위해 Output Log 창을 레이아웃에 고정시키는 방법은 두 가지 방법이 있는데 첫 번째 방법은 Output Log 창에서 오른쪽 상단에 있는 Dock in Layout을 클릭하는 거고,
- 두 번째 방법은 에디터 상단 바에서 Window → Output Log 창을 클릭해서 창을 활성화시키는 것이다.
- 어떤 방법으로 했든 고정을 했다면 아래와 같이 고정된 것을 볼 수 있다. 이렇게 고정되면 플레이 시에도 Output Log 창이 내려가지 않아 모니터링하기 편하다.

로그 필터링 하기
- 사실 이게 없었으면 계속 UE_LOG는 안 쓰지 않았을까 싶다. 로그 필터링하는 방법은 간단하다. Output Log 창의 상단바에 있는 Filters를 열어 Show All 체크 박스를 비활성화해 주고, Filters 목록에서 내가 보고 싶은 로그의 카테고리만 체크해 주면 된다.
- 상황에 따라 LogTemp 카테고리나 그 외 카테고리가 안 나타날 수 있는데, 이런 경우 한 번 Level을 플레이해 주면 정상적으로 뜨게 된다.

- 아쉽게도 Output Log창의 Filters 설정은 에디터 세션 단위로만 유지되기 때문에 에디터를 종료하면 필터링 설정이 사라져서 매번 설정해둬야 한다는 것이다. 이게 귀찮으면 ini파일을 뜯어서 설정을 바꿔야 하는데 귀찮아서 여기에 따로 적어두진 않겠다. 찾아보면 나오니 필요하면 찾아서 하면 될 듯하다.
GEngine→AddOnScreenDebugMessage()
void AddOnScreenDebugMessage(uint64 Key, float TimeToDisplay, FColor DisplayColor, const FString& DebugMessage, bool bNewerOnTop = true, const FVector2D& TextScale = FVector2D::One());
- 이 함수는 게임 화면(뷰포트) 위에 직접 텍스트 형태로 띄워서 실시간으로 디버그 메시지를 출력한다. 개인 프로젝트 정도의 규모에선 굉장히 자주 사용하는, 어찌 보면 애용한다고 볼 수 있는 함수이다.
GEngine->AddOnScreenDebugMessage 각 인자들 설명
1) Key
- uint64 타입.
- 메시지 식별자이다. Key값을 통해 특정 디버그 메시지를 업데이트하거나 제거할 때 사용한다. 사용하는 방법은 2가지인데, 각각 아래와 같다.
- Key를 -1 또는 INDEX_NONE으로 지정: Key값을 지정하지 않음. 해당 디버그 메시지는 업데이트되지 않으며, 출력 시 새로운 메시지로 추가되어 출력된다.
- Key ≥ 0으로 지정: Key값을 지정함. 이 Key를 사용하는 메시지는 화면의 유일하게 존재하며, 같은 Key값으로 AddOnScreenDebugMessage() 함수를 호출하면 기존 메시지가 업데이트(덮어쓰기)된다.
2) TimeToDisplay
- float 타입.
- 메시지가 뷰포트에 표시될 시간을 초 단위로 지정. 이 시간이 지나면 메시지는 자동으로 사라진다.
- 0.0f로 지정하면 다음 프레임까지만 표시된다.
3) DisplayColor
- FColor 타입(아마도 enum인 듯?).
- 메시지 텍스트의 색상을 설정.
- Ex. FColor::Red, FColor::Green
4) DebugMessage
- const FString& 타입.
- 화면에 출력할 디버그 메시지. FString 타입으로 전달해야 함을 주의해야 한다.
- 문자열 포맷 방식을 사용하고 싶다면 FString::Printf()를 사용하면 된다.
GEngine->AddOnScreenDebugMessage(-1, 5.f, FColor::Red, TEXT("Hello World"));
GEngine->AddOnScreenDebugMessage(-1, 5.f, FColor::Red, FString::Printf(TEXT("%f, %f"), 15.0, 5.0));
5) bNewerOnTop
- bool 타입(기본값으로 true)
- 새로 생성된 메시지가 기존 메시지 위에 표시될지 여부를 결정한다.
- Key값이 지정된 경우(Key값 ≥ 0) 해당 변수의 값은 “무시된다”
- Key값이 지정되지 않은 경우(Key값이 -1 또는 INDEX_NONE), true이면 기존 메시지 위에 배치되고, false이면 아래에 배치된다.
- (그냥 true 박아두고 쓰자. 괜히 false로 했다가 헷갈리기만 한다.)
GEngine->AddOnScreenDebugMessage(-1, 5.f, FColor::Red, TEXT("First Message - False"), false);
GEngine->AddOnScreenDebugMessage(-1, 5.f, FColor::Red, TEXT("Second Message - False"), false);
GEngine->AddOnScreenDebugMessage(-1, 5.f, FColor::Cyan, TEXT("First Message - True"));
GEngine->AddOnScreenDebugMessage(-1, 5.f, FColor::Cyan, TEXT("Second Message - True"));

6) TextScale
- FVector2 D 타입(기본값으로 (1, 1))
- 디버그 메시지의 크기를 조절한다. X값을 늘리면 가로가 늘어나고, Y값을 늘리면 세로가 늘어난다.
AddOnScreenDebugMessage() 예제
GEngine->AddOnScreenDebugMessage(0, 5.f, FColor::Red, TEXT("Index is 0"));
GEngine->AddOnScreenDebugMessage(4, 5.f, FColor::Cyan, TEXT("Index is 4"), true, FVector2D(4.f, 2.f));
GEngine->AddOnScreenDebugMessage(2, 5.f, FColor::Blue, TEXT("Index is 2"));
GEngine->AddOnScreenDebugMessage(7, 5.f, FColor::Magenta, TEXT("Index is 7"));
GEngine->AddOnScreenDebugMessage(6, 5.f, FColor::Emerald, TEXT("Index is 6"));

