.NET CLI를 사용하여 라이브러리 개발

이 문서에서는 .NET CLI를 사용하여 .NET용 라이브러리를 작성하는 방법에 대해 설명합니다. CLI는 지원되는 운영 체제에서 작동하는 효율적인 하위 수준 환경을 제공합니다. Visual Studio로 라이브러리를 빌드할 수 있습니다. 그러한 환경을 선호하는 경우 Visual Studio 설명서를 참조하세요.

필수 조건

컴퓨터에 .NET SDK가 설치되어 있어야 합니다.

.NET Framework 버전을 다루는 이 문서의 섹션에서는 .NET Framework가 설치된 Windows 컴퓨터가 필요합니다.

또한 이전 .NET Framework 대상을 지원하려는 경우 .NET Framework 다운로드 페이지에서 타기팅 팩 또는 개발자 팩을 설치해야 합니다. 다음 표를 참조하세요.

.NET Framework 버전 다운로드할 파일
4.6.1 .NET Framework 4.6.1 타기팅 팩
4.6 .NET framework 4.6 타기팅 팩
4.5.2 .NET framework 4.5.2 개발자 팩
4.5.1 .NET framework 4.5.1 개발자 팩
4.5 Windows 8용 Windows 소프트웨어 개발 키트
4.0 Windows 7 및 .NET Framework 4용 Windows SDK
2.0, 3.0 및 3.5 .NET Framework 3.5 SP1 런타임(또는 Windows 8 이상 버전)

.NET 5+ 또는 .NET Standard를 대상으로 지정하는 방법

프로젝트의 대상 프레임워크를 제어하려면 프로젝트 파일에 해당 값(.csproj 또는 fsproj)을 추가합니다. .NET 5+ 또는 .NET Standard 중에서 대상을 선택하는 방법에 대한 지침은 .NET 5+ 및 .NET Standard를 참조하세요.

<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
    <TargetFramework>net6.0</TargetFramework>
  </PropertyGroup>
</Project>
<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
    <TargetFramework>netstandard2.0</TargetFramework>
  </PropertyGroup>
</Project>

.NET Framework 버전 4.0 이하를 대상으로 하거나 .NET Framework에서는 사용 가능하지만 .NET Standard에서는 사용할 수 없는 API를 사용하려는 경우(예: System.Drawing) 다음 섹션을 읽어보고 멀티 타기팅 방법을 알아보세요.

.NET Framework를 대상으로 지정하는 방법

참고 항목

다음 지침은 컴퓨터에 .NET Framework가 설치된 것으로 가정합니다. 설치된 종속성을 알아보려면 필수 조건을 참조하세요.

여기서 사용된 .NET Framework 버전 중 일부는 더 이상 지원되지 않습니다. 지원되지 않는 버전은 .NET Framework Support Lifecycle Policy FAQ(.NET Framework 지원 수명 주기 정책 FAQ)를 참조하세요.

가장 많은 수의 개발자 및 프로젝트에 도달하려면 기준 대상으로 .NET Framework 4.0을 사용합니다. .NET Framework를 대상으로 하려면 지원할 .NET Framework 버전에 해당하는 올바른 TFM(대상 프레임워크 모니커)을 사용하여 시작해야 합니다.

.NET Framework 버전 TFM
.NET Framework 2.0 net20
.NET Framework 3.0 net30
.NET Framework 3.5 net35
.NET Framework 4.0 net40
.NET Framework 4.5 net45
.NET Framework 4.5.1 net451
.NET Framework 4.5.2 net452
.NET Framework 4.6 net46
.NET Framework 4.6.1 net461
.NET Framework 4.6.2 net462
.NET Framework 4.7 net47
.NET Framework 4.8 net48

그런 다음 이 TFM을 프로젝트 파일의 TargetFramework 섹션에 삽입합니다. 예를 들어 .NET Framework 4.0을 대상으로 하는 라이브러리의 작성 방법은 다음과 같습니다.

<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
    <TargetFramework>net40</TargetFramework>
  </PropertyGroup>
</Project>

정말 간단하죠! 이는 .NET Framework 4에 대해서만 컴파일되지만 이후 버전의 .NET Framework에서 라이브러리를 사용할 수 있습니다.

멀티 타기팅 방법

참고 항목

다음 지침에서는 컴퓨터에 .NET Framework가 설치된 것으로 가정합니다. 설치해야 할 종속성 및 다운로드할 위치에 대해 알아보려면 필수 조건 섹션을 참조하세요.

프로젝트가 .NET Framework 및 .NET을 모두 지원하는 경우 이전 버전의 .NET Framework를 대상으로 해야 할 수 있습니다. 이 시나리오에서, 최신 대상에 최신 API 및 언어 구조를 사용하려는 경우 코드에 #if 지시문을 사용하세요. 각각의 경우에 필요한 API를 포함하기 위해 대상으로 지정하는 각 플랫폼마다 서로 다른 패키지와 종속성을 추가해야 할 수도 있습니다.

