Ver5.0 ๐ฆ .NET ๋ผ์ด๋ธ๋ฌ๋ฆฌ ๊ฐ๋ฐ๊ณผ NuGet ํจํค์ง ๋ฐฐํฌ ๊ด๋ฆฌ ์์คํ ์์ ์ ๋ณต

๐ฆ .NET ๋ผ์ด๋ธ๋ฌ๋ฆฌ ๊ฐ๋ฐ๊ณผ NuGet ํจํค์ง ๋ฐฐํฌ ๊ด๋ฆฌ ์์คํ ์์ ์ ๋ณต
"์ด ์ ํธ ํด๋์ค, ํ๋ก์ ํธ๋ง๋ค ๋ณต๋ถํ๊ณ ์์ง ์์?" ๐
๋ง์, ๊ทธ ์ฐ์ฐํ ๋๋. ๋ฒ๊ทธ ํ๋ ๊ณ ์น๋ฉด ๋ค์ฏ ๊ตฐ๋ฐ๋ฅผ ์ฐพ์๋ค๋๋ฉฐ ๊ณ ์ณ์ผ ํ๋ ๊ทธ ์ง์ฅ ๋ง์ด์ผ.
์ค๋์ ๊ทธ ์ฝ๋๋ฅผ ์ ๋๋ก ๋ .NET ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ก ๋ง๋ค๊ณ , NuGet ํจํค์ง๋ก ํฌ์ฅํด์, ์๋์ผ๋ก ๋ฐฐํฌยท๊ด๋ฆฌํ๋ ์์คํ
๊น์ง ํ ๋ฒ์ ํ์ด๋ณผ ๊ฑฐ์ผ. ์น๊ตฌํํ
์ค๋ช
ํ๋ฏ ํธํ๊ฒ ๊ฐ ํ
๋ ์ปคํผ ํ ์ ๋ค๊ณ ๋ฐ๋ผ์ โ
1. NuGet์ด ๋์ฒด ๋ญ๋ฐ? ๐ค
NuGet์ .NET ์ํ๊ณ์ ๊ณต์ ํจํค์ง ๊ด๋ฆฌ์์ผ. JavaScript์ npm, Python์ pip๊ฐ ์๋ค๋ฉด .NET์ NuGet์ด ์๋ ๊ฑฐ์ง. 2010๋
๊ฒฝ ์ฒ์ ๋ฑ์ฅํ๊ณ , ์ง๊ธ์ Visual Studio์ .NET SDK(dotnet CLI)์ ๊ธฐ๋ณธ์ผ๋ก ๋
น์ ์์ด.
ํต์ฌ์ ์์ฃผ ๋จ์ํด. NuGet ํจํค์ง(.nupkg)๋ ์ฌ์ค ZIP ํ์ผ์ด์ผ. ์ง์ง๋ก ํ์ฅ์๋ฅผ .zip์ผ๋ก ๋ฐ๊ฟ์ ์ด์ด๋ณด๋ฉด ์์ ์ปดํ์ผ๋ DLL, ๋ฉํ๋ฐ์ดํฐ๊ฐ ๋ด๊ธด .nuspec ํ์ผ, README, ์์ด์ฝ ๋ฑ์ด ๋ค์ด ์์ด. ๋ณ๊ฑฐ ์์ด ๋ณด์ด์ง? ๊ทผ๋ฐ ์ด "๋ณ๊ฑฐ ์๋ ZIP"์ด ๋ฒ์ ๊ด๋ฆฌ, ์์กด์ฑ ํด์, ํ๊น ํ๋ ์์ํฌ ์ ํ๊น์ง ๋ค ํด์ฃผ๋๊น ์์ฒญ๋ ๊ฑฐ์ผ.
์ฐธ๊ณ ๋ก nuget.org์๋ ์์ญ๋ง ๊ฐ์ ๊ณ ์ ํจํค์ง๊ฐ ๋ฑ๋ก๋ผ ์๊ณ , ๋์ ๋ค์ด๋ก๋๋ ์์ฒ์ต ํ ์์ค์ด์ผ. ์ฐ๋ฆฌ๊ฐ ๋งค์ผ ์ฐ๋ Newtonsoft.Json, Serilog, Dapper, xunit ๋ค ์ฌ๊ธฐ์ ๋ด๋ ค๋ฐ๋ ๊ฑฐ์ผ.
2. ๋ผ์ด๋ธ๋ฌ๋ฆฌ ํ๋ก์ ํธ, ์ฒซ ๋จ์ถ๋ถํฐ ์ ๋๋ก ๐งต
๋จผ์ ํด๋์ค ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ๋ง๋ค์ด ๋ณด์. CLI ํ ์ค์ด๋ฉด ๋๋.
dotnet new classlib -n Jaenung.StringKit
cd Jaenung.StringKit
๊ทธ๋ผ SDK ์คํ์ผ .csproj๊ฐ ์๊ฒจ. ์์ .NET Framework ์์ ์ ์ฅํฉํ csproj์ ๋ฌ๋ฆฌ ์์ฒญ ์งง์์ก์ง. ์ฌ๊ธฐ์ ํจํค์ง ์ ๋ณด๋ฅผ ๋ฐ๋ก ์ ์ ์ ์๋ค๋ ๊ฒ ํฌ์ธํธ์ผ. ์์ ์ฒ๋ผ .nuspec์ ๋ฐ๋ก ์์ผ๋ก ์ธ ํ์๊ฐ ๊ฑฐ์ ์์ด์ก์ด.
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFrameworks>netstandard2.0;net8.0</TargetFrameworks>
<LangVersion>latest</LangVersion>
<Nullable>enable</Nullable>
<!-- ํจํค์ง ๋ฉํ๋ฐ์ดํฐ -->
<PackageId>Jaenung.StringKit</PackageId>
<Version>1.0.0</Version>
<Authors>YourName</Authors>
<Description>๋ฌธ์์ด ์ฒ๋ฆฌ๋ฅผ ํธํ๊ฒ ํด์ฃผ๋ ์ ํธ๋ฆฌํฐ ๋ชจ์</Description>
<PackageTags>string;utility;korean</PackageTags>
<PackageLicenseExpression>MIT</PackageLicenseExpression>
<PackageReadmeFile>README.md</PackageReadmeFile>
<PackageIcon>icon.png</PackageIcon>
<RepositoryUrl>https://github.com/you/StringKit</RepositoryUrl>
<!-- ํ์ง ์ต์
-->
<GenerateDocumentationFile>true</GenerateDocumentationFile>
<IncludeSymbols>true</IncludeSymbols>
<SymbolPackageFormat>snupkg</SymbolPackageFormat>
</PropertyGroup>
<ItemGroup>
<None Include="README.md" Pack="true" PackagePath="\" />
<None Include="icon.png" Pack="true" PackagePath="\" />
</ItemGroup>
</Project>
๐ก ์ ๊น, ๊ฐ๊ฐ ๋ญ ํ๋ ์ ๋ค์ด์ผ?
โข TargetFrameworks (๋ณต์ํ s ์ฃผ์!): ์ฌ๋ฌ ํ๋ ์์ํฌ์ฉ DLL์ ํ ํจํค์ง์ ๋ด๋ ๋ฉํฐ ํ๊ธฐํ
.
โข PackageLicenseExpression: SPDX ๋ผ์ด์ ์ค ์๋ณ์. ์์ licenseUrl ๋ฐฉ์์ deprecated ๋์ด.
โข PackageReadmeFile: nuget.org ํจํค์ง ํ์ด์ง์ README๊ฐ ์์๊ฒ ๋ณด์ฌ.
โข GenerateDocumentationFile: XML ์ฃผ์์ ํ์ผ๋ก ๋ฝ์์, ์ฌ์ฉ์๊ฐ IntelliSense๋ก ์ค๋ช
์ ๋ณผ ์ ์๊ฒ ํด์ค.
โข snupkg: ๋๋ฒ๊น
์ฉ ์ฌ๋ณผ ํจํค์ง. nuget.org ์ฌ๋ณผ ์๋ฒ์ ํจ๊ป ์ฌ๋ผ๊ฐ.
3. ํ๊น ํ๋ ์์ํฌ, ๋ญ ๊ณจ๋ผ์ผ ํด? ๐ฏ
๋ผ์ด๋ธ๋ฌ๋ฆฌ ๊ฐ๋ฐ์๊ฐ ์ ์ผ ๋ง์ด ๊ณ ๋ฏผํ๋ ๋ถ๋ถ์ด์ผ. "netstandard2.0? net8.0? ๋ ๋ค?"
| ํ๊น | ์ธ ์ ์๋ ๊ณณ | ์ธ์ ๊ณ ๋ฅผ๊น |
|---|---|---|
| netstandard2.0 | .NET Framework 4.6.1+, .NET Core 2.0+, .NET 5+, Unity, Xamarin ๋ฑ | ์ต๋ํ ๋์ ํธํ์ฑ์ด ํ์ํ ๋. ์ฌ์ ํ "๊ตญ๋ฏผ ํ๊น" |
| netstandard2.1 | .NET Core 3.0+, .NET 5+ (.NET Framework๋ ๋ฏธ์ง์) | Span ๋ฑ ์ผ๋ถ API๊ฐ ํ์ํ์ง๋ง Framework๋ ๋ฒ๋ ค๋ ๋ ๋ |
| net8.0 / net9.0 ๋ฑ | ํด๋น ๋ฒ์ ์ด์์ ์ต์ .NET | ์ต์ APIยท์ฑ๋ฅ ์ต์ ํ(ํธ๋ฆฌ๋ฐ, AOT ๋ฑ)๋ฅผ ์ฐ๊ณ ์ถ์ ๋ |
๋ง์ดํฌ๋ก์ํํธ ๊ณต์ ๋ผ์ด๋ธ๋ฌ๋ฆฌ ๊ฐ์ด๋์ ๊ถ์ฅ์ ๋๋ต ์ด๋: .NET Framework ์ฌ์ฉ์๊น์ง ํ๊ณ ์ถ์ผ๋ฉด netstandard2.0์ ํฌํจํ๊ณ , ์ต์ ๊ธฐ๋ฅ์ด ํ์ํ๋ฉด net8.0 ๊ฐ์ ์ต์ ํ๊น์ ๊ฐ์ด ๋ฃ์ด๋ผ. ์ฆ ๋ฉํฐ ํ๊ธฐํ ์ด ์ ์์ด์ผ. NuGet์ ์ค์นํ๋ ์ชฝ ํ๋ก์ ํธ์ ๊ฐ์ฅ ์ ๋ง๋ DLL์ ์๋์ผ๋ก ๊ณจ๋ผ์ค.
ํ๊น๋ณ๋ก ์ฝ๋๋ฅผ ๋ค๋ฅด๊ฒ ํ๊ณ ์ถ์ผ๋ฉด ์ ์ฒ๋ฆฌ๊ธฐ ๊ธฐํธ๋ฅผ ์ฐ๋ฉด ๋ผ.
public static int CountChars(string text, char target)
{
#if NET8_0_OR_GREATER
return text.AsSpan().Count(target); // ์ต์ API
#else
int n = 0;
foreach (var c in text) if (c == target) n++;
return n;
#endif
}
โ ๏ธ ์ฃผ์! ์ฐธ๊ณ ๋ก .NET Core 3.1, .NET 5, .NET 6, .NET 7์ ์ด๋ฏธ ์ง์์ด ์ข ๋ฃ๋์ด. ์ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ์์ํ๋ค๋ฉด ํ์ฌ LTS ๋ฒ์ (.NET 8, ๊ทธ๋ฆฌ๊ณ 2025๋ 11์ ์ถ์๋ .NET 10)์ ๊ธฐ์ค์ผ๋ก ์๊ฐํ๋ ๊ฒ ์ข์. ์ง์ ์ข ๋ฃ๋ ํ๋ ์์ํฌ๋ฅผ ํ๊นํ๋ฉด ๋น๋ ์ ๊ฒฝ๊ณ ๊ฐ ๋จ๊ธฐ๋ ํด.
4. ๋ฒ์ ๋ฒํธ, ๋์ถฉ ์ฌ๋ฆฌ๋ฉด ํฐ์ผ ๋ ๐ข
NuGet์ SemVer 2.0(์๋งจํฑ ๋ฒ์ ๋)์ ๋ฐ๋ผ. MAJOR.MINOR.PATCH ํ์์ด๊ณ ๊ท์น์ ์ด๋.
์ฌ๊ธฐ์ ๋ง์ด๋ค ํท๊ฐ๋ฆฌ๋ ๊ฒ .NET์ ๋ฒ์ ์ด ์ฌ๋ฌ ๊ฐ ์๋ค๋ ๊ฑฐ์ผ.
| ์์ฑ | ์๋ฏธ | ํ |
|---|---|---|
| PackageVersion (Version) | NuGet ํจํค์ง ๋ฒ์ | SemVer ๊ทธ๋๋ก. ์ฌ์ฉ์๊ฐ ๋ณด๋ ๋ฒ์ |
| AssemblyVersion | CLR์ด ๋ฐ์ธ๋ฉํ ๋ ์ฐ๋ ๊ฐ๋ ฅํ ๋ฒ์ | .NET Framework์์ ๋ฐ๊พธ๋ฉด ๋ฐ์ธ๋ฉ ๋ฆฌ๋๋ ์
์ง์ฅ์ด ์ฌ ์ ์์ด. ๋ณดํต 2.0.0.0์ฒ๋ผ MAJOR๋ง ๋ฐ์ |
| FileVersion | ํ์ผ ์์ฑ์ ๋ณด์ด๋ ๋ฒ์ | ๋น๋๋ง๋ค ๋ฐ๊ฟ๋ ๋ฌด๋ฐฉ |
| InformationalVersion | ์ฌ๋์ด ์ฝ๋ ์์ธ ๋ฒ์ | ์ปค๋ฐ ํด์ ๋ฑ์ ๋ถ์ด๊ธฐ ์ข์ (์ต์ SDK๋ ์๋์ผ๋ก ๋ถ์ด๊ธฐ๋ ํด) |
์ฌ์ฉํ๋ ์ชฝ์์๋ ๋ฒ์ ๋ฒ์๋ ์ง์ ํ ์ ์์ด. Version="1.2.0"์ ์ฌ์ค "1.2.0 ์ด์"์ด๋ผ๋ ๋ป์ด์ผ(์ต์ ๋ฒ์ ). ์ ํํ ๊ณ ์ ํ๋ ค๋ฉด [1.2.0], ๋ฒ์๋ [1.0,2.0)์ฒ๋ผ ์ํ ๊ตฌ๊ฐ ํ๊ธฐ๋ฅผ ์จ. ๊ทธ๋ฆฌ๊ณ NuGet์ ์์กด์ฑ ํด์ ์ ์กฐ๊ฑด์ ๋ง์กฑํ๋ ๊ฐ์ฅ ๋ฎ์ ๋ฒ์ ์ ๊ณ ๋ฅด๋ ๊ฒ ๊ธฐ๋ณธ ๊ท์น์ด์ผ. npm์ฒ๋ผ "์ต์ ๊ฑธ๋ก ์ค๊ฒ~" ๋ฐฉ์์ด ์๋๋ผ์, ์์์น ๋ชปํ ์
๊ทธ๋ ์ด๋๊ฐ ๋ ์ผ์ด๋.
5. ์ข์ ๋ผ์ด๋ธ๋ฌ๋ฆฌ์ ํ๊ฒฉ โจ
ํจํค์ง๋ฅผ ๋ง๋๋ ๊ฑด ์ฌ์. ๊ทผ๋ฐ "๋จ๋ค์ด ๋ฏฟ๊ณ ์ฐ๋" ํจํค์ง๋ฅผ ๋ง๋๋ ๊ฑด ์ข ๋ค๋ฅธ ์๊ธฐ์ผ. ๊ผญ ์ฑ๊ธธ ๊ฒ๋ค ์ ๋ฆฌํด๋ณผ๊ฒ.
ํ ๋ฒ public์ผ๋ก ๋ด๋ณด๋ธ ๊ฑด ์ฌ์ค์ "์ฝ์"์ด์ผ. ์ง์ฐ๊ฑฐ๋ ์๊ทธ๋์ฒ๋ฅผ ๋ฐ๊พธ๋ฉด MAJOR๋ฅผ ์ฌ๋ ค์ผ ํด. ๋ด๋ถ์ฉ์
internal๋ก ๊ฝ๊ฝ ์จ๊ธฐ์. ํ
์คํธ์์ ์จ์ผ ํ๋ฉด InternalsVisibleTo๋ฅผ ์ฐ๋ฉด ๋ผ.<EnablePackageValidation>true</EnablePackageValidation>์ PackageValidationBaselineVersion์ ์ค์ ํ๋ฉด, ์ด์ ๋ฒ์ ๋๋น ํธํ์ฑ์ ๊นจ๋ ๋ณ๊ฒฝ์ pack ๋จ๊ณ์์ ์ก์์ค. ์ค์๋ก ๋ฉ์๋ ํ๋ ์ง์ ์ ๋ ๊ตฌ์ธ์ฃผ์ผ./// <summary>๋ ์ฌ์ฉ์ IntelliSense์ ๊ทธ๋๋ก ๋ . ์ด๊ฒ ์ฌ์ค์ 1์ฐจ ๋งค๋ด์ผ์ด์ผ.์ฌ์ฉ์๊ฐ ๋๋ฒ๊น ํ ๋ ๋ค GitHub ์์ค๋ก ๋ฐ๋ก ๋ค์ด๊ฐ ์ ์๊ฒ ํด์ค. .NET 8 SDK๋ถํฐ๋ GitHubยทAzure Repos ๋ฑ ์ฃผ์ ํธ์คํธ์ ๋ํด Source Link๊ฐ ๊ธฐ๋ณธ ํฌํจ๋ผ์ ๋ฐ๋ก ํจํค์ง๋ฅผ ์ ๋ฃ์ด๋ ๋ผ.
๊ฐ์ ์์ค๋ฉด ๊ฐ์ ๋ฐ์ด๋๋ฆฌ๊ฐ ๋์ค๊ฒ. CI์์๋
ContinuousIntegrationBuild=true๋ฅผ ์ผ์ ๋ก์ปฌ ๊ฒฝ๋ก ๊ฐ์ ์ ๋ณด๊ฐ ์์ด์ง ์๊ฒ ํด.๋ผ์ด๋ธ๋ฌ๋ฆฌ๊ฐ ๋ฌด๊ฑฐ์ด ์์กด์ฑ์ ๋๊ณ ์ค๋ฉด ์ฌ์ฉ์๋ "๋ค์ด์๋ชฌ๋ ์์กด์ฑ" ์ถฉ๋์ ์๋ฌ๋ ค. ๊ผญ ํ์ํ ๊ฒ๋ง, ๊ทธ๋ฆฌ๊ณ ๋ฒ์๋ ๋๊ทธ๋ฝ๊ฒ.
Package Validation ์ค์ ์์๋ ์ด๋.
<PropertyGroup>
<EnablePackageValidation>true</EnablePackageValidation>
<PackageValidationBaselineVersion>1.0.0</PackageValidationBaselineVersion>
<ContinuousIntegrationBuild Condition="'$(GITHUB_ACTIONS)' == 'true'">true</ContinuousIntegrationBuild>
</PropertyGroup>
๐ฑ ๊ฐ๋ฐ์ ๊ฟํ
Microsoft.CodeAnalysis.PublicApiAnalyzers ํจํค์ง๋ฅผ ์ฐ๋ฉด ๊ณต๊ฐ API ๋ชฉ๋ก์ PublicAPI.Shipped.txt ํ์ผ๋ก ๊ด๋ฆฌํ ์ ์์ด. API๊ฐ ๋ฐ๋๋ฉด ๊ฒฝ๊ณ ๊ฐ ๋จ๋๊น, ์ฝ๋ ๋ฆฌ๋ทฐ์์ "์ด? ์ด๊ฑฐ ๊ณต๊ฐ API ๋ฐ๋์๋ค?"๋ฅผ ์์ฐ์ค๋ฝ๊ฒ ์ก์ ์ ์์ง. ๋ํ ์คํ์์ค๋ค์ด ์ ์ฉํ๋ ๋ฐฉ๋ฒ์ด์ผ.
6. ํฌ์ฅํ๊ณ ๋ง๋ณด๊ธฐ: pack๊ณผ ๋ก์ปฌ ํ ์คํธ ๐ฑ
์ด์ ์ง์ง ํฌ์ฅํด๋ณด์.
dotnet pack -c Release -o ./artifacts
๊ทธ๋ผ artifacts ํด๋์ Jaenung.StringKit.1.0.0.nupkg์ .snupkg๊ฐ ์๊ฒจ. ๋ฐ๋ก nuget.org์ ์ฌ๋ฆฌ๊ณ ์ถ๊ฒ ์ง๋ง ์ ๊น! ๐ nuget.org๋ ๊ฐ์ IDยท๋ฒ์ ์ ๋ค์ ์ฌ๋ฆด ์ ์์ด. ์ค์ํ๋ฉด ๋ฒ์ ์ ์๋ก ์ฌ๋ ค์ผ ํด. ๊ทธ๋ฌ๋๊น ๋ก์ปฌ์์ ๋จผ์ ๋ง์ ๋ณด์.
# ๋ก์ปฌ ํด๋๋ฅผ ํผ๋๋ก ๋ฑ๋ก
dotnet nuget add source ./artifacts -n LocalTest
# ํ
์คํธ ํ๋ก์ ํธ์์ ์ค์นํด๋ณด๊ธฐ
dotnet add package Jaenung.StringKit --version 1.0.0
๊ทธ๋ฆฌ๊ณ NuGet Package Explorer(์คํ์์ค GUI ๋๊ตฌ)๋ก nupkg๋ฅผ ์ด์ด๋ณด๋ฉด ์ด๋ค ํ์ผ์ด ์ด๋ค ํด๋(lib/netstandard2.0, lib/net8.0)์ ๋ค์ด๊ฐ๋์ง, README๋ ์์ด์ฝ์ด ์ ๋ค์ด๊ฐ๋์ง ๋์ผ๋ก ํ์ธํ ์ ์์ด. ์ฒซ ๋ฐฐํฌ ์ ์ ๊ผญ ํด๋ด.
โ ๏ธ ๋ก์ปฌ ์บ์ ํจ์
๊ฐ์ ๋ฒ์ ์ผ๋ก ๋ค์ packํด์ ํ
์คํธํ๋ฉด NuGet์ด ๊ธ๋ก๋ฒ ์บ์(~/.nuget/packages)์ ์๋ ์๋ ๊ฑธ ์จ๋ฒ๋ ค์ "์ด? ๊ณ ์ณค๋๋ฐ ์ ์ ๋ฐ๋์ง?" ์ํฉ์ด ์๊ฒจ. ์ด๋ด ๋ dotnet nuget locals all --clear๋ก ์บ์๋ฅผ ๋น์ฐ๊ฑฐ๋, ํ
์คํธ ๋ฒ์ ์ 1.0.1-local.3์ฒ๋ผ ๊ณ์ ์ฌ๋ ค.
7. ๋ฐฐํฌ์ ๋ฌด๋: ์ด๋์ ์ฌ๋ฆด๊น? ๐ช
ํ์ฌ์์๋ ๋ณดํต "์ฌ๋ด ๊ณตํต ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ ๋น๊ณต๊ฐ ํผ๋์, ์ธ๋ถ ์คํ์์ค๋ nuget.org์์"๋ผ๋ ๊ตฌ์กฐ๋ฅผ ์จ. ์ด๋ ์ฌ๋ฌ ํผ๋๋ฅผ ์์ด ์ฐ๊ฒ ๋๋๋ฐ, ์ฌ๊ธฐ์ ๋ฌด์์ด ๋ณด์ ์ด์๊ฐ ํ๋ ์์ด.
๐ ์์กด์ฑ ํผ๋(Dependency Confusion) ๊ณต๊ฒฉ
2021๋
๋ณด์ ์ฐ๊ตฌ์ Alex Birsan์ด ๊ณต๊ฐํด์ ํ์ ๊ฐ ๋ ๊ณต๊ฒฉ์ด์ผ. ์ฌ๋ด ํจํค์ง ์ด๋ฆ์ด MyCorp.Auth์ธ๋ฐ, ๋๊ตฐ๊ฐ nuget.org(๊ณต๊ฐ ์ ์ฅ์)์ ๊ฐ์ ์ด๋ฆ์ผ๋ก ๋ ๋์ ๋ฒ์ ์ ์
์ฑ ํจํค์ง๋ฅผ ์ฌ๋ฆฌ๋ฉด? ์ค์ ์ด ํ์ ํ ๋น๋ ์๋ฒ๊ฐ ๊ณต๊ฐ ์ชฝ ๊ฑธ ๋ฐ์๋ฒ๋ฆด ์ ์์ด. ๐ฑ
NuGet์ ๋์์ฑ ์ ๋ ๊ฐ์ง์ผ.
โ ํจํค์ง ์์ค ๋งคํ (Package Source Mapping) - NuGet 6.0๋ถํฐ ์ง์. "์ด๋ค ํจํค์ง ์ด๋ฆ ํจํด์ ์ด๋ค ํผ๋์์๋ง ๋ฐ๋๋ค"๋ฅผ ๋ช ์ํด.
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<packageSources>
<clear />
<add key="nuget.org" value="https://api.nuget.org/v3/index.json" />
<add key="company" value="https://pkgs.dev.azure.com/mycorp/_packaging/main/nuget/v3/index.json" />
</packageSources>
<packageSourceMapping>
<packageSource key="company">
<package pattern="MyCorp.*" />
</packageSource>
<packageSource key="nuget.org">
<package pattern="*" />
</packageSource>
</packageSourceMapping>
</configuration>
๋ ๊ตฌ์ฒด์ ์ธ ํจํด(MyCorp.*)์ด ์ฐ์ ์ ์ฉ๋๋๊น, MyCorp๋ก ์์ํ๋ ํจํค์ง๋ ๋ฌด์กฐ๊ฑด ์ฌ๋ด ํผ๋์์๋ง ์ค๊ฒ ๋ผ. ์ด nuget.config๋ฅผ ์ ์ฅ์ ๋ฃจํธ์ ์ปค๋ฐํด๋๋ฉด ํ ์ ์ฒด๊ฐ ๊ฐ์ ๊ท์น์ ๋ฐ๋ผ.
โก ID ์ ๋์ฌ ์์ฝ (Prefix Reservation) - nuget.org์์ MyCorp. ๊ฐ์ ์ ๋์ฌ๋ฅผ ์ ์ฒญํด ์์ฝํ๋ฉด ๋จ์ด ๊ทธ ์ ๋์ฌ๋ก ํจํค์ง๋ฅผ ๋ชป ์ฌ๋ ค. ์์ฝ๋ ์ ๋์ฌ ํจํค์ง์๋ ํ๋ ์ฒดํฌ ํ์๋ ๋ถ์ด์ ์ ๋ขฐ๋๋ ์ฌ๋ผ๊ฐ์ง.
8. ํจํค์ง ๊ด๋ฆฌ ์์คํ : ์ค์์์ ๋ฒ์ ๋ค์ค๋ฆฌ๊ธฐ ๐๏ธ
ํ๋ก์ ํธ๊ฐ 30๊ฐ์ฏค ๋๋ ์๋ฃจ์
์์ Serilog ๋ฒ์ ์ด ํ๋ก์ ํธ๋ง๋ค ์ ๊ฐ๊ฐ์ด๋ฉด? ๊ทธ๊ฑฐ ์ง์ง ์
๋ชฝ์ด์ผ. ๊ทธ๋์ ๋์จ ๊ฒ ์ค์ ํจํค์ง ๊ด๋ฆฌ(Central Package Management, CPM)์ผ. NuGet 6.2๋ถํฐ ์ ์ ์ง์๋ผ.
์๋ฃจ์
๋ฃจํธ์ Directory.Packages.props ํ์ผ ํ๋๋ฅผ ๋ง๋ค์ด.
<Project>
<PropertyGroup>
<ManagePackageVersionsCentrally>true</ManagePackageVersionsCentrally>
</PropertyGroup>
<ItemGroup>
<PackageVersion Include="Serilog" Version="4.0.0" />
<PackageVersion Include="xunit" Version="2.9.0" />
</ItemGroup>
</Project>
๊ทธ๋ฌ๋ฉด ๊ฐ ํ๋ก์ ํธ์์๋ ๋ฒ์ ์์ด ์ด๋ฆ๋ง ์ฐ๋ฉด ๋ผ.
<PackageReference Include="Serilog" />
๋ฒ์ ์
๊ทธ๋ ์ด๋๋ ์ด์ ํ์ผ ํ๋๋ง ๊ณ ์น๋ฉด ๋! ๐ ์ฌ๊ธฐ์ ๊ณตํต ๋ฉํ๋ฐ์ดํฐ(Authors, License, RepositoryUrl ๋ฑ)๋ Directory.Build.props์ ๋ชฐ์๋ฃ์ผ๋ฉด ์ฌ๋ฌ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ํ ์ ์ฅ์์์ ๊ด๋ฆฌํ๋ ๋ชจ๋
ธ๋ ํฌ ๊ตฌ์กฐ๊ฐ ๊น๋ํด์ ธ.
๐งฐ ๊ด๋ฆฌ ์์คํ
์ ๋ฐ์ณ์ฃผ๋ ๋๊ตฌ๋ค
โข dotnet list package --outdated : ์
๋ฐ์ดํธ ๊ฐ๋ฅํ ํจํค์ง ํ์ธ
โข dotnet list package --vulnerable : ์๋ ค์ง ์ทจ์ฝ์ ์ด ์๋ ํจํค์ง ํ์ธ
โข NuGetAudit : .NET 8 SDK๋ถํฐ restore ์ ์ทจ์ฝ์ ์ ์๋ ๊ฒฝ๊ณ . ์ต์ SDK์์ ์ ์ด(๊ฐ์ ) ์์กด์ฑ๊น์ง ๊ฐ์ฌ ๋ฒ์๊ฐ ๋์ด์ก์ด
โข Lock ํ์ผ (RestorePackagesWithLockFile) : packages.lock.json์ผ๋ก ์์กด์ฑ ๊ทธ๋ํ๋ฅผ ๊ณ ์ ํด ์ฌํ ๊ฐ๋ฅํ ๋น๋ ๋ณด์ฅ
โข Dependabot / Renovate : ์ ๋ฒ์ ์ด ๋์ค๋ฉด ์๋์ผ๋ก PR ์์ฑ
9. ์๋ ๋ฐฐํฌ ํ์ดํ๋ผ์ธ ๋ง๋ค๊ธฐ ๐ค
์์ผ๋ก dotnet nuget push ํ๋ ๊ฒ๋ ๋ช ๋ฒ์ ๊ด์ฐฎ์. ๊ทผ๋ฐ ๊ฒฐ๊ตญ ์ฌ๋์ ์ค์๋ฅผ ํด. ๋ฒ์ ์ฌ๋ฆฌ๋ ๊ฑฐ ๊น๋จน๊ณ , ํ
์คํธ ์ ๋๋ฆฌ๊ณ ์ฌ๋ฆฌ๊ณ ... ๊ทธ๋ฌ๋๊น CI/CD๋ก ์๋ํํ์.
GitHub Actions ์์๋ฅผ ๋ณด์. Git ํ๊ทธ v1.2.0์ ํธ์ํ๋ฉด ๊ทธ ๋ฒ์ ์ผ๋ก ํจํค์ง๋ฅผ ๋ง๋ค์ด ๋ฐฐํฌํ๋ ํ๋ฆ์ด์ผ.
name: publish
on:
push:
tags: [ 'v*' ]
permissions:
contents: read
id-token: write # Trusted Publishing(OIDC)์ฉ
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-dotnet@v4
with:
dotnet-version: '8.0.x'
- name: Test
run: dotnet test -c Release
- name: Pack
run: |
VERSION=${GITHUB_REF_NAME#v}
dotnet pack -c Release -o out -p:Version=$VERSION
- name: Push
run: dotnet nuget push "out/*.nupkg" --api-key ${{ secrets.NUGET_API_KEY }} --source https://api.nuget.org/v3/index.json --skip-duplicate
ํฌ์ธํธ ๋ช ๊ฐ ์ง์ด๋ณผ๊ฒ.
โข ${GITHUB_REF_NAME#v} : ํ๊ทธ ์ด๋ฆ์์ ์์ v๋ฅผ ๋ผ์ ๋ฒ์ ์ผ๋ก ์จ. ๋ฒ์ ์ "์ง์ค์ ์์ฒ"์ด Git ํ๊ทธ๊ฐ ๋๋ ๊ฑฐ์ง.
โข --skip-duplicate : ์ด๋ฏธ ๊ฐ์ ๋ฒ์ ์ด ์์ผ๋ฉด ์๋ฌ ๋์ ๊ฑด๋๋ฐ์ด. ์ฌ์คํํ ๋ ํธํด.
โข .nupkg๋ฅผ ํธ์ํ๋ฉด ๊ฐ์ ํด๋์ .snupkg๋ ํจ๊ป ์ฌ๋ผ๊ฐ.
๐ API ํค๋ ์ด๋ป๊ฒ ๊ด๋ฆฌํด?
nuget.org API ํค๋ ์ต๋ 365์ผ ๋ง๋ฃ, ๊ทธ๋ฆฌ๊ณ ๋ฒ์(scope)๋ฅผ ์ง์ ํ ์ ์์ด. "์ด ํค๋ Jaenung.* ํจํค์ง push๋ง ๊ฐ๋ฅ"์ฒ๋ผ ์ต์ ๊ถํ์ผ๋ก ๋ง๋ค์ด์ CI ์ํฌ๋ฆฟ์ ๋ฃ์. ์ ๋ ์ฝ๋์ ํ๋์ฝ๋ฉ ๊ธ์ง! ๐
๋ ๋์๊ฐ nuget.org๋ 2025๋
์ Trusted Publishing์ ๋์
ํ์ด. GitHub Actions์ OIDC ํ ํฐ์ผ๋ก ๋จ๊ธฐ API ํค๋ฅผ ๋ฐ๊ธ๋ฐ๋ ๋ฐฉ์์ด๋ผ, ์ค๋ ์ด์์๋ ๋น๋ฐ ํค๋ฅผ ์ ์ฅํ ํ์ ์์ฒด๊ฐ ์ฌ๋ผ์ ธ. ์ ์์์ id-token: write ๊ถํ์ด ๊ทธ๊ฑธ ์ํ ๊ฑฐ์ผ. ์ค์ ๋ฐฉ๋ฒ์ nuget.org ๊ณต์ ๋ฌธ์๋ฅผ ํ์ธํด์ ์ ์ฑ
์ ๋ง๊ฒ ์ ์ฉํด๋ด.
๐ท๏ธ ๋ฒ์ ์๋ ๊ณ์ฐ ๋๊ตฌ
ํ๊ทธ ๋ฐฉ์ ๋ง๊ณ ๋ ๋ฒ์ ์ ์๋์ผ๋ก ๊ณ์ฐํด์ฃผ๋ ๋๊ตฌ๋ค์ด ์์ด.
| ๋๊ตฌ | ๋ฐฉ์ | ํน์ง |
|---|---|---|
| MinVer | ๊ฐ์ฅ ๊ฐ๊น์ด Git ํ๊ทธ ๊ธฐ์ค | ์ค์ ๊ฑฐ์ 0. ํ๊ทธ ์ดํ ์ปค๋ฐ์ ์๋์ผ๋ก ํ๋ฆฌ๋ฆด๋ฆฌ์ค ๋ฒ์ ๋ถ์ฌ |
| Nerdbank.GitVersioning | version.json + ์ปค๋ฐ ๋์ด | Microsoft ์ชฝ ์คํ์์ค์์๋ ์ฌ์ฉ. ์ธ๋ฐํ ์ ์ด |
| GitVersion | ๋ธ๋์น ์ ๋ต ๊ธฐ๋ฐ | GitFlow ๋ฑ ๋ธ๋์น ๊ท์น์ ๋ฐ๋ผ ๋ฒ์ ์ฐ์ถ |
10. ์ถ์ ์ดํ์ ์ถ: ์ด์๊ณผ ๊ด๋ฆฌ ๐ฟ
๋ฐฐํฌํ๋ค๊ณ ๋์ด ์๋์ผ. ์คํ๋ ค ๊ทธ๋๋ถํฐ ์ง์ง ๊ด๋ฆฌ ์์์ด์ง.
PackageReleaseNotes ์์ฑ์ด๋ CHANGELOG.md๋ก ๋ฌด์์ด ๋ฐ๋์๋์ง ๊ผญ ๋จ๊ธฐ์. ์ฌ์ฉ์๋ ์ด๊ฑธ ๋ณด๊ณ ์
๊ทธ๋ ์ด๋๋ฅผ ๊ฒฐ์ ํด.๊ทธ๋ฆฌ๊ณ ์ ๋ขฐ์ ๋ํ์์ ํจํค์ง ์๋ช
์ด์ผ. nuget.org๋ ์
๋ก๋๋ ๋ชจ๋ ํจํค์ง์ ์ ์ฅ์ ์๋ช
(repository signature)์ ์๋์ผ๋ก ๋ถ์ฌ. ์ฌ๊ธฐ์ ์์ฑ์๊ฐ ์ฝ๋ ์๋ช
์ธ์ฆ์๋ก ์์ฑ์ ์๋ช
(author signature)์ ์ถ๊ฐํ๋ฉด "์ด ํจํค์ง๋ ํ์คํ ๋ด๊ฐ ๋ง๋ ๊ฒ"์ด๋ผ๋ ๊ฑธ ์ฆ๋ช
ํ ์ ์์ง. dotnet nuget sign๊ณผ dotnet nuget verify ๋ช
๋ น์ผ๋ก ๋ค๋ฃฐ ์ ์์ด.
11. ํ๋์ ์ ๋ฆฌํ๋ ์ฒดํฌ๋ฆฌ์คํธ โ
๋ง๋ฌด๋ฆฌ: ์ฝ๋๋ฅผ '๋๋๋' ๊ธฐ์ ๐ค
๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ๋ง๋ค๊ณ NuGet์ผ๋ก ๋ฐฐํฌํ๋ค๋ ๊ฑด, ๊ฒฐ๊ตญ ๋ด ์ฝ๋๋ฅผ ๋ค๋ฅธ ์ฌ๋์ด ๋ฏฟ๊ณ ์ธ ์ ์๊ฒ ๋ง๋๋ ์ผ์ด์ผ. ๋ณต๋ถํ๋ ์ ํธ ํด๋์ค๊ฐ ๋ฒ์ ์ด ๋ถ๊ณ , ๋ฌธ์๊ฐ ์๊ธฐ๊ณ , ์๋์ผ๋ก ๋ฐฐํฌ๋๊ณ , ๋ณด์ ๊ฒฝ๊ณ ๊น์ง ๋ฐ๋ "์ ํ"์ด ๋๋ ๊ฑฐ์ง. ์ฒ์์ ์ค์ ํ ๊ฒ ๋ง์ ๋ณด์ด์ง๋ง, ํ ๋ฒ ํ
ํ๋ฆฟ์ ์ก์๋๋ฉด ๋ค์ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ถํฐ๋ dotnet new ํ ์ค๋ก ์์ํ๋ ๊ธฐ๋ถ์ผ ๊ฑฐ์ผ.
์ด๋ ๊ฒ ์์ C# ๋ผ์ด๋ธ๋ฌ๋ฆฌ ์ค๊ณ ๊ฒฝํ์ ๊ทธ ์์ฒด๋ก ํ๋ฅญํ ํฌํธํด๋ฆฌ์ค๊ฐ ๋ผ. ์ฌ๋ฅ๋ท ๊ฐ์ ์ฌ๋ฅ ๊ณต์ ํ๋ซํผ์์ "์ฌ๋ด ๊ณตํต ๋ชจ๋ ํจํค์งํ", "NuGet ๋ฐฐํฌ ํ์ดํ๋ผ์ธ ๊ตฌ์ถ" ๊ฐ์ ์์ ์ ์๋ขฐ๋ฐ๊ฑฐ๋ ๋ ธํ์ฐ๋ฅผ ๋๋๋ ๊ฐ๋ฐ์๋ ๊ฝค ๋ง์. ์ค๋ ๋ฐฐ์ด ๊ฑธ๋ก ์ผ๋จ ์์ ํจํค์ง ํ๋ nuget.org์ ์ฌ๋ ค๋ด. ๋ด ํจํค์ง ๋ค์ด๋ก๋ ์๊ฐ 1์์ 2๋ก ๋ฐ๋๋ ์๊ฐ, ๊ทธ ์ง๋ฆฟํจ์ ์ง์ง ๋ชป ์์ด ๐
#NuGet #.NET #C# #๋ผ์ด๋ธ๋ฌ๋ฆฌ์ค๊ณ #CI/CD
์ฐธ๊ณ ์๋ฃ
โข Microsoft Learn - .NET library guidance (Open-source library guidance): https://learn.microsoft.com/dotnet/standard/library-guidance/
โข Microsoft Learn - NuGet Documentation: https://learn.microsoft.com/nuget/
โข Microsoft Learn - Central Package Management: https://learn.microsoft.com/nuget/consume-packages/central-package-management
โข Microsoft Learn - Package Source Mapping: https://learn.microsoft.com/nuget/consume-packages/package-source-mapping
โข Microsoft Learn - Package validation overview: https://learn.microsoft.com/dotnet/fundamentals/apicompat/package-validation/overview
โข Microsoft Learn - Auditing package dependencies for security vulnerabilities: https://learn.microsoft.com/nuget/concepts/auditing-packages
โข Microsoft Learn - Trusted Publishing on nuget.org: https://learn.microsoft.com/nuget/nuget-org/trusted-publishing
โข Semantic Versioning 2.0.0: https://semver.org/
๊ด๋ จ ํค์๋
๋๊ธ ์์ฑ
์ด ๊ธ์ ๋ํ ์ฌ๋ฌ๋ถ์ ์๊ฐ์ ๋ค๋ ค์ฃผ์ธ์
๋ก๊ทธ์ธ์ด ํ์ํฉ๋๋ค
๋๊ธ์ ์์ฑํ๋ ค๋ฉด ๋จผ์ ๋ก๊ทธ์ธํด์ฃผ์ธ์.
๋๊ธ 0
์ง์์ธ์ ์ฒ - ์ง์ ์ฌ์ฐ๊ถ ๋ณดํธ ๊ณ ์ง
์ง์ ์ฌ์ฐ๊ถ ๋ณดํธ ๊ณ ์ง
- ์ ์๊ถ ๋ฐ ์์ ๊ถ: ๋ณธ ์ปจํ ์ธ ๋ ์ฌ๋ฅ๋ท์ ๋ ์ AI ๊ธฐ์ ๋ก ์์ฑ๋์์ผ๋ฉฐ, ๋ํ๋ฏผ๊ตญ ์ ์๊ถ๋ฒ ๋ฐ ๊ตญ์ ์ ์๊ถ ํ์ฝ์ ์ํด ๋ณดํธ๋ฉ๋๋ค.
- AI ์์ฑ ์ปจํ ์ธ ์ ๋ฒ์ ์ง์: ๋ณธ AI ์์ฑ ์ปจํ ์ธ ๋ ์ฌ๋ฅ๋ท์ ์ง์ ์ฐฝ์๋ฌผ๋ก ์ธ์ ๋๋ฉฐ, ๊ด๋ จ ๋ฒ๊ท์ ๋ฐ๋ผ ์ ์๊ถ ๋ณดํธ๋ฅผ ๋ฐ์ต๋๋ค.
- ์ฌ์ฉ ์ ํ: ์ฌ๋ฅ๋ท์ ๋ช ์์ ์๋ฉด ๋์ ์์ด ๋ณธ ์ปจํ ์ธ ๋ฅผ ๋ณต์ , ์์ , ๋ฐฐํฌ, ๋๋ ์์ ์ ์ผ๋ก ํ์ฉํ๋ ํ์๋ ์๊ฒฉํ ๊ธ์ง๋ฉ๋๋ค.
- ๋ฐ์ดํฐ ์์ง ๊ธ์ง: ๋ณธ ์ปจํ ์ธ ์ ๋ํ ๋ฌด๋จ ์คํฌ๋ํ, ํฌ๋กค๋ง, ๋ฐ ์๋ํ๋ ๋ฐ์ดํฐ ์์ง์ ๋ฒ์ ์ ์ฌ์ ๋์์ด ๋ฉ๋๋ค.
- AI ํ์ต ์ ํ: ์ฌ๋ฅ๋ท์ AI ์์ฑ ์ปจํ ์ธ ๋ฅผ ํ AI ๋ชจ๋ธ ํ์ต์ ๋ฌด๋จ ์ฌ์ฉํ๋ ํ์๋ ๊ธ์ง๋๋ฉฐ, ์ด๋ ์ง์ ์ฌ์ฐ๊ถ ์นจํด๋ก ๊ฐ์ฃผ๋ฉ๋๋ค.
์์ง ๋๊ธ์ด ์์ต๋๋ค.