원본 생성기
이 문서에서는 .NET Compiler Platform("Roslyn") SDK의 일부로 제공되는 원본 생성기에 대한 개요를 제공합니다. 원본 생성기를 사용하면 C# 개발자가 컴파일되는 사용자 코드를 검사할 수 있습니다. 생성기는 사용자의 컴파일에 추가되는 새 C# 원본 파일을 즉시 만들 수 있습니다. 이러한 방식으로 컴파일하는 동안 실행되는 코드가 있습니다. 프로그램을 검사하여 나머지 코드와 함께 컴파일되는 추가 소스 파일을 생성합니다.
원본 생성기는 C# 개발자가 작성할 수 있는 새로운 종류의 구성 요소로, 다음 두 가지 주요 작업을 수행할 수 있습니다.
컴파일되는 모든 사용자 코드를 나타내는 컴파일 개체를 검색합니다. 이 개체를 검사할 수 있으며 현재의 분석기와 마찬가지로 컴파일 중인 코드에 대한 구문 및 의미 모델과 함께 작동하는 코드를 작성할 수 있습니다.
컴파일 중에 컴파일 개체에 추가할 수 있는 C# 소스 파일을 생성합니다. 즉, 코드를 컴파일하는 동안 추가 소스 코드를 컴파일에 대한 입력으로 제공할 수 있습니다.
이러한 두 가지를 결합하면 원본 생성기가 매우 유용합니다. 컴파일하는 동안 컴파일러가 빌드하는 모든 풍부한 메타데이터를 사용하여 사용자 코드를 검사할 수 있습니다. 그런 다음 생성기는 분석한 데이터를 기반으로 하는 동일한 컴파일로 C# 코드를 다시 내보낸다. Roslyn 분석기에 익숙한 경우 원본 생성기를 C# 소스 코드를 내보낼 수 있는 분석기라고 생각할 수 있습니다.
원본 생성기는 아래에 시각화된 컴파일 단계로 실행됩니다.
원본 생성기는 모든 분석기와 함께 컴파일러에 의해 로드되는 .NET Standard 2.0 어셈블리입니다. .NET Standard 구성 요소를 로드하고 실행할 수 있는 환경에서 사용할 수 있습니다.
중요
현재는 .NET Standard 2.0 어셈블리만 원본 생성기로 사용할 수 있습니다.
일반적인 시나리오
최신 기술에서 사용하는 분석을 바탕으로 사용자 코드를 검사하고 정보나 코드를 생성하는 대표적인 세 가지 방법이 있습니다.
- 런타임 리플렉션,
- MSBuild 작업 저글링,
- (이 문서에서는 설명하지 않는) IL(중간 언어) 위빙입니다.
원본 생성기는 이들 각각의 방법보다 개선될 수 있습니다.
런타임 리플렉션
런타임 리플렉션은 오래 전에 .NET에 추가된 강력한 기술입니다. 이를 사용하는 수많은 시나리오가 있습니다. 일반적인 시나리오는 앱이 시작될 때 사용자 코드의 일부 분석을 수행하고 해당 데이터를 사용하여 작업을 생성하는 것입니다.
예를 들어 ASP.NET Core 웹 서비스가 처음 실행되면 리플렉션을 사용하여 정의한 구문을 검색하여 컨트롤러 및 razor 페이지와 같은 것을 “연결”할 수 있습니다. 이렇게 하면 강력한 추상화로 간단한 코드를 작성할 수 있지만 런타임에 성능 저하가 발생합니다. 웹 서비스 또는 앱이 처음 시작될 때 코드에 대한 정보를 검색하는 모든 런타임 리플렉션 코드 실행이 완료될 때까지 요청을 수락할 수 없습니다. 이 성능 저하는 크지 않지만 자체 앱에서 개선할 수 없는 고정 비용입니다.
원본 생성기를 사용하면 시작의 컨트롤러 검색 단계가 컴파일 시간에 대신 발생할 수 있습니다. 생성기는 소스 코드를 분석하고 앱을 "연결"하는 데 필요한 코드를 내보낼 수 있습니다. 원본 생성기를 사용하면 오늘 런타임에 발생하는 작업이 컴파일 시간으로 푸시될 수 있으므로 시작 시간이 더 빨라질 수 있습니다.
MSBuild 작업 저글링
원본 생성기는 런타임 시 리플렉션에 제한되지 않도록 성능을 개선하여 형식도 검색할 수 있습니다. 일부 시나리오에서는 컴파일에서 데이터를 검사할 수 있도록 MSBuild C# 작업(CSC라고 함)을 여러 번 호출합니다. 짐작하시겠지만, 컴파일러를 두 번 이상 호출하면 앱을 구축하는 데 걸리는 총 시간이 영향을 받습니다. 원본 생성기는 일부 성능 혜택을 제공할 뿐만 아니라 도구가 올바른 추상화 수준에서 작동할 수 있도록 해주므로, 이와 같이 MSBuild 작업을 저글링할 필요가 없도록 원본 생성기를 사용할 방법을 조사하고 있습니다.
원본 생성기에서 제공할 수 있는 또 다른 기능은 컨트롤러와 razor 페이지 간의 ASP.NET Core 라우팅이 작동하는 방식과 같은 일부 "문자열 형식" API의 사용을 없애는 것입니다. 원본 생성기를 사용하면 컴파일 시간 세부 정보로 생성되는 데 필요한 문자열로 라우팅을 강력하게 입력할 수 있습니다. 이렇게 하면 잘못 입력된 문자열 리터럴이 요청이 올바른 컨트롤러에 맞지 않는 횟수를 줄입니다.
원본 생성기 시작
이 가이드에서는 ISourceGenerator API를 사용하여 원본 생성기를 만드는 방법을 살펴봅니다.
.NET 콘솔 애플리케이션을 만듭니다. 이 예제에서는 .NET 6를 사용합니다.
Program
클래스를 다음 코드로 바꿉니다. 다음 코드는 최상위 문을 사용하지 않습니다. 이 첫 번째 소스 생성기는 해당 클래스에 부분 메서드를 작성하므로 클래식 양식이Program
필요합니다.namespace ConsoleApp; partial class Program { static void Main(string[] args) { HelloFrom("Generated Code"); } static partial void HelloFrom(string name); }
참고
이 샘플을 있는 그대로 실행할 수 있지만 아무 일도 발생하지 않습니다.
지금부터는
partial void HelloFrom
메서드를 구현하는 원본 생성기 프로젝트를 만들겠습니다.TFM(대상 프레임워크 모니커)을
netstandard2.0
대상으로 하는 .NET 표준 라이브러리 프로젝트를 만듭니다. NuGet 패키지 Microsoft.CodeAnalysis.Analyzers 및 Microsoft.CodeAnalysis.CSharp를 추가합니다.<Project Sdk="Microsoft.NET.Sdk"> <PropertyGroup> <TargetFramework>netstandard2.0</TargetFramework> </PropertyGroup> <ItemGroup> <PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="4.9.2" PrivateAssets="all" /> <PackageReference Include="Microsoft.CodeAnalysis.Analyzers" Version="3.3.4" PrivateAssets="all" /> </ItemGroup> </Project>
팁
원본 생성기 프로젝트는
netstandard2.0
TFM를 대상으로 해야 합니다. 그렇지 않으면 정상적으로 작동하지 않습니다.다음과 같이 자체 원본 생성기를 지정하는 HelloSourceGenerator.cs라는 새 C# 파일을 만듭니다.
using Microsoft.CodeAnalysis; namespace SourceGenerator { [Generator] public class HelloSourceGenerator : ISourceGenerator { public void Execute(GeneratorExecutionContext context) { // Code generation goes here } public void Initialize(GeneratorInitializationContext context) { // No initialization required for this one } } }
원본 생성기는 Microsoft.CodeAnalysis.ISourceGenerator 인터페이스를 구현하고 Microsoft.CodeAnalysis.GeneratorAttribute가 있어야 합니다. 모든 원본 생성기에 초기화가 필요한 것은 아니며, 이 예제 구현의 경우입니다. 여기서 ISourceGenerator.Initialize 은 비어 있습니다.
ISourceGenerator.Execute 메서드의 내용을 다음 구현으로 바꿉니다.
using Microsoft.CodeAnalysis; namespace SourceGenerator { [Generator] public class HelloSourceGenerator : ISourceGenerator { public void Execute(GeneratorExecutionContext context) { // Find the main method var mainMethod = context.Compilation.GetEntryPoint(context.CancellationToken); // Build up the source code string source = $@"// <auto-generated/> using System; namespace {mainMethod.ContainingNamespace.ToDisplayString()} {{ public static partial class {mainMethod.ContainingType.Name} {{ static partial void HelloFrom(string name) => Console.WriteLine($""Generator says: Hi from '{{name}}'""); }} }} "; var typeName = mainMethod.ContainingType.Name; // Add the source code to the compilation context.AddSource($"{typeName}.g.cs", source); } public void Initialize(GeneratorInitializationContext context) { // No initialization required for this one } } }
context
개체에서는 컴파일 진입점 또는Main
메서드에 액세스할 수 있습니다.mainMethod
인스턴스는 IMethodSymbol이며, 메서드 또는 메서드형 기호(생성자, 소멸자, 연산자 또는 속성/이벤트 접근자 포함)를 나타냅니다. 메서드는 Microsoft.CodeAnalysis.Compilation.GetEntryPoint 프로그램의 진입점에 대한 를 반환 IMethodSymbol 합니다. 다른 메서드를 사용하면 프로젝트에서 메서드 기호를 찾을 수 있습니다. 이 개체에서 포함하는 네임스페이스(있는 경우)와 형식에 대해 추론할 수 있습니다. 이 예제의 는source
보간된 구멍이 포함된 네임스페이스 및 형식 정보로 채워지는 생성할 소스 코드를 템플릿하는 보간된 문자열입니다.source
는 힌트 이름과 함께context
에 추가됩니다. 이 예제에서 생성기는 콘솔 애플리케이션에서 메서드의partial
구현을 포함하는 생성된 새 소스 파일을 만듭니다. 원본 생성기를 작성하여 원하는 원본을 추가할 수 있습니다.팁
GeneratorExecutionContext.AddSource 메서드의
hintName
매개 변수는 고유한 이름이라면 무엇이든 가능합니다. 대부분의 경우에는 이름에".g.cs"
나".generated.cs"
같은 명시적 C# 파일 확장명을 제공합니다. 파일 이름을 사용하면 원본으로 생성 중인 파일을 식별할 수 있습니다.이제 작동하는 생성기가 생겼지만, 이를 콘솔 애플리케이션에 연결해야 합니다. 원래 콘솔 애플리케이션 프로젝트를 편집하고 다음을 추가하여 프로젝트 경로를 위에서 만든 .NET Standard 프로젝트의 경로로 바꿉니다.
<!-- Add this as a new ItemGroup, replacing paths and names appropriately --> <ItemGroup> <ProjectReference Include="..\PathTo\SourceGenerator.csproj" OutputItemType="Analyzer" ReferenceOutputAssembly="false" /> </ItemGroup>
이 새 참조는 기존 프로젝트 참조가 아니며 및
ReferenceOutputAssembly
특성을 포함OutputItemType
하도록 수동으로 편집해야 합니다. 의OutputItemType
및 특성에 대한 자세한 내용은 일반적인 MSBuild 프로젝트 항목: ProjectReference를 참조하세요.ReferenceOutputAssembly
ProjectReference
이제 콘솔 애플리케이션을 실행하면 생성된 코드가 실행되어 화면에 출력되는 것을 볼 수 있습니다. 콘솔 애플리케이션 자체는 메서드를
HelloFrom
구현하지 않고 원본 생성기 프로젝트에서 컴파일하는 동안 생성된 원본입니다. 다음 텍스트는 애플리케이션의 출력 예제입니다.Generator says: Hi from 'Generated Code'
참고
IntelliSense를 확인하고 오류를 제거하려면 Visual Studio를 다시 시작하여 도구 환경을 적극적으로 개선해야 할 수 있습니다.
Visual Studio 사용하는 경우에는 원본에서 생성한 파일을 볼 수 있습니다. 솔루션 탐색기 창에서 종속성>분석기> SourceGeneratorSourceGenerator.HelloSourceGenerator>를 확장하고 Program.g.cs 파일을 두 번 클릭합니다.
이 생성된 파일을 열면 Visual Studio에서 파일이 자동으로 생성되고 편집할 수 없음을 나타냅니다.
빌드 속성을 설정하여 생성된 파일을 저장하고 생성된 파일이 저장되는 위치를 제어할 수도 있습니다. 콘솔 애플리케이션의 프로젝트 파일에서 요소를
<PropertyGroup>
에 추가하고<EmitCompilerGeneratedFiles>
해당 값을true
로 설정합니다. 프로젝트를 다시 빌드합니다. 이제 생성된 파일은 obj/Debug/net6.0/generated/SourceGenerator/SourceGenerator.HelloSourceGenerator 아래에 만들어집니다. 경로의 구성 요소는 빌드 구성, 대상 프레임워크, 원본 생성기 프로젝트 이름 및 생성기의 정규화된 형식 이름에 매핑됩니다. 애플리케이션의 프로젝트 파일에 요소를 추가하여<CompilerGeneratedFilesOutputPath>
더 편리한 출력 폴더를 선택할 수 있습니다.
다음 단계
원본 생성기 Cookbook은 이러한 예제 중 일부를 해결하는 몇 가지 권장 방법을 설명합니다. 또한 직접 시도해 볼 수 있는 GitHub에서 사용할 수 있는 샘플 세트도 있습니다.
다음 문서에서 원본 생성기에 대해 자세히 알아볼 수 있습니다.
.NET