์ฝ˜ํ…์ธ  ๋Œ€ํ‘œ ์ด๋ฏธ์ง€ - ๐Ÿ“ฆ .NET ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ๊ฐœ๋ฐœ๊ณผ NuGet ํŒจํ‚ค์ง€ ๋ฐฐํฌ ๊ด€๋ฆฌ ์‹œ์Šคํ…œ ์™„์ „ ์ •๋ณต

๐Ÿ“ฆ .NET ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ๊ฐœ๋ฐœ๊ณผ NuGet ํŒจํ‚ค์ง€ ๋ฐฐํฌ ๊ด€๋ฆฌ ์‹œ์Šคํ…œ ์™„์ „ ์ •๋ณต

"์ด ์œ ํ‹ธ ํด๋ž˜์Šค, ํ”„๋กœ์ ํŠธ๋งˆ๋‹ค ๋ณต๋ถ™ํ•˜๊ณ  ์žˆ์ง€ ์•Š์•„?" ๐Ÿ™‹
๋งž์•„, ๊ทธ ์ฐ์ฐํ•œ ๋А๋‚Œ. ๋ฒ„๊ทธ ํ•˜๋‚˜ ๊ณ ์น˜๋ฉด ๋‹ค์„ฏ ๊ตฐ๋ฐ๋ฅผ ์ฐพ์•„๋‹ค๋‹ˆ๋ฉฐ ๊ณ ์ณ์•ผ ํ•˜๋Š” ๊ทธ ์ง€์˜ฅ ๋ง์ด์•ผ.
์˜ค๋Š˜์€ ๊ทธ ์ฝ”๋“œ๋ฅผ ์ œ๋Œ€๋กœ ๋œ .NET ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋กœ ๋งŒ๋“ค๊ณ , NuGet ํŒจํ‚ค์ง€๋กœ ํฌ์žฅํ•ด์„œ, ์ž๋™์œผ๋กœ ๋ฐฐํฌยท๊ด€๋ฆฌํ•˜๋Š” ์‹œ์Šคํ…œ๊นŒ์ง€ ํ•œ ๋ฒˆ์— ํ›‘์–ด๋ณผ ๊ฑฐ์•ผ. ์นœ๊ตฌํ•œํ…Œ ์„ค๋ช…ํ•˜๋“ฏ ํŽธํ•˜๊ฒŒ ๊ฐˆ ํ…Œ๋‹ˆ ์ปคํ”ผ ํ•œ ์ž” ๋“ค๊ณ  ๋”ฐ๋ผ์™€ โ˜•

์ฝ”๋“œ๊ฐ€ ํŒจํ‚ค์ง€๊ฐ€ ๋˜๊ธฐ๊นŒ์ง€ C# ์ฝ”๋“œ .csproj ํด๋ž˜์Šค ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ dotnet pack .nupkg v1.2.0 NuGet ํ”ผ๋“œ nuget.org ยท ์‚ฌ๋‚ด ํ”ผ๋“œ ยท GitHub ๋‹ค๋ฅธ ํ”„๋กœ์ ํŠธ dotnet add package

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"์ด ๋ฒ„์ „ ๊ด€๋ฆฌ, ์˜์กด์„ฑ ํ•ด์„, ํƒ€๊นƒ ํ”„๋ ˆ์ž„์›Œํฌ ์„ ํƒ๊นŒ์ง€ ๋‹ค ํ•ด์ฃผ๋‹ˆ๊นŒ ์—„์ฒญ๋‚œ ๊ฑฐ์•ผ.

๐Ÿ“ฆ ํŒจํ‚ค์ง€ (.nupkg)
DLL + ๋ฉ”ํƒ€๋ฐ์ดํ„ฐ๋ฅผ ๋ฌถ์€ ๋ฐฐํฌ ๋‹จ์œ„. ๊ณ ์œ  ID์™€ ๋ฒ„์ „์œผ๋กœ ์‹๋ณ„๋ผ.
๐Ÿช ํ”ผ๋“œ (Feed)
ํŒจํ‚ค์ง€๋ฅผ ๋ณด๊ด€ํ•˜๋Š” ์ €์žฅ์†Œ. ๋Œ€ํ‘œ๊ฐ€ nuget.org, ๊ทธ ์™ธ์— ์‚ฌ๋‚ด ํ”ผ๋“œ๋„ ๊ฐ€๋Šฅํ•ด.
๐Ÿ”— ์˜์กด์„ฑ (Dependency)
"๋‚˜๋Š” Newtonsoft.Json 13.0.1 ์ด์ƒ์ด ํ•„์š”ํ•ด" ๊ฐ™์€ ๊ด€๊ณ„. NuGet์ด ์•Œ์•„์„œ ๊ฐ™์ด ์„ค์น˜ํ•ด์ค˜.

