Unity·Unreal 실전

Unreal Live Coding 실전: 에디터 재시작 없이 고쳐도 되는 코드와 안 되는 코드 구분하기

kr-gamedev 2026. 7. 6. 09:02

Unreal Engine의 Live Coding은 C++ 작업 속도를 크게 올려준다. 에디터를 끄지 않고 함수 구현을 바꾸고, 바로 플레이해서 확인할 수 있기 때문이다. 작은 전투 로직이나 UI 계산식을 조정할 때는 컴파일 대기 시간이 확 줄어든다.

문제는 Live Coding이 모든 C++ 변경을 안전하게 반영해 주는 기능은 아니라는 점이다. 특히 UCLASS, UPROPERTY, 생성자 기본값, 컴포넌트 구성처럼 에디터와 리플렉션 시스템에 깊게 묶인 코드는 “컴파일은 됐는데 에디터 상태는 이상한” 상황을 만들 수 있다. 오늘은 실전에서 어디까지 Live Coding으로 처리하고, 어디서 에디터 재시작을 해야 하는지 기준을 잡아본다.

1. Live Coding으로 안전한 변경

대체로 함수 내부 구현만 바꾸는 작업은 안전하다. 이미 존재하는 함수의 계산식, 조건문, 로그, 게임플레이 판정 정도는 Live Coding으로 빠르게 반복할 수 있다.

// MyDamageComponent.cpp

float UMyDamageComponent::CalculateFinalDamage(float BaseDamage, float Armor) const
{
    const float Reduced = BaseDamage - Armor * 0.6f;
    return FMath::Max(1.0f, Reduced);
}

이런 코드는 클래스의 메모리 구조를 바꾸지 않는다. 에디터가 알고 있는 객체 구조와 새로 컴파일된 함수 본문 사이에 큰 충돌이 없다. 그래서 전투 공식, 이동 보정, 쿨타임 계산처럼 자주 만지는 로직은 작은 함수로 분리해 두면 Live Coding 효율이 좋아진다.

2. 재시작이 필요한 변경

반대로 아래 변경은 Live Coding만 믿지 않는 편이 좋다.

  • UPROPERTY 추가, 삭제, 타입 변경
  • UFUNCTION 시그니처 변경
  • UCLASS 상속 구조 변경
  • 생성자에서 컴포넌트 추가 또는 이름 변경
  • DefaultSubobject 구성 변경
  • 블루프린트에 노출되는 기본값 변경

예를 들어 이런 변경은 에디터 재시작 대상이다.

// MyEnemyCharacter.h

UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category="Combat")
float StaggerThreshold = 30.0f;

필드를 하나 추가하는 것 자체는 단순해 보이지만, Unreal Header Tool이 만든 리플렉션 정보와 블루프린트 클래스의 저장 상태가 함께 맞아야 한다. Live Coding 후 에디터에서 값이 보이더라도 블루프린트 인스턴스가 이전 구조를 들고 있으면 예상 밖의 기본값이나 직렬화 문제가 생길 수 있다.

3. 생성자 기본값 대신 런타임 설정으로 빼기

자주 바꿔야 하는 값은 생성자에 박아두기보다 설정 객체나 데이터 에셋으로 빼는 편이 좋다. 그러면 Live Coding 재컴파일 없이도 에디터에서 값을 바꾸고 테스트할 수 있다.

// CombatTuningSettings.h

UCLASS(Config=Game, DefaultConfig)
class UCombatTuningSettings : public UDeveloperSettings
{
    GENERATED_BODY()

public:
    UPROPERTY(Config, EditAnywhere, Category="Damage")
    float ArmorScale = 0.6f;

    UPROPERTY(Config, EditAnywhere, Category="Damage")
    float MinDamage = 1.0f;
};

사용하는 쪽은 기본 설정을 읽어 계산한다.

float UMyDamageComponent::CalculateFinalDamage(float BaseDamage, float Armor) const
{
    const UCombatTuningSettings* Settings = GetDefault<UCombatTuningSettings>();

    const float Reduced = BaseDamage - Armor * Settings->ArmorScale;
    return FMath::Max(Settings->MinDamage, Reduced);
}

이 방식은 밸런스 값을 코드 구조와 분리한다. 코드 변경은 Live Coding으로 빠르게 확인하고, 숫자 조정은 프로젝트 설정이나 데이터 에셋에서 처리하면 반복 속도가 훨씬 안정적이다.

4. 컴포넌트 구성은 생성자에서 한 번만 믿기

액터 생성자에서 컴포넌트를 추가하는 코드는 Live Coding과 궁합이 좋지 않다.

AMyEnemy::AMyEnemy()
{
    WeaponPoint = CreateDefaultSubobject<USceneComponent>(TEXT("WeaponPoint"));
    WeaponPoint->SetupAttachment(GetMesh());
}

이미 에디터에 열린 블루프린트나 배치된 액터는 이전 컴포넌트 트리를 들고 있을 수 있다. 이 상태에서 컴포넌트 이름을 바꾸거나 새 기본 컴포넌트를 추가하면, 겉으로는 컴파일됐지만 디테일 패널이나 블루프린트 상속 구조가 꼬일 수 있다.

실전 기준은 단순하다. 생성자와 헤더를 건드렸다면 에디터를 재시작한다. 함수 내부만 바꿨다면 Live Coding으로 반복한다. 이 선을 팀 규칙으로 정해두면 “내 PC에서는 되는데 다른 사람 에디터에서는 깨지는” 시간을 줄일 수 있다.

결론

Live Coding은 Unreal C++ 개발의 반복 속도를 올려주는 좋은 도구지만, 리플렉션과 객체 구조 변경까지 안전하게 해결해 주는 만능 기능은 아니다. 함수 내부 구현, 계산식, 조건문 수정은 Live Coding으로 빠르게 돌리고, UPROPERTY, 생성자, 컴포넌트 구성, 블루프린트 노출 구조를 바꿨다면 에디터 재시작을 기준으로 삼는 것이 안전하다.

핵심은 자주 바꾸는 값을 코드 구조에서 분리하는 것이다. 전투 수치와 튜닝 값은 설정 객체나 데이터 에셋으로 빼고, C++ 코드는 작은 함수 단위로 유지하자. 그러면 Live Coding은 불안한 편법이 아니라 매일 쓰는 안정적인 개발 루프가 된다.