C# Regex Source Generator — 컴파일 타임 정규식
.NET 7에서 도입된 [GeneratedRegex] 어트리뷰트는 정규식을 런타임이 아닌 컴파일 타임에 C# 코드로 변환합니다. 런타임 Regex.Compile()보다 JIT 없이 최적화된 코드를 생성해 첫 실행 속도와 AOT 호환성을 모두 개선합니다.
1. 기본 사용법
섹션 제목: “1. 기본 사용법”using System.Text.RegularExpressions;
public partial class EmailValidator{ // 컴파일 타임에 Regex 구현 코드 생성 [GeneratedRegex( @"^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$", RegexOptions.IgnoreCase)] private static partial Regex EmailRegex();
public static bool IsValid(string email) => EmailRegex().IsMatch(email);}
// 사용2. 런타임 Regex와 비교
섹션 제목: “2. 런타임 Regex와 비교”public partial class Patterns{ // ❌ 매 호출마다 파싱 (가장 느림) static bool SlowCheck(string s) => Regex.IsMatch(s, @"\d{3}-\d{4}");
// ✅ 한 번 컴파일, 캐시됨 (중간) static readonly Regex _compiled = new Regex(@"\d{3}-\d{4}", RegexOptions.Compiled);
// ✅✅ 컴파일 타임 생성 (가장 빠름, AOT 안전) [GeneratedRegex(@"\d{3}-\d{4}")] private static partial Regex PhoneRegex();}3. 다양한 옵션
섹션 제목: “3. 다양한 옵션”public partial class Parsers{ // 멀티라인 + 대소문자 무시 [GeneratedRegex(@"^hello$", RegexOptions.Multiline | RegexOptions.IgnoreCase)] public static partial Regex HelloLine();
// 타임아웃 설정 (무한 루프 방지) [GeneratedRegex(@"(a+)+b", RegexOptions.None, matchTimeoutMilliseconds: 1000)] public static partial Regex SafePattern();
// 명명된 그룹 캡처 [GeneratedRegex( @"(?<year>\d{4})-(?<month>\d{2})-(?<day>\d{2})")] public static partial Regex DatePattern();}
// 명명된 그룹 사용var match = Parsers.DatePattern().Match("2024-03-15");if (match.Success){ string year = match.Groups["year"].Value; // "2024" string month = match.Groups["month"].Value; // "03" string day = match.Groups["day"].Value; // "15"}4. Span 기반 오버로드
섹션 제목: “4. Span 기반 오버로드”public partial class SpanParsers{ [GeneratedRegex(@"\b\w+\b")] private static partial Regex WordPattern();
public static int CountWords(ReadOnlySpan<char> text) { // .NET 7+: Span<char> 직접 매칭 (할당 없음) int count = 0; foreach (var _ in WordPattern().EnumerateMatches(text)) count++; return count; }
// 모든 매치 열거 (할당 최소화) public static IEnumerable<string> ExtractWords(string text) { foreach (Match m in WordPattern().Matches(text)) yield return m.Value; }}5. 벤치마크 결과
섹션 제목: “5. 벤치마크 결과”| Method | Mean | Allocated ||----------------- |----------:|----------:|| RuntimeNew | 2,145 ns | 1,456 B || RuntimeCompiled | 312 ns | 0 B || GeneratedRegex | 198 ns | 0 B |
- GeneratedRegex는 RuntimeCompiled 대비 ~36% 빠름- 첫 호출에서 JIT 비용 없음- NativeAOT 환경에서 유일하게 지원됨6. 복잡한 패턴 예시
섹션 제목: “6. 복잡한 패턴 예시”public partial class LogParser{ // 로그 라인 파싱: [2024-03-15 10:30:00] ERROR Message [GeneratedRegex( @"^\[(?<ts>\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2})\] " + @"(?<level>DEBUG|INFO|WARN|ERROR) (?<msg>.+)$", RegexOptions.Compiled)] private static partial Regex LogLine();
public record LogEntry(DateTime Timestamp, string Level, string Message);
public static LogEntry? Parse(string line) { var m = LogLine().Match(line); if (!m.Success) return null;
return new LogEntry( DateTime.Parse(m.Groups["ts"].Value), m.Groups["level"].Value, m.Groups["msg"].Value); }}7. AOT 호환성
섹션 제목: “7. AOT 호환성”// NativeAOT 빌드에서 Regex.Compile()은 지원 안 됨// [GeneratedRegex]는 완전 AOT 호환
// .pubxml 또는 csproj// <PublishAot>true</PublishAot>
// AOT 환경에서 동적 Regex가 필요하면:// RegexOptions.None 사용 (인터프리터 방식, 느리지만 동작함)var regex = new Regex(pattern, RegexOptions.None);[GeneratedRegex]는 정적으로 알 수 있는 모든 정규식 패턴에 사용하세요. 컴파일 타임 생성으로 첫 실행 지연이 없고 NativeAOT와 호환됩니다. 클래스에 partial 키워드를 붙이고 메서드를 private static partial Regex로 선언하면 Source Generator가 구현을 자동 생성합니다. EnumerateMatches(ReadOnlySpan<char>)로 힙 할당 없는 고성능 텍스트 스캔이 가능합니다.