예를 들어 HTTP를 통해 네트워킹 작업을 수행하는 라이브러리가 있다고 가정해 보겠습니다. .NET 표준 및 .NET Framework 버전 4.5 이상 경우 System.Net.Http 네임스페이스의 HttpClient 클래스를 사용할 수 있습니다. 그러나 이전 버전의 .NET Framework에는 HttpClient 클래스가 없으므로, 이에 대해 System.Net 네임스페이스의 WebClient 클래스를 사용할 수 있습니다.

프로젝트 파일이 다음과 같이 표시될 수 있습니다.

<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
    <TargetFrameworks>netstandard2.0;net40;net45</TargetFrameworks>
  </PropertyGroup>

  <!-- Need to conditionally bring in references for the .NET Framework 4.0 target -->
  <ItemGroup Condition="'$(TargetFramework)' == 'net40'">
    <Reference Include="System.Net" />
  </ItemGroup>

  <!-- Need to conditionally bring in references for the .NET Framework 4.5 target -->
  <ItemGroup Condition="'$(TargetFramework)' == 'net45'">
    <Reference Include="System.Net.Http" />
    <Reference Include="System.Threading.Tasks" />
  </ItemGroup>
</Project>

여기서 다음 세 가지 주요 변경 내용을 확인할 수 있습니다.

  1. TargetFramework 노드가 TargetFrameworks로 대체되었으며, 세 개의 TFM이 내부에 표시됩니다.
  2. net40 대상에는 .NET Framework 참조 하나를 끌어오는 <ItemGroup> 노드가 있습니다.
  3. net45 대상에는 .NET Framework 참조 두 개를 끌어오는 <ItemGroup> 노드가 있습니다.

전처리기 기호

빌드 시스템은 #if 지시문에 사용된 다음의 전처리기 기호를 인식합니다.

대상 프레임워크 기호 추가 기호
(.NET 5+ SDK에서 사용 가능)
플랫폼 기호(OS별
TFM을 지정할 때만 사용 가능)
.NET Framework NETFRAMEWORK, NET48, NET472, NET471, NET47, NET462, NET461, NET46, NET452, NET451, NET45, NET40, NET35, NET20 NET48_OR_GREATER, NET472_OR_GREATER, NET471_OR_GREATER, NET47_OR_GREATER, NET462_OR_GREATER, NET461_OR_GREATER, NET46_OR_GREATER, NET452_OR_GREATER, NET451_OR_GREATER, NET45_OR_GREATER, NET40_OR_GREATER, NET35_OR_GREATER, NET20_OR_GREATER
.NET Standard NETSTANDARD, NETSTANDARD2_1, NETSTANDARD2_0, NETSTANDARD1_6, NETSTANDARD1_5, NETSTANDARD1_4, NETSTANDARD1_3, NETSTANDARD1_2, NETSTANDARD1_1, NETSTANDARD1_0 NETSTANDARD2_1_OR_GREATER, NETSTANDARD2_0_OR_GREATER, NETSTANDARD1_6_OR_GREATER, NETSTANDARD1_5_OR_GREATER, NETSTANDARD1_4_OR_GREATER, NETSTANDARD1_3_OR_GREATER, NETSTANDARD1_2_OR_GREATER, NETSTANDARD1_1_OR_GREATER, NETSTANDARD1_0_OR_GREATER
.NET 5 이상(및 .NET Core) NET, NET8_0, NET7_0, NET6_0, NET5_0, NETCOREAPP, NETCOREAPP3_1, NETCOREAPP3_0, NETCOREAPP2_2, NETCOREAPP2_1, NETCOREAPP2_0, NETCOREAPP1_1, NETCOREAPP1_0 NET8_0_OR_GREATER, NET7_0_OR_GREATER, NET6_0_OR_GREATER, NET5_0_OR_GREATER, NETCOREAPP3_1_OR_GREATER, NETCOREAPP3_0_OR_GREATER, NETCOREAPP2_2_OR_GREATER, NETCOREAPP2_1_OR_GREATER, NETCOREAPP2_0_OR_GREATER, NETCOREAPP1_1_OR_GREATER, NETCOREAPP1_0_OR_GREATER ANDROID, BROWSER, IOS, MACCATALYST, MACOS, TVOS, WINDOWS,
[OS][version](예를 들어 IOS15_1),
[OS][version]_OR_GREATER(예를 들어 IOS15_1_OR_GREATER)

