깃 시스템이 코드 형상 관리의 표준이 된 이후, 원격 저장소는 협업의 중심 광장이 된 지 꽤 오래되었습니다.
이 글에서는 깃 시스템을 도입했더라도, 코드 저장소 중심이 아닌 패키지 저장소 중심의 협업 모델에 대해 알아 봅니다.
이 글에서는 깃허브에서 제공하는 페키지 저장소를 사용합니다.
Github Packages
깃허브 패키지는 깃허브 계정 마다 주어지는 패키지 저장소입니다.
누겟 패키지를 지원하며 비공개(private)가 기본값이라 업무용 패키지 저장소로 안성맞춤이고 코드와 함께 관리할 수 있어 편리합니다.
접근 설정
비공개이기 때문에 외부에서 접근할 때는 인증이 필요합니다.
깃허브의 다른 서비스가 그렇듯 토큰 기반 인증이라, 패키지 사용자가 사용할 토큰을 발행하고 그 토큰을 사용자에게 전달해야 합니다.
토큰 발행
앞서 설명했듯 깃허브 패키지 는 깃허브 계정에 속하기 때문에 계정 수준에서 발행한 토큰으로만 접근이 가능합니다. (저장소 수준에서 발행한 토큰으로는 접근 불가)
계정 수준의 토큰을, 개인 계정인 경우, PAT(Persional Access Token)라고 하며, 아래의 경로에서 발급이 가능합니다.
Profile (오른 쪽 상단 아이콘) > Settings > Developer Settings > Personal access token > Tokens (classic)
경로로 이동하면, 기 발행된 토큰 목록을 볼 수 있고, 그 상단에 있는 [ Generate new token (classic) ] 버튼을 눌러 생성할 수 있습니다.
생성 화면에서 이 토큰의 권한 범위를 설정할 수 있는데, 패키지 저장소 접근 전용으로 발급해야 합니다.
깃허브 패키지 접근과 관련된 권한 범위는 write:packages 와 read:packages 입니다.
- write:packages
패키지 쓰기 권한을 토큰에 부여합니다.
여기에 체크하면, repo(코드 저장소 읽기/쓰기)와 read:packages(패키지 저장소 쓰기) 권한이 자동으로 추가(선택)됩니다.
- read:packages
패키지 저장소 읽기 권한만 토큰에 부여합니다. ( repo 와 write:packages ) 가 자동으로 체크 되지 않습니다.)
이 글의 목적 상 PAT 두 개를 발급합니다.
-
패키지 관리자(보통 계정 주인) 용 토큰
write:packages를 선택하여 생성 -
패키지 접근자 (협업자) 용 토큰
read:packages만 선택(Readonly)하여 생성
깃허브는 일관되게 토큰은 한번만 보여주기 때문에 생성된 토큰은 잘 저장해야 합니다.
누겟 피드 등록
패키지 관리자이든 접근자이든, 닷넷 SDK에 깃허브 패키지 를 누겟 저장소로 추가하려면, 솔루션 루트 디렉토리에 NuGet.config 파일을 추가하고 아래 내용으로 채워 넣습니다.
<configuration>
<packageSources>
<add key="github" value="https://nuget.pkg.github.com/{계정이름}/index.json" />
</packageSources>
<packageSourceCredentials>
<github>
<add key="Username" value="{계정이름}" />
<add key="ClearTextPassword" value="{PAT}" />
</github>
</packageSourceCredentials>
</configuration>
- {계정이름} : 패키지 저장소가 속한 계정의 이름.
- {PAT} : 그 계정에서 발급한 토큰 (패키지 관리자는 Read/Write, 참조자는 ReadOnly)
이 파일의 추가로 개발 도구에 패키지 저장소가 추가됩니다.
정상적으로 추가되었다면, VS2026 의 누겟 패키지 관리 화면에서 패키지 소스로 나타날 것입니다.
패키지 저장소 등록은 이 파일 외에도, dotnet cli 명령어로 등록하는 방법도 있습니다.
이 파일이 코드 저장소에 있으면, Github Actions 의 워크플로에서 설치한 닷넷 SDK 에도 동일하게 누겟 저장소로 등록됩니다.
만약 등록한 PAT 가 읽기 전용이라면 패키지 push 시에 권한없음 에러가 납니다.
\> dotnet nuget push ... => 에러
패키지 작성자의 패키지 관리
패키지 작성자는 읽기/쓰기 토큰을 사용하기 때문에 패키지 생성/삭제/업데이트를 할 수 있습니다.
패키지 배포 자동화
패키지 배포는 개발 로컬에서 수작업으로 처리할 수 있습니다.
\> dotnet nuget push ...
이 글에서는 깃 태그를 이용하여 업데이트를 자동화합니다.
다시 말하면, 소스 코드는 브랜치 별로 관리하는 기존 방식을 유지하면서, 그 커밋 중 하나에 패키지 버전을 나타내는 태그를 붙이는 방식으로 패키지를 배포하는 방식을 사용합니다.
git tag 기반의 워크 플로
이를 위해, 태그 푸시로 실행되는 아래의 워크 플로를 코드 저장소에 추가합니다.
name: Deploy to Git Packages
on:
push:
tags:
# 태그의 형태는 "v0.2.3"
- 'v*'
env:
APP_VERSION: ""
jobs:
publish:
# runs-on: windows-latest
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Setup .NET
uses: actions/setup-dotnet@v4
with:
dotnet-version: '9.0.x'
- name: Extract app version
run: |
# tags 객체를 통해 버전 ("n.n.n" 형식 추출)
APP_VERSION=${GITHUB_REF#refs/tags/v}
echo "APP_VERSION=$APP_VERSION" >> $GITHUB_ENV
- name: Packing
run: dotnet pack ./MyProject -c Release -o ./nupkgs /p:Version=${{ env.APP_VERSION }}
- name: Publish nupkgs
run: dotnet nuget push ./nupkgs/*.nupkg --source "github" --skip-duplicate
워크 플로의 제일 마지막, 누겟 저장소 푸시 커맨드에 --skip-duplicate 옵션을 넣었습니다.
이는 사용자 실수로 버전 중복이 있더라도 워크플로가 에러로 멈추는 것을 방지합니다.
만약 워크 플로가 멈춰야 한다면, 그 옵션을 빼야 합니다.
멈추게 하는 편이 실수 발견에 좋습니다.
패키지 배포 방법
이 워크플로를 개시하려면, 특정 커밋에서 “vN.N.N” 형식의 태그를 생성한 후
git tag v1.0.0
이 태그를 푸시하면 됩니다. (간단하죠?!)
git push origin v1.0.0
동일한 작업을 VS에서 하기 위해서는
- 태그 생성
Git Changes 창에서,
[모든 커밋 보기] > 특정 커밋에 우클릭 => [새 태그 추가] 를 선택하여 태그를 추가.
태그 이름을 “v1.2.3” 입력하고 설명란은 비워도 됩니다.
- 태그 푸시
브랜치 목록에 “tags” 노드에 생성된 태그를 찾고,
태그에 우클릭 > [push] 를 선택하면 됩니다.
협업자의 패키지 참조
추가된 깃허브 패키지가 패키지 저장소로 추가되었으므로, 필요한 패키지를 검색하여 "패키지 참조 추가"하면 됩니다. 즉, 누겟 패키지 저장소처럼 사용하면 됩니다.
패키지 검색 시 나오지 않는다면, 패키지 소스에 “모두” 혹은 "github"가 선택되었는지 확인합니다.
비고
이 방식은 코드를 공유하며 협업하는 방식에 비해 아래의 장/단점이 있습니다.
장점
- 책임과 권한의 분리
패키지 관리자는 코드 저장소를 공개할 필요가 없습니다.
이로 인해, 코드 실패에 대한 책임과 변경에 대한 권한을 갖는 유일한 존재가 됩니다.
- 실수 방지
여러 사람이 코드를 공유하다보면 Renaming 등 리펙토링할 때 다른 사람의 코드까지 변경하는 실수를 할 수 있는데, 이러한 실수들이 근원적으로 차단됩니다.
- 코드 전파 안정성
패키지의 신버전은 패키지 업데이트를 할 때만 적용됩니다.
이는 나의 신버전이 다른 사람의 코드와 충돌을 일으킬 걱정을 할 필요가 없다는 의미입니다.
- 협업 풀(pool)의 확대
협업의 상대방이 조직 내부 인사일 필요가 없습니다.
소스 코드가 아닌 패키지를 공유하고, 토큰 삭제를 통해 언제든지 패키지 접근을 차단할 수 있기 때문입니다.
- 코드 관리 세분화
여러 커밋 중 릴리스할 커밋을 선택할 수 있기 때문에 보다 세세한 단계로 코드를 관리할 수 있습니다.
단점
- 복잡성
위와 같이 설정이 복잡합니다. 그래도 한번만 설정하면 되기 때문에 큰 단점이라고 생각하지는 않습니다.
- 전체 버전 관리
서로 다른 버전들이 공존할 수 있는데, 이는 전체 버전 관리를 어렵게 만들 수도 있습니다.


