
GitHub Template Repository × Reusable Workflow로 개별 기업 최적화를 양산하기
요약
AI 시대의 낮은 구현 단가를 활용하여, 공통 플랫폼의 제약에서 벗어나 GitHub Template Repository와 Reusable Workflow로 개별 기업별 최적화된 코드를 양산하는 전략을 제안합니다.
핵심 포인트
- 공통 기반 강화와 개별 최적화 유연성 사이의 충돌 해결
- GitHub Template Repository와 Reusable Workflow를 통한 경량 구현
- AI를 활용한 개별 리포지토리 생성 방식의 경제성 확보
- SaaS의 구조적 제약인 DSL 기반 커스터마이징의 한계 극복
공통 기반을 두텁게 할수록, 개별 기업을 위해 자유롭게 코드를 작성할 수 없게 된다. SaaS적인 공통 플랫폼을 정비하는 운용 방식에서는, 규모가 커질수록 개별 기업 고유의 예외 사항을 작성할 때마다 「공통 기능의 제약에 맞추기 위한」 조정 비용이 불어납니다.
AI 시대에 구현 단가가 낮아진 지금, 개별 기업마다 코드를 새로 작성하는 것이 공통 기반에 맞춰 타협하는 것보다 빠른 상황이 늘어나고 있습니다. SAP S/4HANA처럼 「베이스를 기업별로 deploy하여 커스터마이징」하는 운용 방식과 발상은 같으며, 이를 GitHub의 두 가지 기능 — Template Repository (형태의 배포)와 Reusable Workflow (공통 CI만 집약) — 로 경량하게 구현할 수 있습니다.
3가지 구현 모델의 정리부터 시작하여, GitHub 상에서의 구체적인 절차와 주의할 점(ハマりどころ)까지 차례대로 작성하겠습니다.
kintone, Salesforce, Zoho. 이것들은 「커스텀 필드(Custom Field)」, 「커스텀 오브젝트(Custom Object)」를 사용자 측에서 정의할 수 있는 설계입니다. 이용자에게는 편리하지만, 제공자 입장에서 보면, 사용자가 화면에서 정의한 스키마(Schema)를 동적으로 해석하여 UI·API·권한 제어를 자동 생성하는 메커니즘을 제품 본체에 구축해야 합니다.
즉, DSL (도메인 특화 언어, Domain Specific Language)과 그것을 해석하여 구동하는 인터프리터(Interpreter)를 통째로 제품에 갖게 됩니다. 사용자가 정의한 필드에 맞춰 DB 열을 만들고, UI를 그리고, API를 공개하며, 권한을 판정합니다. 이 일련의 처리 기반이 그대로 제품 코어의 공수가 됩니다. 이용자 측도 DSL의 학습 비용을 지불해야 하며, kintone의 계산식이나 Salesforce의 수식 항목을 다룰 수 있는 담당자가 없으면 어렵게 만든 커스터마이징 기구가 빛을 발하지 못합니다.
그리고 앞서 언급한 세 가지는 SaaS 중에서도 상대적으로 유연한 부류입니다. 많은 SaaS는 애초에 개별 기업 커스터마이징 기구를 갖추지 않고, 제공 기능을 「있는 그대로 사용하는」 것을 전제로 만들어져 있습니다. 유연성을 부여하면 DSL 기반의 무거움을 떠안게 되고, 부여하지 않으면 개별 기업 대응의 자유도가 없습니다. SaaS라는 카테고리 전체에 공통되는 구조적인 제약입니다.
「공통 기반을 정비할수록 스케일 메리트(Scale Merit)가 나온다」는 것은 SaaS의 기본 전제입니다. AI 시대에는 이 전제가 일부 반전됩니다. 공통 기반을 두텁게 할수록, 개별 기업을 위한 「사소한 예외」를 쓰기 어려워지기 때문입니다.
구체적으로는 규모가 커질수록 다음과 같은 제약이 쌓입니다.
- 공통 기능을 늘릴수록, 개별 기업 고유의 예외 패턴을 「규약에 맞추기 위한」 비용이 증가한다
- 공통 기반의 버전 업그레이드로 인해 개별 기업의 구현이 깨지는 사태가 발생하여, 추종(Follow-up) 비용이 발생한다
- 개별 기업을 위해 AI로 구현할 때, 공통 기반의 제약을 모두 프롬프트(Prompt)에 실어야 하므로 설계와 생성 속도가 떨어진다
공통 기반의 이점(스케일 메리트)과 개별 기업 최적화의 유연성(AI로 개별 기업별로 새로 작성하는 속도)이 규모가 커질수록 직접적으로 충돌합니다.
여기에 최근의 변화가 더해집니다. 코딩 AI를 통해 「개별 기업마다 1개의 리포지토리(Repository)를 새로 작성하는 것」이 현실적인 비용 범위 내로 들어오는 시대가 되었습니다. 10년 전이라면 한 고객을 위해 시스템을 스크래치(Scratch)로 작성하는 것은 생각할 수 없었지만, 지금은 설계와 요건만 확정되면 코드는 AI에게 맡길 수 있는 범위가 넓습니다.
개별 기업마다 별도의 리포지토리로 관리하면, 공통 기반의 제약을 프롬프트에 실을 필요가 없으며 AI에게 「이 고객을 위해 자유롭게 작성해줘」라고 지시할 수 있습니다. 공통 기반의 추종 비용도 없으며, 개별 기업의 예외 패턴을 쓰기 쉽습니다. AI의 유연함을 가장 잘 활용할 수 있는 구조입니다.
「SaaS is Dead」 논의의 핵심은 UI로 완결되는 단일 기능 SaaS가 AI 에이전트(Agent)로 대체되는 것입니다. 반면 백엔드(Backend)의 업무 로직은 개별 기업에 최적화하는 편이 가치가 높아지는 방향으로 재편되고 있습니다.
업무 애플리케이션을 「개별 기업의 차이를 어떻게 흡수할 것인가」로 분류하면, 구현 모델은 크게 3가지로 정리할 수 있습니다.
| 모델 | 예 | 개별 기업 차이의 처리 | 구현 비용 |
|---|---|---|---|
| 완전 SaaS (DSL형) | kintone, Zoho, Salesforce | 커스텀 필드 DSL로 사용자가 정의 | 매우 무거움 (제품 본체가 DSL 기반) |
| ... |
완전 SaaS (DSL형)은 규모가 커졌을 때 제품 코어의 초기 투자를 회수할 수 있는 구조입니다. kintone이나 Salesforce와 같은 거대한 규모의 투자가 전제됩니다.
개별 기업별 인스턴스 (포크(Fork) 또는 개별 기업별 deploy)는 SAP S/4HANA의 커스터마이징 운용이 대표적인 예입니다. 코드는 공통이라도 기업마다 별도의 환경을 가지므로 운용 부하가 높고, 업그레이드 추종에도 기업별 공수가 필요하게 됩니다.
중단의 「AI 코어 + 개별 기업 어댑터 (Adapter)」는 UiPath나 Automation Anywhere와 같은 업무용 AI 제품에서 채택하고 있는 모델입니다. 코어 로직 (Core Logic)은 공통으로 두고, 마스터 데이터의 입출력과 외부 시스템 연동만을 개별 기업별 모듈로 만드는 발상입니다. 중단 방식은 코어 부분이 공통 모듈로서 고정되기 때문에, AI로 개별 기업을 최적화할 때의 자유도는 그 범위 내로 제약됩니다.
하단의 「개별 기업별 인스턴스 (Instance)」는 SAP S/4HANA 커스터마이징이 대표적인 예입니다. 코드는 공통의 출발점을 가지면서도, 기업마다 별도의 환경과 별도의 배포 (Deploy)로 운용하는 모델입니다. 본 기사가 목표로 하는 것은 바로 이 방향이며, Template Repository (공통의 출발점을 배포) + Reusable Workflow (CI/CD만 집약) 라는 형태로 GitHub 상에 경량하게 재현합니다. 중단 방식과의 차이점은 공통화하는 범위를 최소한 (CI/CD만)으로 한정한다는 것입니다. 본체 코드는 기업이 자유롭게 작성할 수 있는 상태를 유지함으로써, AI를 통한 개별 기업 최적화의 유연성을 확보합니다.
리포지토리의 Settings → 「Template repository」를 체크하면 끝입니다. 이것만으로 「Use this template」 버튼이 활성화되어, 버튼을 통해 신규 리포지토리를 생성할 수 있습니다.
포크 (Fork)와의 차이점은 커밋 히스토리 (Commit History)를 승계하지 않는다는 점입니다. 신규 리포지토리에는 템플릿의 최신 커밋 내용만 복사되며, 부모 리포지토리와의 기여 관계도 가지지 않습니다. 「개별 기업을 위한 완전 독립 리포지토리」를 만드는 데 적합한 동작입니다.
템플릿에 포함할 후보와 포함하지 않을 후보의 경계 설정이 향후 추종 비용을 크게 좌우합니다.
| 포함 (공통 기반) | 포함하지 않음 (기업별로 재작성) |
|---|---|
| 디렉토리 구성 | 업무 로직 본체 |
| 공통 설정 (tsconfig, eslint, .gitignore, .editorconfig) | 환경 변수 · 시크릿 (Secret)의 실체 |
.github/workflows/ci.yml (Reusable Workflow를 호출하기만 하는 파일) | 마스터 데이터 · 스키마 정의 |
| README, CLAUDE.md, docs/의 템플릿 | 기업 고유의 인증 정보 · 접속처 |
판단 기준은 「모든 고객에게 동일하게 적용되는가」 또는 「기업이 나중에 재작성할 것을 전제로 하는가」 중 어느 쪽에 해당하는가입니다. 망설여진다면 포함하지 않는 것이 안전합니다.
my-template/
├── .github/
│ └── workflows/
...
src/adapters/
와 같이 「여기에 기업별 차이점을 모은다」라는 경계를 템플릿 스스로가 선언하고 있는 것이 포인트입니다. 신규 고객 대응 시 고민될 때의 판단 기준이 됩니다.
공통 CI를 둘 장소로서, org/shared-workflows와 같은 전용 리포지토리를 하나 준비합니다. .github/workflows/build.yml에 on: workflow_call:을 작성하기만 하면, 외부 리포지토리에서 호출할 수 있는 워크플로우가 됩니다.
on:
workflow_call:
inputs:
...
호출 측 (고객 리포지토리)의 CI 파일은 Reusable Workflow를 uses:로 가리키기만 하면 됩니다.
name: CI
on:
push:
...
이로써 개별 기업 리포지토리의 CI 설정이 「호출만 하면 되는」 형태로 압축됩니다. 공통 수정 사항은 shared-workflows 측에 PR (Pull Request)을 보내면, 이를 사용 중인 모든 리포지토리가 다음 태그 업데이트 시점에 반영할 수 있습니다.
Reusable Workflow를 uses:로 참조할 때, @<ref>를 통해 브랜치 이름, 태그, 또는 커밋 SHA 중 하나를 지정합니다. 여기서 무엇을 가리키느냐가 운용의 안정성을 크게 좌우합니다.
피해야 할 것은 @main과 같은 브랜치 참조입니다. 공통 CI 측의 main이 한 번이라도 망가지는 순간, 이를 참조하고 있는 모든 리포지토리의 CI가 동시에 중단되기 때문입니다. 공통 CI는 많은 개별 기업 리포지토리로부터 참조되는 성질상, 「망가진 상태가 한꺼번에 전파되는」 리스크가 구조적으로 존재합니다.
실용적인 방법은 @v1과 같은 Moving Major Tag를 만들어 두는 운용 방식입니다. v1.0.0 → v1.1.0과 같은 호환 가능한 수정은 그대로 추종하게 하고, Breaking Change는 v2를 발행하여 사용하는 측이 명시적으로 올리기 전까지는 반영되지 않도록 합니다. GitHub 공식 액션 (actions/checkout...
등과 동일한 버전 관리 (Versioning) 전략을 사용하여, 소비자의 수만큼 "망가지는 순간 동시에 다운되는" 리스크를 줄일 수 있습니다.
혼동하기 쉬운 3가지 기능의 용도 차이를 정리해 둡니다.
| 기능 | 단위 | 배치 장소 | 용도 |
|---|---|---|---|
| Reusable Workflow | Job | .github/workflows/ 직하 | 공통 CI/CD 로직 전체를 집약 |
| Composite Action | Step | action.yml | 세부 처리의 부품화 |
| Workflow Templates | 파일 템플릿 | org .github 리포지토리 | 신규 워크플로우 작성을 위한 스켈레톤 |
Reusable Workflow는 별도의 Job으로 실행되므로, 파일 시스템은 호출 측과 공유되지 않습니다. "빌드 결과물을 전달하는" 것과 같은 케이스는 artifact에 의존하게 됩니다. Step 단위로 가볍게 재사용하고 싶다면 Composite Action이 적합합니다.
템플릿을 얇게 유지하는 것이 이 구성의 핵심입니다. 운영하다 보면 "공통적인 부분은 일단 템플릿에 넣자"라는 유혹을 이기지 못하는 순간이 생깁니다. 기억해야 할 점은, 템플릿에 넣은 것은 개별 기업 측에서 다시 쓰기 어려워진다는 비대칭성입니다.
예를 들어 ORM의 스키마 정의는 템플릿에 넣고 싶어지겠지만, 각 고객마다 스키마가 다를 것이므로 넣어서는 안 됩니다. 템플릿 측의 ORM 버전을 올리는 수정을 했을 때, 각 고객 리포지토리의 스키마와 충돌하여 머지(Merge)할 수 없는 사고가 발생합니다. 템플릿에 포함해야 할 것은 "Prisma를 사용한다는 전제"뿐이며, 스키마 본체는 개별 기업 측에 있어야 합니다.
Use this template로 만든 리포지토리는 독립된 리포지토리입니다. 템플릿 측을 개정해도 소비 측에 자동으로 반영되지 않습니다. 수동으로 반영하려면 git remote add template ...를 하여 머지하는 운영 방식이 되지만, 개별 기업에서 수정된 파일과의 충돌이 늘어날수록 작업이 힘들어집니다.
실용적인 타협점은 **"템플릿을 얇게 유지하는 것"**입니다. 공통화하고 싶은 것은 Reusable Workflow 측으로 몰아넣고, 템플릿에는 "발판과 경계의 선언"만을 남깁니다. 템플릿 자체에 두께를 갖게 하면, 추종 비용이 개별 기업 수에 비례하여 증가합니다.
"얇게 유지하기"가 효과를 발휘하는 또 다른 이유는 AI를 통한 기업별 최적화의 유연성을 빼앗지 않기 위해서입니다. 템플릿이 두꺼울수록, 개별 기업에서 코드를 수정할 때마다 템플릿 측의 전제를 AI에게 컨텍스트(Context)로 읽게 해야 하므로, "이 기업에 맞춰 자유롭게 작성해줘"라고 지시하기 어려워집니다.
Reusable Workflow를 호출할 때 secrets: inherit를 사용하면, 호출 측의 모든 Secret이 공통 CI 측으로 전달됩니다. 편리하지만, 공통 CI 코드를 작성할 수 있는 사람이라면 누구나 각 기업의 Secret을 참조할 수 있는 상태가 됩니다. 신뢰 경계(Trust boundary)가 모호해지므로, 명시적 전달 방식으로 전환하는 것이 안전합니다.
참고로 secrets: inherit는 동일한 organization 또는 동일한 enterprise 내의 reusable workflow 호출에서만 동작합니다. org를 넘나드는 참조에서는 명시적 전달이 필수입니다.
jobs:
build:
uses: org/shared-workflows/.github/workflows/build.yml@v1
...
GitHub 공식의 2026년 Actions 보안 로드맵에 따르면, secrets: inherit를 통한 암묵적 상속이 폐지되고, scoped secrets(실행 컨텍스트에 명시적으로 바인딩된 Secret)로의 이행이 예정되어 있습니다. 명시적 전달 방식으로 맞춰두면, 향후의 사양 변경에도 견딜 수 있는 작성 방식이 됩니다.
기능을 어디에 둘지 고민된다면, 3개 층으로 나누어 생각하면 정리할 수 있습니다.
- 코어 (Core): 빌드, 테스트, lint, format, 배포 골격, 보안 스캔 (
shared-workflows리포지토리) - 어댑터 (Adapter, 템플릿의 scaffold): 개별 기업의 차이점이 들어갈 자리를 타입(Type)으로서 선언만 함 (
src/adapters/,config/customer.yaml등) - 개별 기업 (Individual,
Use this template로 만든 기업 리포지토리): 마스터 I/O, 출력 어댑터, 업무 규칙, 환경 변수의 실체
코어는 CI 로직, 어댑터는 기업이 채워 넣을 빈칸, 개별 기업은 구현 본체라는 역할 분담입니다.
새로운 기능을 어디에 둘지 고민된다면, 다음을 순서대로 질문하십시오.
- 모든 소비자에게 완전히 동일한 코드로 동작하는가? → 코어 (Reusable Workflow) - 형태는 정해져 있지만
- 내용물은 개별 기업마다 다른가? → 어댑터 (Adapter)의 타입을 템플릿에 넣음 - 형태조차도 개별 기업에 따라 다른가? → 개별 기업 리포지토리 (Repository)에 맡김
고민될 때는 개별 기업 측에 두는 것이 안전합니다. 나중에 코어로 끌어올리는 것은 비용이 적게 들지만, 코어에서 개별 기업으로 분리하는 것은 비용이 많이 듭니다. 코어로 끌어올리는 작업은 단순한 추상화(Abstraction)이지만, 코어에서 개별 기업으로 분리하는 작업은 "다른 기업에서는 사용하고 싶다", "아니, 사용하지 않겠다"를 기업마다 하나하나 확인해야 하는 정치적 비용(Political Cost)이 수반됩니다.
이 패턴은 수탁(SI) 중심의 운영에 적합합니다. "모든 고객에게 동일한 CI/CD 품질을 배포한다"는 니즈가 강하며, Reusable Workflow 측에서 공통 기반의 실적이 쌓이기 때문입니다.
자사 프로덕트 + 대형 엔터프라이즈(Enterprise)를 위한 개별 튜닝의 케이스에서는, 템플릿을 얇게 유지하면서 공통 기능은 코어 SDK를 npm/PyPI 패키지로 배포하는 것이 경계(Boundary)를 깔끔하게 만듭니다. CI 설정과 애플리케이션 코드에서는 공통화의 단위가 다르다는 점을 의식하면 고민이 줄어듭니다.
완전 SaaS 형태의 DSL 기반은 무거우며, 공통 기반화를 진행할수록 AI 시대의 개별 기업 최적화 유연성이 깎여 나갑니다. GitHub Template Repository + Reusable Workflow는 "공통화는 CI/CD에만 한정하고, 본체는 개별 기업에서 자유롭게 작성한다"라는 구조를 경량으로 구현하는 수단입니다. 경계 설계 (코어·어댑터·개별 기업의 3계층)를 처음에 결정함으로써, 공통화의 이점과 개별 기업 최적화의 유연성 사이의 균형을 잡기 쉬워집니다.
남은 과제는 템플릿 자체의 버전 관리 — 템플릿 측의 업데이트를 개별 기업 리포지토리에 추종시키는 메커니즘 — 의 설계입니다. Renovate를 사용하여 .template-version과 같은 표식 파일의 업데이트를 통지하는 안 등이 생각될 수 있으나, 이 부분의 자동화는 아직 실용적인 패턴이 정립되지 않은 영역으로 보입니다.
AI로 인해 개별 기업 구현의 공수(Man-month)가 낮아진 세상에서는, 공통 플랫폼과 개별 기업 최적화 사이의 균형이 재평가되고 있습니다. GitHub의 두 기능은 그 균형을 지금 바로 손안에서 조립할 수 있는 형태로 만들어 주는 조합입니다.
AI 자동 생성 콘텐츠
본 콘텐츠는 Qiita AI의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기