참고 항목

  • 버전 없는 기호는 대상으로 지정하는 버전과 무관하게 정의됩니다.
  • 버전별 기호는 대상으로 지정하는 버전에 대해서만 정의됩니다.
  • <framework>_OR_GREATER 기호는 대상으로 지정하는 버전과 모든 이전 버전에 대해 정의됩니다. 예를 들어 .NET Framework 2.0을 대상으로 지정하는 경우 NET20, NET20_OR_GREATER, NET11_OR_GREATER, NET10_OR_GREATER 기호가 정의됩니다.
  • NETSTANDARD<x>_<y>_OR_GREATER 기호는 .NET Standard 대상에 대해서만 정의되며 .NET Core 및 .NET Framework와 같은 .NET Standard를 구현하는 대상에는 정의되지 않습니다.
  • 이는 MSBuild TargetFramework 속성NuGet에서 사용되는 대상 프레임워크 모니커(TFM)와 다릅니다.

대상당 조건부 컴파일을 사용하는 예는 다음과 같습니다.

using System;
using System.Text.RegularExpressions;
#if NET40
// This only compiles for the .NET Framework 4 targets
using System.Net;
#else
 // This compiles for all other targets
using System.Net.Http;
using System.Threading.Tasks;
#endif

namespace MultitargetLib
{
    public class Library
    {
#if NET40
        private readonly WebClient _client = new WebClient();
        private readonly object _locker = new object();
#else
        private readonly HttpClient _client = new HttpClient();
#endif

#if NET40
        // .NET Framework 4.0 does not have async/await
        public string GetDotNetCount()
        {
            string url = "https://www.dotnetfoundation.org/";

            var uri = new Uri(url);

            string result = "";

            // Lock here to provide thread-safety.
            lock(_locker)
            {
                result = _client.DownloadString(uri);
            }

            int dotNetCount = Regex.Matches(result, ".NET").Count;

            return $"Dotnet Foundation mentions .NET {dotNetCount} times!";
        }
#else
        // .NET Framework 4.5+ can use async/await!
        public async Task<string> GetDotNetCountAsync()
        {
            string url = "https://www.dotnetfoundation.org/";

            // HttpClient is thread-safe, so no need to explicitly lock here
            var result = await _client.GetStringAsync(url);

            int dotNetCount = Regex.Matches(result, ".NET").Count;

            return $"dotnetfoundation.org mentions .NET {dotNetCount} times in its HTML!";
        }
#endif
    }
}

dotnet build를 사용하여 이 프로젝트를 빌드하면 bin/ 폴더 아래에 다음 3개의 디렉터리가 표시됩니다.

net40/
net45/
netstandard2.0/

각 디렉터리에는 각 대상의 .dll 파일이 들어 있습니다.

.NET에서 라이브러리를 테스트하는 방법

플랫폼 간에 테스트할 수 있는 기능이 중요합니다. 기본적으로 xUnit 또는 MSTest를 사용할 수 있습니다. 둘 다 .NET에서 라이브러리를 유닛 테스트하는 데 적합합니다. 테스트 프로젝트로 솔루션을 설정하는 방법은 솔루션 구조에 따라 달라집니다. 다음 예제에서는 테스트 및 원본 디렉터리가 동일한 최상위 디렉터리에 있다고 가정합니다.

참고 항목

일부 .NET CLI 명령이 사용됩니다. 자세한 내용은 dotnet newdotnet sln을 참조하세요.

  1. 솔루션을 설정합니다. 다음 명령을 사용하여 설정할 수 있습니다.

    mkdir SolutionWithSrcAndTest
    cd SolutionWithSrcAndTest
    dotnet new sln
    dotnet new classlib -o MyProject
    dotnet new xunit -o MyProject.Test
    dotnet sln add MyProject/MyProject.csproj
    dotnet sln add MyProject.Test/MyProject.Test.csproj
    

    그러면 프로젝트가 생성되고 솔루션에서 함께 연결됩니다. SolutionWithSrcAndTest 디렉터리가 다음과 같이 표시됩니다.

    /SolutionWithSrcAndTest
    |__SolutionWithSrcAndTest.sln
    |__MyProject/
    |__MyProject.Test/
    
  2. 테스트 프로젝트의 디렉터리로 이동한 다음 MyProjectMyProject.Test에 대한 참조를 추가합니다.

    cd MyProject.Test
    dotnet add reference ../MyProject/MyProject.csproj
    
  3. 패키지를 복원하고 프로젝트를 빌드합니다.

    dotnet restore
    dotnet build
    
  4. dotnet test 명령을 실행하여 xUnit가 실행되는지 확인합니다. MSTest를 사용하도록 선택한 경우 MSTest 콘솔 실행기가 대신 실행되어야 합니다.