์ฐธ๊ณ ๋กœ 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 ํ˜•์‹์ด๊ณ  ๊ทœ์น™์€ ์ด๋ž˜.

๋ฒ„์ „ 2.4.1 ํ•ด๋ถ€ํ•˜๊ธฐ 2 . 4 . 1 MAJOR ํ˜ธํ™˜์„ฑ ๊นจ์ง ๐Ÿ’ฅ ์ฃผ์˜ ํ•„์š” MINOR ๊ธฐ๋Šฅ ์ถ”๊ฐ€ โœจ ์•ˆ์ „ํ•œ ์—… PATCH ๋ฒ„๊ทธ ์ˆ˜์ • ๐Ÿฉน ๋งˆ์Œ ํŽธํžˆ ๋ฏธ๋ฆฌ๋ณด๊ธฐ: 2.5.0-beta.1 (ํ•˜์ดํ”ˆ ๋’ค๋Š” ํ”„๋ฆฌ๋ฆด๋ฆฌ์Šค)

์—ฌ๊ธฐ์„œ ๋งŽ์ด๋“ค ํ—ท๊ฐˆ๋ฆฌ๋Š” ๊ฒŒ .NET์— ๋ฒ„์ „์ด ์—ฌ๋Ÿฌ ๊ฐœ ์žˆ๋‹ค๋Š” ๊ฑฐ์•ผ.

์†์„ฑ์˜๋ฏธํŒ
PackageVersion (Version)NuGet ํŒจํ‚ค์ง€ ๋ฒ„์ „SemVer ๊ทธ๋Œ€๋กœ. ์‚ฌ์šฉ์ž๊ฐ€ ๋ณด๋Š” ๋ฒ„์ „
AssemblyVersionCLR์ด ๋ฐ”์ธ๋”ฉํ•  ๋•Œ ์“ฐ๋Š” ๊ฐ•๋ ฅํ•œ ๋ฒ„์ „.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. ์ข‹์€ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์˜ ํ’ˆ๊ฒฉ โœจ

ํŒจํ‚ค์ง€๋ฅผ ๋งŒ๋“œ๋Š” ๊ฑด ์‰ฌ์›Œ. ๊ทผ๋ฐ "๋‚จ๋“ค์ด ๋ฏฟ๊ณ  ์“ฐ๋Š”" ํŒจํ‚ค์ง€๋ฅผ ๋งŒ๋“œ๋Š” ๊ฑด ์ข€ ๋‹ค๋ฅธ ์–˜๊ธฐ์•ผ. ๊ผญ ์ฑ™๊ธธ ๊ฒƒ๋“ค ์ •๋ฆฌํ•ด๋ณผ๊ฒŒ.

