프라이빗 GitHub repo에 대한 접근 권한을 자동으로 판매하는 시스템을 구축한 방법

발행: (2026년 2월 8일 오전 02:43 GMT+9)
16 분 소요
원문: Dev.to

Source: Dev.to

(번역을 진행하려면 번역하고자 하는 본문 텍스트를 제공해 주세요.)

TL;DR

프라이빗 GitHub 레포에 대한 접근 권한을 판매해야 했습니다. ZIP 다운로드는 업데이트가 되지 않고, 라이선스 키는 과도하며, 여러 결제 제공업체가 제 비즈니스를 거부했습니다. 그래서 전체 흐름을 처리하는 완전 자동화 시스템을 직접 구축했습니다:

  1. Payment webhook fires → signature verified
  2. GitHub username captured via OAuth
  3. Collaborator invite sent in seconds

잘 작동하지만, Polar.sh에 이미 이 기능이 내장되어 있고 더 잘 구현돼 있다는 것을 알게 되었습니다. 제 버전을 사용하지는 않았지만, 구축하면서 많은 것을 배웠습니다.

문제

보일러플레이트를 만들었고 판매하고 싶었습니다. 제품은 완성됐지만 – 실제로 어떻게 전달할지가 가장 어려운 부분이었습니다.

시작 키트, 보일러플레이트, 템플릿 등 개발자들이 비용을 지불할 만한 무언가를 만들었습니다. 이제는? 결제 후 어떻게 전달할까요?

평가한 옵션

옵션장점단점
Zip 파일 다운로드간단하고 바로 사용 가능업데이트 불가, git 히스토리 없음, pull/diff 불가
License‑key 게이팅접근을 잠글 수 있음커스텀 CLI + 인프라 필요 – 과도함
npm 프라이빗 패키지개발자에게 친숙레지스트리 인증 필요 → 마찰이 전환을 저해
GitHub Sponsors (프라이빗 레포)GitHub 내장 접근구독 전용, 일회성 구매에 부적합
결제 플랫폼을 통한 수동 전달소수 판매에선 가능규모가 커지면 불가능 (예: 새벽 2시 3건 판매)

제가 실제로 원했던 것은 아주 간단하게: 고객이 결제하면 → 고객이 자동으로, 즉시 프라이빗 레포에 접근할 수 있게.

결제 제공자 장애물

내가 등록한 사업체는 디자인 및 컨설팅 회사입니다. Lemon Squeezy와 몇몇 다른 플랫폼에 디지털 제품을 판매하려고 가입을 시도했지만 거절당했습니다.

  • 나는 일회성 디지털 제품(보일러플레이트 코드, 스타터 킷)을 판매하고 있으며, 컨설팅 서비스는 제공하지 않는다고 설명했습니다.
  • 거절은 계속되었고, 일부 제공자는 명백히 무례했습니다.

그때 나는 결제 시 웹훅을 호출할 수 있는 제공자를 찾아 나머지 작업을 직접 자동화해야 한다는 것만 알았습니다.

영감

  • 결제 웹훅 → GitHub 협업자 초대
  • 개인 저장소, 읽기 전용 접근 (복제 + 업데이트 풀, 전체 git 히스토리)

그들은 이에 대해 $30를 청구했습니다. 저는 직접 만들 수 있을 거라고 생각했어요 – 일석이조.

왜 가능했는가

  • GitHub API를 사용하면 협업자를 프로그래밍 방식으로 추가할 수 있습니다.
  • 웹훅을 발생시키는 모든 결제 제공업체가 흐름을 트리거할 수 있습니다.
  • 권한을 pull (읽기 전용)으로 설정하면 고객이 클론은 할 수 있지만 푸시할 수 없습니다.

내 솔루션 아키텍처

A stand‑alone Next.js app that is payment‑provider agnostic.

User clicks "Buy"
       |
GitHub OAuth (capture username)
       |
Payment checkout
       |
Webhook fires → verify signature → invite to repo → send email
       |
Customer has access in seconds

주요 구성 요소

ComponentDescription
GitHub OAuth결제 진행 전에 구매자의 GitHub 사용자 이름을 캡처합니다
Webhook handler결제 확인을 수신하고, HMAC‑SHA256 서명을 검증합니다
GitHub API client저장소 초대를 자동으로 보냅니다
PostgreSQL각 고객을 추적합니다 (데이터 33개 필드)
Resend환영 이메일을 보냅니다

상세 흐름

1. 체크아웃 GitHub 사용자명 캡처

결제 제공자는 구매자의 GitHub 사용자명을 알지 못하므로 먼저 캡처해야 합니다.