정말 간단하죠! 이제 명령줄 도구를 사용하여 모든 플랫폼 라이브러리를 테스트할 수 있습니다. 이제 모든 것이 설정되어 계속해서 테스트하려는 경우 라이브러리 테스트는 매우 간단합니다.

  1. 라이브러리를 변경합니다.
  2. 명령줄의 테스트 디렉터리에서 dotnet test 명령으로 테스트를 실행합니다.

dotnet test 명령을 호출하면 자동으로 코드가 다시 빌드됩니다.

여러 프로젝트를 사용하는 방법

더 큰 라이브러리의 경우 일반적으로 서로 다른 프로젝트에 기능을 배치해야 합니다.

자연스러운 C# 및 F#에 사용할 수 있는 라이브러리를 빌드하려 한다고 가정해 보겠습니다. 다시 말해 라이브러리의 소비자가 C# 및 F#에 자연스러운 방식으로 라이브러리를 사용합니다. 예를 들어 C#에서 다음과 같이 라이브러리를 사용할 수 있습니다.

using AwesomeLibrary.CSharp;

public Task DoThings(Data data)
{
    var convertResult = await AwesomeLibrary.ConvertAsync(data);
    var result = AwesomeLibrary.Process(convertResult);
    // do something with result
}

F#에서는 다음과 같습니다.

open AwesomeLibrary.FSharp

let doWork data = async {
    let! result = AwesomeLibrary.AsyncConvert data // Uses an F# async function rather than C# async method
    // do something with result
}

이와 같은 사용 시나리오는, 액세스하는 API가 C# 및 F#에 대해 다른 구조를 가지고 있어야 한다는 뜻입니다. 이를 수행하는 일반적인 방법은 Core 프로젝트로 호출하는 API 계층을 정의하는 C# 및 F# 프로젝트에서 라이브러리의 모든 논리를 해당 Core 프로젝트로 팩터링하는 것입니다. 섹션의 나머지 부분에서는 다음 이름을 사용합니다.

  • AwesomeLibrary.Core - 라이브러리에 대한 모든 논리를 포함하는 Core 프로젝트
  • AwesomeLibrary.CSharp - C#에서 사용하기 위한 공용 API가 포함된 프로젝트
  • AwesomeLibrary.FSharp - F#에서 사용하기 위한 공용 API가 포함된 프로젝트

터미널에서 다음 명령을 실행하면 이 가이드와 동일한 구조를 생성할 수 있습니다.

mkdir AwesomeLibrary && cd AwesomeLibrary
dotnet new sln
mkdir AwesomeLibrary.Core && cd AwesomeLibrary.Core && dotnet new classlib
cd ..
mkdir AwesomeLibrary.CSharp && cd AwesomeLibrary.CSharp && dotnet new classlib
cd ..
mkdir AwesomeLibrary.FSharp && cd AwesomeLibrary.FSharp && dotnet new classlib -lang "F#"
cd ..
dotnet sln add AwesomeLibrary.Core/AwesomeLibrary.Core.csproj
dotnet sln add AwesomeLibrary.CSharp/AwesomeLibrary.CSharp.csproj
dotnet sln add AwesomeLibrary.FSharp/AwesomeLibrary.FSharp.fsproj

그러면 위의 세 가지 프로젝트와 프로젝트를 함께 연결하는 솔루션 파일 하나가 추가됩니다. 솔루션 파일을 만들고 프로젝트를 연결하면 최상위 수준에서 프로젝트를 복원하고 빌드할 수 있습니다.

프로젝트 간 참조

프로젝트를 참조하는 가장 좋은 방법은 .NET CLI를 사용하여 프로젝트 참조를 추가하는 것입니다. AwesomeLibrary.CSharpAwesomeLibrary.FSharp 프로젝트 디렉터리에서 다음 명령을 실행할 수 있습니다.

dotnet add reference ../AwesomeLibrary.Core/AwesomeLibrary.Core.csproj

이제 AwesomeLibrary.CSharpAwesomeLibrary.FSharp 둘 다의 프로젝트 파일에서 AwesomeLibrary.CoreProjectReference 대상으로 참조합니다. 프로젝트 파일을 검사하고 파일에서 다음을 통해 이를 확인할 수 있습니다.

<ItemGroup>
  <ProjectReference Include="..\AwesomeLibrary.Core\AwesomeLibrary.Core.csproj" />
</ItemGroup>

.NET CLI를 사용하지 않으려는 경우 이 섹션을 각 프로젝트 파일에 수동으로 추가할 수 있습니다.

솔루션 구성

다중 프로젝트 솔루션의 또 다른 중요한 측면은 전체 프로젝트 구조를 올바르게 설정하는 것입니다. 코드를 원하는 대로 구성할 수 있으며, dotnet sln add를 사용하여 각 프로젝트를 솔루션 파일에 연결하기만 하면 솔루션 수준에서 dotnet restoredotnet build를 실행할 수 있습니다.