1
๊ณต๊ฐœ API๋Š” ์‹ ์ค‘ํ•˜๊ฒŒ
ํ•œ ๋ฒˆ public์œผ๋กœ ๋‚ด๋ณด๋‚ธ ๊ฑด ์‚ฌ์‹ค์ƒ "์•ฝ์†"์ด์•ผ. ์ง€์šฐ๊ฑฐ๋‚˜ ์‹œ๊ทธ๋‹ˆ์ฒ˜๋ฅผ ๋ฐ”๊พธ๋ฉด MAJOR๋ฅผ ์˜ฌ๋ ค์•ผ ํ•ด. ๋‚ด๋ถ€์šฉ์€ internal๋กœ ๊ฝ๊ฝ ์ˆจ๊ธฐ์ž. ํ…Œ์ŠคํŠธ์—์„œ ์จ์•ผ ํ•˜๋ฉด InternalsVisibleTo๋ฅผ ์“ฐ๋ฉด ๋ผ.
2
Package Validation ์ผœ๊ธฐ
<EnablePackageValidation>true</EnablePackageValidation>
์™€ PackageValidationBaselineVersion์„ ์„ค์ •ํ•˜๋ฉด, ์ด์ „ ๋ฒ„์ „ ๋Œ€๋น„ ํ˜ธํ™˜์„ฑ์„ ๊นจ๋Š” ๋ณ€๊ฒฝ์„ pack ๋‹จ๊ณ„์—์„œ ์žก์•„์ค˜. ์‹ค์ˆ˜๋กœ ๋ฉ”์„œ๋“œ ํ•˜๋‚˜ ์ง€์› ์„ ๋•Œ ๊ตฌ์„ธ์ฃผ์•ผ.
3
XML ๋ฌธ์„œ ์ฃผ์„
/// <summary>๋Š” ์‚ฌ์šฉ์ž IntelliSense์— ๊ทธ๋Œ€๋กœ ๋– . ์ด๊ฒŒ ์‚ฌ์‹ค์ƒ 1์ฐจ ๋งค๋‰ด์–ผ์ด์•ผ.
4
Source Link
์‚ฌ์šฉ์ž๊ฐ€ ๋””๋ฒ„๊น…ํ•  ๋•Œ ๋„ค GitHub ์†Œ์Šค๋กœ ๋ฐ”๋กœ ๋“ค์–ด๊ฐˆ ์ˆ˜ ์žˆ๊ฒŒ ํ•ด์ค˜. .NET 8 SDK๋ถ€ํ„ฐ๋Š” GitHubยทAzure Repos ๋“ฑ ์ฃผ์š” ํ˜ธ์ŠคํŠธ์— ๋Œ€ํ•ด Source Link๊ฐ€ ๊ธฐ๋ณธ ํฌํ•จ๋ผ์„œ ๋”ฐ๋กœ ํŒจํ‚ค์ง€๋ฅผ ์•ˆ ๋„ฃ์–ด๋„ ๋ผ.
5
๊ฒฐ์ •์  ๋นŒ๋“œ (Deterministic Build)
๊ฐ™์€ ์†Œ์Šค๋ฉด ๊ฐ™์€ ๋ฐ”์ด๋„ˆ๋ฆฌ๊ฐ€ ๋‚˜์˜ค๊ฒŒ. CI์—์„œ๋Š” ContinuousIntegrationBuild=true๋ฅผ ์ผœ์„œ ๋กœ์ปฌ ๊ฒฝ๋กœ ๊ฐ™์€ ์ •๋ณด๊ฐ€ ์„ž์ด์ง€ ์•Š๊ฒŒ ํ•ด.
6
์˜์กด์„ฑ ๋‹ค์ด์–ดํŠธ
๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๊ฐ€ ๋ฌด๊ฑฐ์šด ์˜์กด์„ฑ์„ ๋Œ๊ณ  ์˜ค๋ฉด ์‚ฌ์šฉ์ž๋Š” "๋‹ค์ด์•„๋ชฌ๋“œ ์˜์กด์„ฑ" ์ถฉ๋Œ์— ์‹œ๋‹ฌ๋ ค. ๊ผญ ํ•„์š”ํ•œ ๊ฒƒ๋งŒ, ๊ทธ๋ฆฌ๊ณ  ๋ฒ”์œ„๋Š” ๋„ˆ๊ทธ๋Ÿฝ๊ฒŒ.

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
์ „ ์„ธ๊ณ„ ๊ณต๊ฐœ. ์˜คํ”ˆ์†Œ์Šค๋ผ๋ฉด ์—ฌ๊ธฐ. ๊ณ„์ • + API ํ‚ค ํ•„์š”. ์—…๋กœ๋“œ ํ›„ ์ž๋™ ๊ฒ€์ฆยท์ธ๋ฑ์‹ฑ์„ ๊ฑฐ์ณ ๋ณดํ†ต ๋ช‡ ๋ถ„~์ˆ˜์‹ญ ๋ถ„ ๋‚ด์— ๊ฒ€์ƒ‰์— ๋…ธ์ถœ๋ผ.
๐Ÿ™ GitHub Packages
์ €์žฅ์†Œ์™€ ๋ถ™์–ด์žˆ๋Š” ํ”ผ๋“œ. ๊ถŒํ•œ์„ GitHub ์กฐ์ง ๋‹จ์œ„๋กœ ๊ด€๋ฆฌํ•˜๊ธฐ ์ข‹์•„. ๋‹จ, ์ฝ์„ ๋•Œ๋„ ์ธ์ฆ์ด ํ•„์š”ํ•ด.
๐Ÿข Azure Artifacts
Azure DevOps์˜ ํ”ผ๋“œ. ์—…์ŠคํŠธ๋ฆผ ์†Œ์Šค(nuget.org ํ”„๋ก์‹œ), ๋ทฐ(@prerelease, @release) ๊ฐ™์€ ๊ธฐ์—…์šฉ ๊ธฐ๋Šฅ์ด ํ’๋ถ€ํ•ด.
๐Ÿ—„๏ธ ์ž์ฒด ํ˜ธ์ŠคํŒ…
BaGet(์ปค๋ฎค๋‹ˆํ‹ฐ ๊ฒฝ๋Ÿ‰ ์„œ๋ฒ„), Sonatype Nexus, JFrog Artifactory, ํ˜น์€ ๋‹จ์ˆœ ํŒŒ์ผ ๊ณต์œ  ํด๋”๊นŒ์ง€.

