GitHub 개인 계정과 조직: 어떤 리포를 조직으로 옮길까?
기능 모듈을 패키지로 내려다가 리포 하나를 개인 계정에서 조직으로 옮겼습니다. 옮기기 직전까지 저는 이 일이 귀찮을 거라고 생각했습니다. 러너를 다시 붙이고, 시크릿을 다시 넣고, 문서마다 주소를 고쳐야 할 것 같았습니다. 실제로는 API 호출 한 번과 원격 주소 한 줄로 끝났습니다. 그 과정에서 어떤 리포를 조직에 두고 어떤 리포를 개인 계정에 둘지 기준이 하나 생겨서 정리해 둡니다.
스코프가 곧 소유자라는 GitHub Packages의 규칙
듀오랩스에는 디자인 시스템 DEUX가 있고, 이미 duolabs-deux라는 조직에서 @duolabs-deux/ui 같은 이름으로 npm 패키지를 내고 있습니다. 이번에 만드는 UNIT은 사업자등록 확인 같은 기능을 모듈로 묶어 다른 프로젝트에서 npm install 한 줄로 쓰게 하려는 프로젝트입니다. DEUX를 쓰는 프로젝트는 이미 .npmrc에 @duolabs-deux 레지스트리와 읽기 토큰이 잡혀 있으니, UNIT도 같은 스코프로 나가면 소비하는 쪽에서 설정할 것이 없습니다.
문제는 GitHub Packages의 npm 레지스트리가 스코프를 소유자 이름으로 쓴다는 점입니다. @duolabs-deux/…로 내려면 패키지 주인이 duolabs-deux 조직이어야 합니다. 그런데 UNIT 리포는 개인 계정에 있었습니다.
옮기지 않는 방법 두 가지와 그 비용
리포를 옮기지 않고도 방법은 있었습니다. 셋을 놓고 비교했습니다.
| 리포 위치 | 패키지 이름 | 대가 | |
|---|---|---|---|
| 조직으로 옮김 | 조직 | @duolabs-deux/unit-* |
한 번 옮기는 수고 |
| 그대로 두고 토큰 추가 | 개인 계정 | @duolabs-deux/unit-* |
조직 쓰기 권한 토큰을 시크릿으로 넣고, 만료될 때마다 갱신 |
| 그대로 두고 개인 스코프 | 개인 계정 | @<개인계정>/unit-* |
소비 프로젝트마다 .npmrc에 레지스트리 한 줄 추가 |
두 번째는 지금 당장은 가장 편해 보였습니다. 그런데 만료되는 토큰은 잊고 있다가 배포가 멈춘 날에야 생각납니다. 세 번째는 비용이 소비하는 쪽으로 넘어갑니다. 프로젝트가 늘수록 설정 줄도 같이 늘어납니다. 한 번 수고하고 끝나는 첫 번째가 길게 보면 가장 덜 귀찮다고 판단했습니다.
이름표를 같이 쓰면 생기는 충돌, contracts
같은 스코프를 쓰기로 하자 바로 걸리는 것이 있었습니다. DEUX에 이미 @duolabs-deux/contracts가 있습니다. 디자인 시스템의 매니페스트 타입을 담은 패키지입니다. UNIT에도 공통 타입 패키지가 필요한데 같은 이름으로는 낼 수 없습니다.
그래서 UNIT 패키지에는 전부 unit- 접두를 붙이기로 했습니다. unit-contracts, unit-runtime, unit-biz-registry처럼요. 충돌하는 것은 contracts 하나였지만 전부 붙인 데는 이유가 있습니다. 조직의 패키지 목록은 이름순으로 섞여 나오는데, biz-registry만 보고는 그게 화면 패키지인지 기능 패키지인지 알 수 없습니다. DEUX도 테마 패키지에 -ui 접미를 붙여(vega-ui, lyra-ui) 종류를 이름으로 드러내고 있어서 같은 관례를 따른 셈입니다.
API 호출 한 번으로 끝난 이전
옮기는 것 자체는 이렇게 끝났습니다.
gh api -X POST repos/<개인계정>/<리포>/transfer -f new_owner=<조직> -f new_name=<새 이름>
git remote set-url origin https://github.com/<조직>/<새 이름>.git제가 가장 걱정한 것은 셀프호스티드 러너였습니다. 이 리포는 배포를 오리진 서버의 러너가 맡고 있고, 러너는 리포 단위로 등록되어 있었습니다. 옮기면 떨어질 거라고 보고 다시 등록할 준비를 해 두었습니다.
확인해 보니 러너는 online인 채로 새 리포에 붙어 있었습니다. 러너 폴더의 설정 파일에는 여전히 옛 주소가 적혀 있는데도 그랬습니다. 배포 워크플로를 한 번 돌려 봤고 1분 46초 만에 성공했습니다. 시크릿 세 개도 그대로 따라왔습니다. 문서에 박혀 있던 옛 주소는 한 곳뿐이었습니다.
하나 확인하지 못한 것이 있습니다. 조직의 Actions 정책은 제 토큰에 admin:org 권한이 없어서 API로 읽지 못했습니다. 실제 배포가 돌았으니 셀프호스티드 러너가 허용되어 있는 것은 맞지만, 정책을 눈으로 확인한 것은 아닙니다.
조직에 둘 것과 개인 계정에 둘 것
이번 일로 기준을 하나 정했습니다. 다른 프로젝트가 가져다 쓰는 것은 조직에 두고, 제가 운영하는 서비스 앱은 개인 계정에 그대로 둡니다.
가져다 쓰는 것을 조직에 두면 얻는 것이 분명합니다. 패키지 스코프를 함께 쓰고, 권한을 조직 단위로 줄 수 있습니다. 나중에 외부 인력에게 디자인 시스템과 기능 모듈만 보여 줘야 할 때, 개인 계정에 섞여 있으면 리포마다 권한을 따로 줘야 합니다. 제품군을 통째로 넘길 일이 생겨도 조직 소유자만 바꾸면 됩니다.
반대로 랜딩 사이트나 고객 데모 같은 운영 앱은 옮겨도 얻는 것이 없습니다. 아무도 그 리포를 패키지로 받지 않고, 주소만 바뀝니다. 그래서 이번에는 UNIT 하나만 옮겼고 나머지 서비스 리포는 그대로 두었습니다.
남겨 둔 숙제, 조직 러너
조직으로 모으면 생기는 이점 중 아직 쓰지 않은 것이 있습니다. 지금 오리진 서버에는 리포마다 러너가 하나씩 떠 있습니다. 조직 러너를 하나 두면 조직 안의 리포가 그 러너를 같이 씁니다. DEUX 쪽 배포는 GitHub가 제공하는 러너에서 돌고 있어서 지금은 합칠 대상이 UNIT 하나뿐이고, 그래서 미뤄 두었습니다. 조직에 리포가 더 늘면 그때 옮길 생각입니다.
우리 서비스에 적용할 계획이 있으신가요?
기술 자문·개발 범위 상담 살펴보기 →함께 읽기
- 여러 고객 앱을 운영할 때 계정과 서명 키를 분리하는 방법React Native와 Expo로 여러 고객 앱을 개발·배포한다면 계정, 비용, 서명 키, 스토어 권한을 어떻게 나눌지 초기에 정해야 합니다. 이 구조를 잘못 잡으면 나중에 소유권 이전, 업데이트, 보안 관리가 복잡해집니다.
- 개인 액세스 토큰의 한계: 토큰은 권한이 아니라 신원- run: eas build --platform all --non-interactive
- git worktree로 여러 브랜치를 동시에 작업하는 방법개발 중에는 현재 작업을 유지한 채 다른 브랜치에서 긴급 수정이나 리뷰를 해야 할 때가 있습니다. 이때 매번 stash를 하거나 브랜치를 바꾸면 작업 흐름이 끊깁니다. git worktree는 이런 상황에서 매우 유용한 도구입니다.
- AI가 사용할 수 있는 CLI 도구 총정리AI 코딩 에이전트는 이제 단순히 코드를 작성하는 수준을 넘어, 터미널에서 명령어를 실행하고 실제 개발·배포·운영 업무까지 처리할 수 있다.
- 환경변수로 둔 CLI 기본값: 비대화형 셸에서 사라지는 이유문서를 저장하는 CLI를 하나 만들었습니다. 저장할 때마다 그 문서가 어디로 떨어졌는지 찾는 일이 반복돼서, 새 문서는 인박스로 가도록 기본값을 정했습니다. 터미널에서 쳐 보니 의도한 자리에 들어갔습니다.