Microsoft MVP성태의 닷넷 이야기
글쓴 사람
정성태 (seongtaejeong at gmail.com)
홈페이지
첨부 파일
 

.NET SDK 10 - 단일 소스 코드 파일을 빌드/실행하는 기능을 "dotnet" 명령어에 추가

실행까지는 아니었지만, 과거에도 단일 소스 코드 파일을 컴파일하는 것이 가능했습니다. 일례로, .NET Framework 시절에는 고정된 위치에 프레임워크가 설치됐고, 그 구성 요소에는 C# 컴파일러(csc.exe)도 함께 있었습니다. (msbuild.exe가 프로젝트 단위의 빌드를 담당했다면,) csc.exe는 소스 코드 파일을 컴파일하는 역할을 했는데요, 당연히 소스 코드 파일 1개도 빌드가 가능했습니다.

이후 .NET Core가 등장하면서 초기에 잠깐 csc.exe로 제공되다가 .NET 2.2.2 SDK부터는 csc.exe가 누락되고 아예 csc.dll로 제공되기 시작했습니다. 따라서 단일 소스 코드 파일을 컴파일하려면 dotnet.exe + csc.dll의 조합으로 컴파일해야 했는데요, 관련해서는 다음의 글에 자세하게 설명했으니 참고하시고.

.NET Core/5+ 환경에서 (프로젝트가 아닌) C# 코드 파일을 입력으로 컴파일하는 방법 - 두 번째 이야기
; https://www.sysnet.pe.kr/2/0/13105

어찌 보면, 사실상 .NET 9 환경까지는 단일 소스 코드 파일의 빌드를 공식적으로 지원한다고 보기에는 다소 무리가 있었는데요, 이런 애매함이 .NET 10부터 사라지게 되었습니다. 게다가 방법도 간단해졌는데요, 기존의 dotnet 실행 파일에 그 기능을 통합시켰기 때문입니다.

간단하게 실습을 해볼까요? ^^ 어떤 식으로든 단일 C# 코드 파일을 하나 만든 후, 다음과 같이 "dotnet build"로 전달만 하면,

c:\temp> type test.cs
Console.WriteLine("Hello world");

c:\temp> dotnet build test.cs

컴파일이 됩니다. 문제가 하나 있다면, 저렇게 빌드한 결과물의 위치가 매우 복잡하다는 점입니다.

%USERPROFILE%\AppData\Local\Temp\dotnet\runfile\test-6dee3fbcf5d3d00ed309fd6226803f671db092c17093e603ca20d6880b18d12c\bin\debug\test.dll

// 위의 경로는 빌드 시 -v 옵션을 주면 쉽게 확인할 수 있습니다.
c:\temp> dotnet build -v d test.cs
Restore complete (0.4s)
    All projects are up-to-date for restore.
    info NETSDK1057: You are using a preview version of .NET. See: https://aka.ms/dotnet-support-policy
  test succeeded (0.2s) → C:\Users\testusr\AppData\Local\Temp\dotnet\runfile\test-6dee3fbcf5d3d00ed309fd6226803f671db092c17093e603ca20d6880b18d12c\bin\debug\test.dll

Build succeeded in 1.4s

그래서 보통은 output 디렉터리를 지정해서 빌드하게 될 텐데요,

c:\temp> dotnet build test.cs -o .

c:\temp> dotnet test.dll
Hello world

여기서 더 재미있는 점은, 단일 소스 코드 파일에 대한 빌드뿐만 아니라 실행까지도 dotnet 명령어가 지원한다는 점입니다. 그래서 위의 명령어처럼 2개로 나뉠 필요 없이 곧바로 "dotnet run"으로 실행할 수 있습니다.

c:\temp> dotnet run test.cs
Hello world

이게 뭘 의미하냐면, 컴파일 언어에 속한 C#을 마치 스크립트 언어처럼 다룰 수 있게 되었다는 점입니다. ^^ 심지어 *NIX 환경에서는 이에 대한 특징이 더욱더 잘 드러나는데요, 왜냐하면 소스 코드 파일에 shebang 지시자를 담을 수 있기 때문입니다.

$ cat test.cs
#!/usr/bin/dotnet run

Console.WriteLine("Hello world");

이 상태에서 저 텍스트 파일에 실행 권한만 있다면, 단독으로 실행하는 것도 가능합니다.

$ chmode +x test.cs
$ ./test.cs
Hello world

오호~~~ 나름 괜찮은 기능이 추가된 것 같죠? 이로써 그동안 C#을 스크립트 언어처럼 사용하기 위한 부가 프로젝트들이 더 이상 필요 없게 되었습니다. ^^




그나저나, 기본적인 내용은 저렇게 컴파일과 실행이 가능하다는 건데요, 이게 또 들어가 보면 곁가지들이 좀 나옵니다. 왜냐하면, 그동안 프로젝트 파일(csproj)을 통해 관리를 했던 부분들이 날아갔기 때문에 그 빈자리가 아쉬울 수밖에 없고, 그래서 이에 대한 보완으로 5개 정도의 지시자가 추가됐습니다. (향후, 더 나올 수도 있습니다.)

  • #! (shebang)
  • #:sdk (SDK 설정)
  • #:property (속성 설정)
  • #:package (nuget 패키지 참조)
  • #:project (외부 프로젝트 참조)