// After OAuth callback, redirect to checkout with GitHub info
const checkoutUrl = new URL(CHECKOUT_URL);
checkoutUrl.searchParams.set('gh_username', githubUser.login);
checkoutUrl.searchParams.set('gh_user_id', String(githubUser.id));

결제 제공자는 이 필드들을 웹훅 페이로드에 포함시켜, 결제가 완료될 때 정확히 누구를 초대해야 하는지 알 수 있게 합니다.

2. 웹훅 서명 검증

누구든지 웹훅 엔드포인트에 POST 요청을 보낼 수 있습니다. 검증이 없으면 공격자가 스스로에게 무료 접근 권한을 부여할 수 있습니다.

import { createHmac, timingSafeEqual } from 'crypto';

function verifySignature(payload: string, signature: string): boolean {
  const expected = createHmac('sha256', WEBHOOK_SECRET)
    .update(payload)
    .digest('hex');

  return timingSafeEqual(Buffer.from(signature), Buffer.from(expected));
}

주의사항

  • 원시 요청 본문을 사용하고, 파싱된 JSON을 사용하지 마세요 – 재직렬화 시 공백이 바뀌어 서명이 깨집니다.
  • === 대신 timingSafeEqual을 사용하세요 – 단순 문자열 비교는 타이밍 정보를 누출합니다.

3. 고객 레코드 UPSERT

고객이 업그레이드나 두 번째 제품을 구매할 수 있습니다. UPSERT가 없으면 두 번째 웹훅이 이메일에 대한 고유 제약 위반으로 실패합니다.

INSERT INTO customers (email, name, amount_paid, ...)
VALUES (...)
ON CONFLICT (email) DO UPDATE SET
  amount_paid = EXCLUDED.amount_paid,
  order_id    = EXCLUDED.order_id,
  updated_at  = NOW();

4. 보류 중인 초대 처리

고객이 초대를 수락하지 않으면 보류 중 상태가 됩니다. 보류 중인 초대가 50개가 되면 GitHub은 새 초대를 완전히 차단합니다.

  • 초대 상태를 추적합니다.
  • 보류 중인 초대를 모니터링하고 수동으로 재전송하거나 취소할 수 있는 관리자 패널을 구축했습니다.

5. GitHub API 실패 시 재시도

GitHub API가 다운되거나, 레이트 제한에 걸리거나, 네트워크 오류가 발생할 수 있습니다. 보일러플레이트에 대해 결제한 고객이 오류만 보고 지원팀에 연락하라는 메시지를 받으면 안 됩니다.

지수 백오프 전략

시도지연 시간
11 초
22 초
34 초
48 초
101 시간

오류 분류

  • 재시도 가능 – 네트워크 타임아웃, 레이트 제한 응답, 5xx 오류 → 백오프 루프를 통해 재시도합니다.
  • 재시도 불가 – 4xx 클라이언트 오류(예: 잘못된 페이로드) → 로그를 남기고 알림을 보냅니다.

배운 점

  1. Payment providers can be finicky about the type of digital goods you sell. → 결제 제공업체는 판매하는 디지털 상품 유형에 대해 까다로울 수 있습니다.
  2. Signature verification must use the raw payload and a timing‑safe compare. → 서명 검증은 원시 페이로드와 타이밍‑안전 비교를 사용해야 합니다.
  3. UPSERT (or equivalent) prevents duplicate‑key errors when a buyer makes multiple purchases. → UPSERT(또는 동등한 방법)는 구매자가 여러 번 구매할 때 중복 키 오류를 방지합니다.
  4. Pending GitHub invites can silently block you – monitor and clean them up. → 보류 중인 GitHub 초대는 조용히 차단할 수 있으니 모니터링하고 정리하세요.
  5. Robust retry logic is essential when you depend on third‑party APIs. → 강력한 재시도 로직은 타사 API에 의존할 때 필수적입니다.

요약

저는 다음과 같은 완전 자동화된, 공급자에 구애받지 않는 시스템을 구축했습니다:

  • OAuth를 통해 구매자의 GitHub 사용자 이름을 캡처합니다.
  • 결제 웹훅을 수신하고, 이를 안전하게 검증합니다.
  • 구매자를 읽기 전용 협업자로 프라이빗 레포지토리에 초대합니다.
  • 레포지토리 링크가 포함된 환영 이메일을 보냅니다.

시스템은 정상적으로 동작했지만 Polar.sh가 이미 이 기능을 즉시 제공하고 더 우수하게 구현하고 있어 결국 그들의 서비스를 사용하게 되었습니다. 그럼에도 불구하고 이번 경험을 통해 OAuth, 웹훅 보안, GitHub API, 그리고 신뢰성 높은 자동화 패턴을 깊이 있게 학습할 수 있었습니다.