ํšŒ์‚ฌ์—์„œ๋Š” ๋ณดํ†ต "์‚ฌ๋‚ด ๊ณตํ†ต ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋Š” ๋น„๊ณต๊ฐœ ํ”ผ๋“œ์—, ์™ธ๋ถ€ ์˜คํ”ˆ์†Œ์Šค๋Š” 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๋กœ ์ž๋™ํ™”ํ•˜์ž.

ํƒœ๊ทธ ํ•˜๋‚˜๋กœ ์ถœ์‹œ๊นŒ์ง€ ๐Ÿท๏ธ v1.2.0 ๐Ÿ”จ ๋นŒ๋“œ โœ… ํ…Œ์ŠคํŠธ ๐Ÿš€ push ํ…Œ์ŠคํŠธ๊ฐ€ ํ•˜๋‚˜๋ผ๋„ ์‹คํŒจํ•˜๋ฉด ๋ฐฐํฌ๋Š” ๋ฉˆ์ถฐ์š” ์‚ฌ๋žŒ์˜ ์‹ค์ˆ˜ ๋Œ€์‹  ํŒŒ์ดํ”„๋ผ์ธ์ด ๋ฌธ์ง€๊ธฐ ๐Ÿ›ก๏ธ

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.GitVersioningversion.json + ์ปค๋ฐ‹ ๋†’์ดMicrosoft ์ชฝ ์˜คํ”ˆ์†Œ์Šค์—์„œ๋„ ์‚ฌ์šฉ. ์„ธ๋ฐ€ํ•œ ์ œ์–ด
GitVersion๋ธŒ๋žœ์น˜ ์ „๋žต ๊ธฐ๋ฐ˜GitFlow ๋“ฑ ๋ธŒ๋žœ์น˜ ๊ทœ์น™์— ๋”ฐ๋ผ ๋ฒ„์ „ ์‚ฐ์ถœ

10. ์ถœ์‹œ ์ดํ›„์˜ ์‚ถ: ์šด์˜๊ณผ ๊ด€๋ฆฌ ๐ŸŒฟ

๋ฐฐํฌํ–ˆ๋‹ค๊ณ  ๋์ด ์•„๋‹ˆ์•ผ. ์˜คํžˆ๋ ค ๊ทธ๋•Œ๋ถ€ํ„ฐ ์ง„์งœ ๊ด€๋ฆฌ ์‹œ์ž‘์ด์ง€.

๐Ÿ™ˆ Unlist (๋ชฉ๋ก ์ˆจ๊น€)
nuget.org์—์„  ํŒจํ‚ค์ง€๋ฅผ ์‚ญ์ œํ•  ์ˆ˜ ์—†์–ด. ๋Œ€์‹  unlistํ•˜๋ฉด ๊ฒ€์ƒ‰์—์„  ์‚ฌ๋ผ์ง€์ง€๋งŒ, ์ •ํ™•ํ•œ ๋ฒ„์ „์„ ์ง€์ •ํ•œ ๊ธฐ์กด ์‚ฌ์šฉ์ž ๋นŒ๋“œ๋Š” ๊นจ์ง€์ง€ ์•Š์•„. ์ƒํƒœ๊ณ„ ์•ˆ์ •์„ฑ์„ ์œ„ํ•œ ์„ค๊ณ„์•ผ.
๐Ÿชฆ Deprecate (์‚ฌ์šฉ ์ค‘๋‹จ)
"์ด ๋ฒ„์ „์€ ๋ฒ„๊ทธ ์žˆ์Œ", "์ด ํŒจํ‚ค์ง€๋Š” ๋ ˆ๊ฑฐ์‹œ, ๋Œ€์‹  XX ์จ" ๊ฐ™์€ ํ‘œ์‹œ๋ฅผ ๋‹ฌ ์ˆ˜ ์žˆ์–ด. ์‚ฌ์šฉ์ž IDE์— ๊ฒฝ๊ณ ๋กœ ๋ณด์—ฌ.
๐Ÿ›ก๏ธ ์ทจ์•ฝ์  ๋Œ€์‘
๋ณด์•ˆ ์ด์Šˆ๊ฐ€ ์ƒ๊ธฐ๋ฉด ํŒจ์น˜ ๋ฒ„์ „์„ ๋นจ๋ฆฌ ๋‚ด๊ณ , GitHub Security Advisory๋กœ ๊ณต์ง€ํ•˜๋ฉด NuGetAudit์ด ์‚ฌ์šฉ์ž์—๊ฒŒ ๊ฒฝ๊ณ ํ•ด์ค˜.
๐Ÿ“ ๋ฆด๋ฆฌ์Šค ๋…ธํŠธ
PackageReleaseNotes ์†์„ฑ์ด๋‚˜ CHANGELOG.md๋กœ ๋ฌด์—‡์ด ๋ฐ”๋€Œ์—ˆ๋Š”์ง€ ๊ผญ ๋‚จ๊ธฐ์ž. ์‚ฌ์šฉ์ž๋Š” ์ด๊ฑธ ๋ณด๊ณ  ์—…๊ทธ๋ ˆ์ด๋“œ๋ฅผ ๊ฒฐ์ •ํ•ด.

