콘텐츠로 이동

C# Hot Reload 내부 동작 원리와 활용

Hot Reload는 .NET 6에서 도입된 기능으로, 애플리케이션을 재시작하거나 중단점에서 멈추지 않고도 실행 중인 코드를 수정하여 변경 사항을 즉시 반영합니다. 개발 이터레이션 속도를 크게 향상시킵니다.


항목Edit and ContinueHot Reload
도입 시기Visual Studio 구버전.NET 6+
중단점 필요필요불필요
디버거 필요필요불필요
CLI 지원XO (dotnet watch)
비디버그 모드XO
지원 범위더 제한적더 넓음

Terminal window
# dotnet watch — 파일 변경 감지 후 자동 Hot Reload
dotnet watch run
# Hot Reload 없이 watch (재시작만)
dotnet watch run --no-hot-reload
# 명시적 적용 (파일 저장 후 콘솔에서)
# [Ctrl+R] — 변경 사항 강제 적용
# [Ctrl+C] — 종료
<!-- launchSettings.json에서 설정 -->
{
"profiles": {
"MyApp": {
"hotReloadEnabled": true
}
}
}

Hot Reload는 System.Reflection.Metadata.MetadataUpdater를 통해 실행 중인 어셈블리의 메타데이터와 IL을 동적으로 교체합니다.

소스 파일 변경
Roslyn 증분 컴파일 (변경된 부분만)
Delta IL + Delta Metadata 생성
MetadataUpdater.ApplyUpdate() 호출
CLR이 실행 중인 메서드 교체

프레임워크와 라이브러리가 Hot Reload에 반응하도록 핸들러를 등록합니다.

[assembly: MetadataUpdateHandler(typeof(HotReloadHandler))]
static class HotReloadHandler
{
// Hot Reload 적용 전 호출
internal static void ClearCache(Type[]? updatedTypes) { }
// Hot Reload 적용 후 호출
internal static void UpdateApplication(Type[]? updatedTypes)
{
Console.WriteLine("Hot Reload 적용됨");
// UI 갱신, 캐시 무효화 등
foreach (var type in updatedTypes ?? Array.Empty<Type>())
{
Console.WriteLine($" 변경된 타입: {type.FullName}");
}
}
}

// ✅ 지원: 메서드 본문 수정
public string GetGreeting() => "Hello!"; // → "Hi!"
// ✅ 지원: 람다 표현식 변경
Action greet = () => Console.WriteLine("Hello");
// ✅ 지원: 필드 초기화식 변경
private int _count = 0; // → 1
// ✅ 지원: 속성 getter/setter 본문 변경
public int Value { get => _value * 2; } // → _value * 3
// ❌ 미지원: 새 멤버 추가
public void NewMethod() { } // 재시작 필요
// ❌ 미지원: 인터페이스 변경
interface IService { void NewMethod(); } // 재시작 필요
// ❌ 미지원: 제네릭 타입 변경
class Box<T> { public T? ExtraField; } // 재시작 필요

// Razor 파일 변경 즉시 반영
// 미들웨어/엔드포인트 변경도 일부 지원
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddRazorPages();
var app = builder.Build();
app.MapRazorPages();
app.Run();

dotnet watch로 실행 시 .cshtml, .razor 파일 변경이 자동으로 반영됩니다.


@* Counter.razor *@
@page "/counter"
<h1>카운터: @count</h1>
<button @onclick="Increment">증가</button>
@code {
int count = 0;
void Increment()
{
count++; // 이 로직을 변경하면 Hot Reload로 즉시 반영
}
}

Blazor WebAssembly는 브라우저 새로고침 없이 변경 사항이 반영됩니다.


<!-- MauiProgram.cs에서 Hot Reload 지원 확인 -->
// XAML 변경도 Hot Reload 지원
// MainPage.xaml 수정 → 에뮬레이터에서 즉시 반영

8. 프로그래밍 방식으로 Hot Reload 감지

섹션 제목: “8. 프로그래밍 방식으로 Hot Reload 감지”
// 컴포넌트가 Hot Reload에 반응해야 할 때
[assembly: MetadataUpdateHandler(typeof(ComponentRegistry))]
public static class ComponentRegistry
{
private static readonly List<WeakReference<IHotReloadable>> _components = new();
public static void Register(IHotReloadable component)
{
_components.Add(new WeakReference<IHotReloadable>(component));
}
internal static void UpdateApplication(Type[]? types)
{
foreach (var weakRef in _components.ToList())
{
if (weakRef.TryGetTarget(out var component))
component.OnHotReload();
else
_components.Remove(weakRef);
}
}
}
public interface IHotReloadable
{
void OnHotReload();
}

Terminal window
# 권장 개발 명령
dotnet watch run --project MyApp.csproj
# 특정 파일만 감시
dotnet watch run --project MyApp.csproj -- --urls=https://localhost:7000
# 변경 후 자동 브라우저 새로고침 (Blazor)
dotnet watch --project MyBlazorApp.csproj
  1. 디버그 실행 (F5) 또는 디버그 없이 실행 (Ctrl+F5)
  2. 코드 수정
  3. 저장 (Ctrl+S) → 자동 적용
  4. 또는 “Hot Reload” 버튼 클릭

제약: 새 타입/멤버 추가 불가
해결: 기존 메서드 수정으로 대체 → 나중에 재시작 후 정리
제약: 제네릭 변경 불가
해결: 비제네릭 헬퍼 메서드 추출 후 제네릭 메서드에서 호출
제약: 인터페이스 변경 불가
해결: 새 인터페이스 분리 → 재시작 후 기존 인터페이스와 통합

  • Hot Reload는 실행 중 IL/메타데이터를 교체하여 재시작 없이 변경 반영
  • dotnet watch CLI와 Visual Studio에서 기본 지원
  • 메서드 본문, 람다, 필드 초기화 변경은 지원
  • 새 멤버/타입 추가, 인터페이스 변경은 재시작 필요
  • MetadataUpdateHandler로 프레임워크와 Hot Reload 통합 가능