첫 번째 shebang 지시자는 이미 위에서 설명했으니 넘어가고, 두 번째 지시자부터 하나씩 알아보겠습니다.


#:sdk 지시자

예를 들어, Console 응용 프로그램과 ASP.NET Core 웹 응용 프로그램의 경우 SDK 유형부터가 다릅니다.

<Project Sdk="Microsoft.NET.Sdk">
    <!-- ...[생략]... -->
</Project>

<Project Sdk="Microsoft.NET.Sdk.Web">
    <!-- ...[생략]... -->
</Project>

따라서, 단일 파일의 경우 저 SDK 유형을 지정할 방법이 필요했고, 그 용도로 "#:sdk" 지시자가 도입된 것입니다. 이를 응용하면 ASP.NET Core 웹 응용 프로그램도 단일 소스 코드 파일로 작성할 수 있습니다.

$ cat test.cs
#!/usr/bin/dotnet run
#:sdk Microsoft.NET.Sdk.Web

var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();

app.MapGet("/", () => "Hello World!");

app.Run();

$ dotnet run test.cs
info: Microsoft.Hosting.Lifetime[14]
      Now listening on: http://localhost:5000
info: Microsoft.Hosting.Lifetime[0]
      Application started. Press Ctrl+C to shut down.
info: Microsoft.Hosting.Lifetime[0]
      Hosting environment: Production
info: Microsoft.Hosting.Lifetime[0]
      Content root path: /home/testusr


#:property 지시자

sdk 유형 외에 프로젝트 파일에서 거의 반드시 등장하는 것이 바로 속성(property) 설정인데요,

<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
    <OutputType>Exe</OutputType>
    <TargetFramework>net8.0</TargetFramework>
    <ImplicitUsings>enable</ImplicitUsings>
    <Nullable>enable</Nullable>
  </PropertyGroup>

</Project>

이것을 소스 코드 파일에 지정이 가능하도록 다음의 유형으로 "#:property" 지시자를 추가할 수 있습니다.

#:property [속성]=[값]

이에 대한 예제로, WinRT API를 사용하는 단일 소스 코드 파일을 이렇게 작성할 수 있습니다.

#:sdk Microsoft.NET.Sdk.WindowsDesktop
#:property TargetFramework=net10.0-windows10.0.17763.0

using Windows.Devices.Enumeration;

// 이 코드는 WinRT를 사용하므로 *NIX 환경에선 동작하지 않습니다.

using Windows.Devices.Enumeration;

// https://www.sysnet.pe.kr/2/0/11284
await ListDevices();

static async Task ListDevices()
{
    var devices = await DeviceInformation.FindAllAsync(DeviceClass.VideoCapture);

    foreach (var item in devices)
    {
        Console.WriteLine($"{item.Id}: {item.Name}");
    }
}


#:package 지시자

그다음 프로젝트 파일의 역할 중 가장 중요한 것으로는 패키지 참조가 있는데요, 이에 대응해서는 "#:package" 지시자를 제공합니다.

#:package [패키지_이름]@[버전]

nuget에서 제공하는 패키지를 참조할 수 있게 해주는데, 아래의 예제는 Newtonsoft.Json 13.0.3 버전을 참조하는 단일 소스 코드 파일을 보여줍니다.

$ cat test.cs
#:package Newtonsoft.Json@13.0.3

using Newtonsoft.Json;

Product product = new Product();

product.Name = "Apple";
product.Price = 3.99M;

string output = JsonConvert.SerializeObject(product);
Console.WriteLine(output);

class Product
{
    public string Name { get; set; }
    public decimal Price { get; set; }
}

$ dotnet run test.cs
{"Name":"Apple","Price":3.99}


#:project 지시자

마지막으로, nuget 패키지뿐만 아니라 로컬의 프로젝트 참조도 가능하게 해주는 "#:project" 지시자가 있습니다. 예를 들어, 파일/디렉터리 구조가 다음과 같을 때,

c:\temp> tree /F
Folder PATH listing
Volume serial number is 2E94-0194
C:.
│   test.cs
│
└───ClassLibrary1
        Class1.cs
        ClassLibrary1.csproj

test.cs 파일에서 ClassLibrary1.csproj의 경로를 project 지시자로 참조해 실행할 수 있습니다.

c:\temp> type test.cs
#:project .\ClassLibrary1\ClassLibrary1.csproj

ClassLibrary1.Class1.HelloWorld();

c:\temp> type .\ClassLibrary1\Class1.cs
namespace ClassLibrary1;

public class Class1
{
    public static void HelloWorld()
    {
        System.Console.WriteLine("Hello world");
    }
}

c:\temp> dotnet run test.cs
Hello world




정리하면, "dotnet build", "dotnet run" 명령어의 인자로 단일 C# 소스 코드 파일을 지정할 수 있게 되었고, 이로 인해 5개의 지시자("#:", "#:sdk", "#:property", "#:package", "#:project")를 추가해 프로젝트 파일의 기능을 일부 대체했습니다.