๊ทธ๋ฆฌ๊ณ  ์‹ ๋ขฐ์˜ ๋ํŒ์™•์€ ํŒจํ‚ค์ง€ ์„œ๋ช…์ด์•ผ. nuget.org๋Š” ์—…๋กœ๋“œ๋œ ๋ชจ๋“  ํŒจํ‚ค์ง€์— ์ €์žฅ์†Œ ์„œ๋ช…(repository signature)์„ ์ž๋™์œผ๋กœ ๋ถ™์—ฌ. ์—ฌ๊ธฐ์— ์ž‘์„ฑ์ž๊ฐ€ ์ฝ”๋“œ ์„œ๋ช… ์ธ์ฆ์„œ๋กœ ์ž‘์„ฑ์ž ์„œ๋ช…(author signature)์„ ์ถ”๊ฐ€ํ•˜๋ฉด "์ด ํŒจํ‚ค์ง€๋Š” ํ™•์‹คํžˆ ๋‚ด๊ฐ€ ๋งŒ๋“  ๊ฒƒ"์ด๋ผ๋Š” ๊ฑธ ์ฆ๋ช…ํ•  ์ˆ˜ ์žˆ์ง€. dotnet nuget sign๊ณผ dotnet nuget verify ๋ช…๋ น์œผ๋กœ ๋‹ค๋ฃฐ ์ˆ˜ ์žˆ์–ด.

11. ํ•œ๋ˆˆ์— ์ •๋ฆฌํ•˜๋Š” ์ฒดํฌ๋ฆฌ์ŠคํŠธ โœ…

โœ“
SDK ์Šคํƒ€์ผ csproj์— ๋ฉ”ํƒ€๋ฐ์ดํ„ฐ ์ž‘์„ฑ (ID, ๋ผ์ด์„ ์Šค, README, ์•„์ด์ฝ˜, ์ €์žฅ์†Œ URL)
โœ“
netstandard2.0 + ์ตœ์‹  .NET ๋ฉ€ํ‹ฐ ํƒ€๊ธฐํŒ… ๊ฒ€ํ† 
โœ“
SemVer ์ค€์ˆ˜ + AssemblyVersion ์ •์ฑ… ๊ฒฐ์ •
โœ“
XML ๋ฌธ์„œ, Source Link, snupkg, ๊ฒฐ์ •์  ๋นŒ๋“œ
โœ“
Package Validation์œผ๋กœ ํ˜ธํ™˜์„ฑ ๊นจ์ง ๋ฐฉ์ง€
โœ“
๋กœ์ปฌ ํ”ผ๋“œ๋กœ ์‚ฌ์ „ ํ…Œ์ŠคํŠธ โ†’ NuGet Package Explorer๋กœ ๋‚ด์šฉ๋ฌผ ํ™•์ธ
โœ“
CPM(Directory.Packages.props) + ํŒจํ‚ค์ง€ ์†Œ์Šค ๋งคํ•‘
โœ“
CI/CD ์ž๋™ ๋ฐฐํฌ + ๋ฒ”์œ„ ์ œํ•œ API ํ‚ค ๋˜๋Š” Trusted Publishing
โœ“
NuGetAudit, Dependabot์œผ๋กœ ์ง€์†์  ๊ด€๋ฆฌ ยท unlist/deprecate ํ™œ์šฉ

๋งˆ๋ฌด๋ฆฌ: ์ฝ”๋“œ๋ฅผ '๋‚˜๋ˆ„๋Š”' ๊ธฐ์ˆ  ๐Ÿค

๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ๋งŒ๋“ค๊ณ  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
๋Œ“๊ธ€์„ ๋ถˆ๋Ÿฌ์˜ค๋Š” ์ค‘์ž…๋‹ˆ๋‹ค.

์•„์ง ๋Œ“๊ธ€์ด ์—†์Šต๋‹ˆ๋‹ค.