백오프가 있는 큐

  • 영구적인 실패 (예: 사용자를 찾을 수 없음, 레포를 찾을 수 없음, 401/403) → 수동 검토를 위해 데드레터 큐로 보냅니다.
  • 10번의 실패 시도 후 해당 항목이 데드레터 큐로 이동하고 알림을 받습니다.

GitHub 토큰 교환 엔드포인트

올바른 엔드포인트는 https://github.com, **https://api.github.com**이 아닙니다.

# WRONG – returns an HTML login page
POST https://api.github.com/login/oauth/access_token
# CORRECT – returns the token
POST https://github.com/login/oauth/access_token

GitHub API를 사용하면서 익숙해진 근육 기억 때문에 API 서브도메인을 가리키고 있었지만, OAuth 흐름은 메인 사이트에서 동작합니다.

스택 개요

ComponentTechnology
FrameworkNext.js 16 (App Router)
LanguageTypeScript (strict mode)
DatabasePostgreSQL on Neon
GitHub APIOctokit
EmailResend
ValidationZod
TestingVitest (all passing)

모든 호스팅 제공업체에서 실행될 수 있도록 구축되었습니다. 현재는 저용량 트래픽을 위해 Vercel 무료 티어에서 운영 중입니다.

이 접근 방식이 실제 장점을 갖는 이유

  • 즉시 업데이트main에 수정 사항을 푸시하면 모든 고객이 git pull을 할 수 있어 zip 파일을 다시 다운로드할 필요가 없습니다.
  • 전체 git 히스토리 – 고객이 왜 그렇게 구현되었는지 확인할 수 있습니다.
  • 기본적으로 읽기 전용pull 권한으로 클론은 가능하지만 레포에 푸시할 수 없습니다.
  • 추가 도구 불필요git만 있으면 되며, 라이선스 키, 커스텀 CLI, 레지스트리 인증이 필요 없습니다.
  • 접근 권한 회수 가능 – 비용을 청구해야 할 경우, API 호출 하나로 협업자를 제거할 수 있습니다.

단점

  • 보류 중인 초대 50개 제한 – GitHub은 미처리 초대 수를 제한합니다.
  • GitHub 중심 – GitHub에 종속되어 있어 보편적인 솔루션이 아닙니다.

개발자에게 개발자 도구를 판매할 때는 이러한 단점이 보통 수용 가능합니다.

대신 사용하게 된 방법

도구를 만들면서 결제는 Polar.sh 로 전환했습니다. 그들은 제 비즈니스를 받아들였기 때문입니다 (Lemon Squeezy와는 달리). 그들의 플랫폼은 제가 다시 만들고 있던 모든 것을 이미 처리합니다:

  • GitHub 사용자 이름 수집
  • 레포지토리 초대
  • 접근 관리

요약하면, Polar는 제가 만들려고 했던 정확한 흐름을 자동화합니다:

  1. 고객이 결제합니다.
  2. Polar가 그들의 GitHub 사용자 이름을 수집합니다.
  3. Polar가 레포지토리 초대를 보냅니다.
  4. 고객이 접근 권한을 얻습니다.

제가 필요했던 것은 승인이었으며, 그 후에 기능을 켤 수 있었습니다.

회고

내 도구는 작동합니다: 모든 테스트가 통과하고 파이프라인이 실행되지만, 배포할 필요는 없었습니다. 그래도 많은 것을 배웠습니다:

  • 웹훅 서명 검증
  • OAuth 흐름
  • 지수 백오프를 이용한 재시도 시스템
  • GitHub 협업자 API가 실제로 어떻게 작동하는지

이러한 세부 사항은 튜토리얼에서는 얻기 힘든 것들입니다.

열린 질문

비슷한 솔루션에 $30을 청구하는 사람을 보고 직접 만들 수 있을 것 같아 이 프로젝트를 시작했습니다.

  • 누구든 실제로 이것을 유용하게 사용할까요?
  • 보일러플레이트나 스타터 킷을 판매하고 Polar의 내장 솔루션에 얽매이고 싶지 않다면, 이런 독립형 도구가 가치가 있을까요?
  • 오픈소스로 공개하는 것이 좋을까요?

댓글로 알려 주세요. 이걸 배포할지(오픈소스로 공개하거나 “커피 한 잔 사 주세요” 형태로 지원받을지) 아니면 학습 프로젝트로 유지할지 고민 중입니다. 판매하기엔 좀 무의미하게 느껴지지만, 만드는 과정은 즐거웠습니다.

TL;DR

Next.js, TypeScript와 웹훅 문서를 읽는 데 많은 시간을 투자하여 구축했습니다. 🚀

Back to Blog

관련 글

더 보기 »