README의 형용사가 아닌 CI 작업이 약속을 증명하는 PHP 스타터
요약
Pragmatic FrankenPHP는 단순한 데모용 스켈레톤을 넘어, 실제 프로덕션 환경의 교훈을 반영한 PHP 스타터 템플릿입니다. CI(지속적 통합)를 통해 이미지 부팅, AI 에이전트 호환성, 배포 검증 및 문서 일치성을 엄격하게 증명합니다.
핵심 포인트
- CI 작업을 통한 프로덕션 이미지 부팅 및 환경 검증
- AI 에이전트의 코드 생성 및 스타일 일관성 자동 확인
- SLSA 빌드 출처 및 서명된 아티팩트 검증 기능 포함
- 실제 도메인 로직(Vertical Slices)과 비동기 이벤트 구조 제공
대부분의 프로젝트 스켈레톤(Skeleton)은 데모에 불과합니다. composer install을 실행하고, hello 페이지를 서빙한 뒤, 실제 작업이 시작되는 지점에서 멈춰버리죠. 우리는 정반대의 길을 택했습니다. Pragmatic FrankenPHP는 실제 프로덕션 프로젝트가 실제로 성장해 나간 템플릿이며, 그 과정에서 얻은 교훈들을 다시 포팅한 것입니다. 이 템플릿으로 구축된 첫 번째 제품의 초기 커밋 40개 중 약 25개가 인프라 수리 작업이었습니다. 그 모든 커밋은 이제 템플릿에 미리 반영되어 있습니다.
github.com/k2gl/pragmatic-franken — PHP 8.5, Symfony 8, FrankenPHP 워커(worker) 모드, PostgreSQL 17, Vertical Slices + Messenger 기반 CQRS, MIT 라이선스.
믿는 것이 아니라 확인할 수 있는 주장들
README의 모든 마케팅 문구는 필수 CI(지속적 통합) 작업에 의해 뒷받침됩니다:
- "프로덕션 이미지가 부팅됩니다."
prod-image작업은 모든 PR(Pull Request)에서 실제php_prod타겟을 빌드하고, 이를 Postgres+Redis 환경에서 실행하며,/ready가{"ok":true,"db":true,"redis":true}를 반환하는지 확인(assert)합니다. (이 작업을 추가했을 때, 이전 이미지가 시작될 수 없었던 세 가지 독립적인 이유를 즉시 찾아냈습니다. 그것이 핵심입니다.) - "AI 에이전트가 여기서 통과(green)합니다."
agent-smoke작업은 에이전트가 사용하는 것과 동일한 생성기(generator)를 사용하여 버티컬 슬라이스(vertical slice)를 구성(make slice)하며, 수동 편집 없이 Pint + PHPStan 레벨 10 + 테스트를 통과해야 합니다. 첫 실행에서 스캐폴더(scaffolder)의 코드 스타일 드리프트(drift)를 잡아냈습니다. - "배포하는 것을 검증할 수 있습니다." 릴리스 이미지에는 SLSA 빌드 출처(build provenance, GitHub Artifact Attestations, keyless)가 포함됩니다. 배포 스크립트는 검증되지 않은 이미지를 거부할 수 있으며,
bin/console app:verify-attestation은 모든 아티팩트를 순수 PHP로 오프라인에서 검증합니다 (k2gl/sigstore-verify — 공식 sigstore-conformance 제품군을 통과함). 테스트 스위트는 실제 서명된 아티팩트를 커밋하고 실패 시 차단(fail-closed) 동작을 증명합니다: 단 1바이트만 변조되어도 거부됩니다. - "문서는 거짓말하지 않습니다."
make docs-check는 코드베이스를 대상으로 주장된 모든 라우트(route), make 타겟, ADR 참조를 grep으로 검색하여 확인하는 CI 게이트(gate)입니다.
에이전트 대상 컨텍스트 파일 (AGENTS.md, ≤2000 tokens, budget-linted)은 실제 상황과 조용히 괴리될 수 없습니다.
실제로 포함된 내용
- 실제 예시 수직 계층 (vertical): Task 엔티티 → 마이그레이션 (migration) → 타입이 지정된 리포지토리 (typed repository) → Foundry 팩토리 (factory) → 검증된 422 problem+json → E2E 테스트 → 비동기
TaskCompleted이벤트 →/tasks토픽으로의 Mercure 발행. 자신만의 도메인을 시작할 때 하나의 플래그로 이를 삭제할 수 있습니다 (make init name=my-app prune=1). - 프로덕션에서 포팅된 SharedKernel: 타입이 지정된
DoctrineRepository<T>(PHPStan 레벨 10, 매직(magic) 없음), RFC 9457 problem+json 리스너, 컨테이너 상태 확인 (healthchecks)을 위한 워커 하트비트 (worker heartbeat). - 프로덕션에서 끝나는 배포 시나리오:
/ready를 기준으로 게이트가 설정된 블루-그린 롤아웃 (blue-green rollout, 실패한 컨테이너는 기존 컨테이너를 교체하지 않음), 마이그레이션 전 덤프 (pre-migration dumps), 문서화된 복구 훈련이 포함된 암호화된 오프사이트 백업, 동적 업스트림 (dynamic upstreams)을 갖춘 Caddy 프론트엔드 — 이 모든 것이 저렴한 VDS 하나에서 실행됩니다. - 병렬 에이전트 세션:
dev/worktree.sh는 격리된 스택(자체 브랜치, 자체 도커 프로젝트, 자체 포트 슬롯)을 실행하여 여러 Claude/Codex 세션이 충돌 없이 작동할 수 있도록 합니다. - 불필요한 기능(bloat) 대신 선택적 레시피 (Opt-in recipes): JWT 인증 (프로덕션에서 검증된 설정), 피처 플래그 (feature flags), SPA 프론트엔드, PR 프리뷰 환경.
비목표 (의도적)
코어(core)에 인증 포함 안 함 (이는 제품 결정 사항이며, 레시피로 포함되어 있음), SPA 번들 미포함, Kubernetes 미사용, 멀티 DB 미사용. 거절하지 않는 템플릿은 모든 것을 축적하다가 결국 사장됩니다.
사용해보기
gh repo create my-app --template k2gl/pragmatic-franken --clone && cd my-app
make install && make init name=my-app && make smoke
# 또는 Packagist에서 직접 설치:
composer create-project k2gl/pragmatic-franken my-app
피드백을 환영합니다 — 특히 비판적인 피드백을 환영합니다. 17개의 모든 아키텍처 결정 기록 (architecture decision records)이 리포지토리에 있습니다.
AI 자동 생성 콘텐츠
본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기