그런데 혹시나, 프로젝트 파일의 특정 기능이 필요한 상황이 있을까요? 그런 경우의 대안으로 Directory.Build.props 파일 역시 유효하므로 현재는 아쉬운 대로 이것을 활용하시면 됩니다.

(참고로 글을 쓰면서 느낀 건데, 이제 블로그에서의 웬만한 예제는 단일 파일 기준으로 작성해도 될 것 같습니다. ^^)




[이 글에 대해서 여러분들과 의견을 공유하고 싶습니다. 틀리거나 미흡한 부분 또는 의문 사항이 있으시면 언제든 댓글 남겨주십시오.]







[최초 등록일: ]
[최종 수정일: 8/19/2025]

Creative Commons License
이 저작물은 크리에이티브 커먼즈 코리아 저작자표시-비영리-변경금지 2.0 대한민국 라이센스에 따라 이용하실 수 있습니다.
by SeongTae Jeong, mailto:techsharer at outlook.com

비밀번호

댓글 작성자
 




... 166  [167]  168  169  170  171  172  173  174  175  176  177  178  179  180  ...
NoWriterDateCnt.TitleFile(s)
948정성태11/16/201039913.NET Framework: 190. 트위터 계정으로 로그인하는 C# 웹 사이트 제작 [7]파일 다운로드1
947정성태11/14/201043670.NET Framework: 189. Mono Cecil로 만들어 보는 .NET Decompiler [1]파일 다운로드1
946정성태11/11/201044442.NET Framework: 188. .NET 64비트 응용 프로그램에서 왜 (2GB) OutOfMemoryException 예외가 발생할까? [1]파일 다운로드1
945정성태11/11/201027797VC++: 44. C++/CLI 컴파일 오류 - error C4368: mixed types are not supported
944정성태11/11/201034743VC++: 43. C++/CLI 컴파일 오류 - error C2872: 'IServiceProvider' : ambiguous symbol could be ...
943정성태11/8/201033813디버깅 기술: 30. windbg ".loadby sos" 명령어 [2]
942정성태11/7/201046450.NET Framework: 187. 실행 시에 메서드 가로채기 - CLR Injection: Runtime Method Replacer 개선 [7]파일 다운로드3
941정성태11/6/201028069.NET Framework: 186. windbg로 확인하는 .NET CLR LCG 메서드(DynamicMethod) [1]파일 다운로드1
940정성태11/6/201029416.NET Framework: 185. windbg로 확인하는 .NET CLR 메서드파일 다운로드1
939정성태10/24/201035466Windows: 51. RDP로 접속한 Windows Server 2008 R2 환경에서 Aero Glass 활성화 [1]
938정성태10/23/201028110디버깅 기술: 29. Windbg - Hyper-V 윈도우 7 원격 디버깅 구성 [1]
937정성태10/22/201033847DDK: 5. NT Legacy 드라이버: 프로세스(EXE) 생성/제거 모니터링 [3]파일 다운로드1
936정성태10/21/201032907DDK: 4. Device Driver 응용 프로그램의 빌드 스크립트 - 두 번째 이야기 [2]파일 다운로드1
935정성태10/17/201033723디버깅 기술: 28. Windbg - 윈도우 핸들 테이블 [3]
934정성태10/11/201036840디버깅 기술: 27. Windbg - Local Kernel Debug 모드 [2]
933정성태10/10/201028759.NET Framework: 184. 닷넷에서 호출 스택의 메서드에 대한 인자 값 확인이 가능할까? [2]파일 다운로드1
932정성태10/10/201032101DDK: 3. NT Legacy 드라이버를 이용하여 C#에서 Port 입출력파일 다운로드1
931정성태9/30/201026696오류 유형: 108. Net.Tcp Listener Adapter 서비스 시작 실패
930정성태9/30/201025739웹: 16. 윈도우 미디어 플레이어 - 일시 정지/시작을 스크립트에서 감지
929정성태9/17/201027108웹: 15. IE 9 - 작업 표시줄의 웹 사이트 바로가기 사용자 정의 - JumpLists [3]파일 다운로드1
928정성태9/16/201031164VC++: 42. 쓰기 전용 파일(예: 로그 파일)의 동기화 방법파일 다운로드1
927정성태9/14/201041188VC++: 41. UTF-8 포맷의 INI 파일에 대한 GetPrivateProfile... API 사용 불가 [2]
926정성태9/9/201030591Team Foundation Server: 41. 빌드 스크립트에 Code Coverage 추가 [1]파일 다운로드1
925정성태9/8/201034297Team Foundation Server: 40. Visual Studio 2010 - Code Coverage 결과를 외부 XML 파일로 출력하는 명령행 도구 제작 [1]파일 다운로드1
924정성태9/6/201024278개발 환경 구성: 88. SCVMM이 설치된 도메인에 참여하지 않은 Hyper-V 호스트 추가
923정성태9/5/201030768오류 유형: 107. SCVMM Agent 설치 오류 - Failed to configure the WS-Management service.
... 166  [167]  168  169  170  171  172  173  174  175  176  177  178  179  180  ...