feature.yaml 스키마 (매니페스트 v1)#
cob compose가 소비하는 feature 매니페스트의 전체 키 레퍼런스입니다. 파싱·검증 규칙의 단일 소스는 lib/src/models/feature_manifest.dart이며, 검증 실패는 전부
FormatException으로 즉시 종료(hard fail) 합니다 — 부분 생성은 없습니다.
v1에는 contract(메서드 시그니처) 선언이 없습니다. usecase 이름이 곧 repository 메서드 이름이라는 1:1 이름 규약으로 stub을 생성합니다 (예:
get_notices → getNotices). contract 선언은 M3에서 검토됩니다.
최상위 키#
| 키 | 타입 | 필수 | 기본값 | 검증 규칙 |
|---|---|---|---|---|
feature |
string | 필수 | - | 비어 있지 않은 snake_case (^[a-z][a-z0-9_]*$) |
project_name |
string | 필수 | - | 비어 있지 않은 문자열 (모노레포의 프로젝트 이름, snake_case 권장) |
targets |
string 배열 | 선택 | [application] |
빈 배열 불가. 허용값: application, console. 그 외 값은 hard fail |
network |
string | 선택 | serverpod |
v1은
serverpod
만 허용. 다른 값은 hard fail (mixin 템플릿이 실재하는 다른 network가 필요하면
cob add
사용)
|
frontend |
맵 | 선택 | 빈 선언 |
하위에
usecases
/
blocs
/
widgets
(
아래
)
|
models |
맵 | 선택 | 빈 선언 | 하위에 entities (아래) |
backend |
맵 | 선택 | 토글별 기본값 | boolean 토글 13종만 허용. 알 수 없는 키는 hard fail (아래) |
최상위가 맵이 아니거나 YAML 파싱에 실패해도 hard fail입니다.
frontend 섹션#
frontend:
usecases: [get_notices, create_notice]
blocs:
- { name: notice_list, usecases: [get_notices] }
- { name: notice_form, usecases: [] }
widgets: [notice_card]
| 키 | 타입 | 기본값 | 설명 |
|---|---|---|---|
usecases |
string 배열 | [] |
생성할 usecase 이름 목록. 각 이름은 snake_case 필수. 1:1 규약으로 camelCase repository 메서드가 파생됨 |
blocs | 배열 | [] | 생성할 BLoC 선언 목록 (아래 구조) |
widgets |
string 배열 | [] |
생성할 widget 이름 목록. 각 이름은 snake_case 필수 |
v1 스키마에 pages 키는 없습니다. 페이지 골격은 base feature 브릭이 feature 단위로 생성합니다.
blocs 항목 구조#
| 키 | 타입 | 필수 | 검증 규칙 |
|---|---|---|---|
name |
string | 필수 | snake_case. 누락·빈 문자열이면 hard fail |
usecases |
string 배열 | 선택 (기본 []) |
BLoC 생성자에 주입할 usecase 목록.
frontend.usecases에 선언된 이름만 참조 가능
— 선언되지 않은 usecase 참조 시 hard fail
|
항목이 맵이 아닌 평문 문자열이면 그 문자열을 이름으로, 주입 usecase는 빈 목록으로 처리합니다.
models.entities#
models:
entities:
- { name: notice, fields: { title: String, content: String } }
| 키 | 타입 | 필수 | 검증 규칙 |
|---|---|---|---|
name |
string | 필수 |
snake_case. 누락 시 hard fail.
같은 이름을 중복 선언하면 hard fail
(
entity 이름이 중복 선언되었습니다
)
|
fields |
맵 (필드명 → Dart 타입) | 선택 (기본 {}) |
키·값 모두 문자열로 읽음 |
각 항목은 반드시 {name, fields} 맵이어야 합니다 (스칼라 불가). 선언된 entity는 b-entity 브릭으로 backend/{project_name}_server에
{name}.spy.yaml로 생성되고, 프론트 도메인 entity 브릭에는 같은 필드가 name:Type CSV로 전달됩니다.
entities를 하나라도 선언하면
backend.entity기본값이false로 바뀝니다 — placeholder 중복 생성을 막기 위함입니다 (아래 표 참고).
backend 토글 13종#
backend_feature 브릭의 boolean 토글입니다. 매니페스트에서는 has_ 접두사 없이 선언하며 (예:
cache: true), compose가 13개 has_* 변수를 전부 명시해 브릭에 전달합니다. 알 수 없는 키는 지원 목록과 함께 hard fail합니다.
| 키 | 기본값 | 생성/억제 대상 |
|---|---|---|
endpoint |
true |
Endpoint 클래스 (feature 동명 엔티티 {Feature} 참조) |
console_endpoint |
false |
Console 전용 Endpoint ({Feature} 참조) |
service |
true |
Service 클래스 ({Feature} 참조) |
constant | true | Constant 클래스 |
exception | true | Exception 클래스 |
validation | true | Validation 클래스 |
helper | true | Helper 클래스 |
test | true | 백엔드 테스트 골격 |
cache | false | Cache 상수 |
dto |
true |
DTO 5종 spy 모델:
{Feature}CreateRequest
/
{Feature}CreateResponse
/
{Feature}UpdateRequest
/
{Feature}UpdateResponse
/
Paginated{Feature}Result
(
{Feature}
참조)
|
entity |
models.entities 미선언 시 true, 선언 시 false |
feature 동명 entity placeholder spy (status: {Feature}Status? 필드를 하드 참조) |
enum |
true |
{Feature}Status enum spy 모델 |
readme | true | feature README |
기본값은 구 post_gen _createBackendFeature가 하드코딩하던 값과 동일합니다. entity만 조건부입니다: 매니페스트에 entities를 선언하면 b-entity 브릭이 실제 엔티티를 생성하므로 placeholder는 자동으로 꺼집니다.
이름 규약 (snake_case 강제)#
requireSnakeCase()가 정규식 ^[a-z][a-z0-9_]*$로 검사하며, 위반 시 "{대상} 이름은 snake_case여야 합니다: {값}"
FormatException을 던집니다.
적용 대상: feature, 모든 usecase, 모든 bloc 이름(맵 형식 선언 시), 모든
widget, 모든 entity 이름.
따라서 NoticeBoard(PascalCase), wallet-list(kebab-case), GetWallet
등은 전부 거부됩니다.
snake_case를 강제하는 이유: mason 브릭 템플릿은 ReCase 변환(paramCase(), pascalCase()
등)으로 이름을 파생하고, CLI 측도 같은 이름에서 camelCase/PascalCase를 파생합니다 (예: usecase → repository 메서드, feature → {Feature}
모델명). 입력이 snake_case로 정규화되어 있어야 양쪽 변환 결과가 항상 일치합니다.
pre-flight 계약 (compose가 요구하는 의미적 제약)#
스키마 검증을 통과해도, compose의 백엔드 모델 pre-flight(_checkBackendModelPreflight)가 매니페스트에 다음 의미적 제약을 추가로 요구합니다. 백엔드 서버 디렉토리(backend/{project_name}_server)가 없으면 백엔드 합성 자체가 스킵되므로 이 검사도 스킵됩니다.
동명 엔티티 계약#
backend.endpoint / console_endpoint / service / dto
중 하나라도 켜져 있으면, 생성되는 골격이 feature와 동명인 protocol 클래스 {Feature}(예: notice_board
→ NoticeBoard)를 참조합니다. 따라서 다음 중 한 곳에서 반드시 공급되어야 합니다:
-
models.entities에 feature 동명 엔티티 선언:- { name: notice_board, fields: { ... } } backend.entity: true(placeholder 생성)- 서버의 기존 spy 모델에 이미 존재
어디서도 공급되지 않으면 hard fail하며, 대안으로 backend: { endpoint: false, console_endpoint: false, service: false, dto: false }를 안내합니다.
enum 역계약#
backend.entity가 placeholder를 생성하면 그 템플릿이 status: {Feature}Status? 필드를 하드 참조합니다. 따라서
backend.enum: false로 끄려면 기존 spy 모델에 {Feature}Status enum이 이미 있어야 합니다. 없으면 hard fail —
backend.enum: true로 되돌리거나 placeholder 대신 models.entities에 직접 선언하세요.
전역 네임스페이스 유일성#
serverpod 모델명(class/enum/exception)은 서버 전역 네임스페이스를 공유합니다. 생성 예정 모델명 — entities의 PascalCase 이름, placeholder
{Feature}, {Feature}Status enum, DTO 5종 — 은:
- 매니페스트 내부에서 서로 중복되면 안 되고 (예: entities 선언 ↔ placeholder가 같은 이름),
-
기존 spy/protocol 모델(
.spy.yaml의class:/enum:/exception:선언)과도 겹치면 안 됩니다.
충돌 시 출처별 해결책(이름 변경, 선언 제거로 기존 모델 재사용, 해당 토글 끄기)과 함께 hard fail합니다. 상세 메시지는 cob compose — 실행 전 가드를 참고하세요.
검증된 전체 샘플#
아래 매니페스트는 compose 통합 테스트 게이트(test/src/integration/compose_integration_test.dart)로 검증된 예시입니다 — fixture 모노레포에서 부품 배치, barrel 재생성, 라우터 등록, 구문 파싱 0오류, 잔여 Mustache 토큰 0건을 통과합니다. 동명 엔티티 계약을
models.entities의 notice_board 선언으로 충족하는 형태입니다.
feature: notice_board
project_name: myproject
targets: [application]
network: serverpod
frontend:
usecases: [get_notices, get_notice_detail, create_notice]
blocs:
- { name: notice_list, usecases: [get_notices] }
- { name: notice_form, usecases: [create_notice] }
widgets: [notice_card, notice_list_tile]
models:
entities:
# backend_feature의 endpoint/service/dto 골격은 feature 동명 엔티티를 참조
- { name: notice_board, fields: { title: String } }
- { name: notice, fields: { title: String, content: String } }
backend:
cache: true
이 선언의 파생 결과:
-
repository 메서드:
getNotices,getNoticeDetail,createNotice(1:1 규약) -
backend.entity는 entities 선언으로 인해 자동false—NoticeBoard는 placeholder가 아닌 b-entity로 생성 -
serverpod 모델:
NoticeBoard,Notice(b-entity),NoticeBoardStatus(enum), DTO 5종
최소 매니페스트는 두 필수 키만으로 유효합니다 (frontend 부품 0개 + 백엔드 기본 토글):
feature: wallet
project_name: myproject
관련 문서#
- cob compose 명령 — 실행 흐름, 옵션, 실행 전 가드 4종, 생성 후 수동 단계
- Compose 튜토리얼 — 매니페스트 작성부터 codegen까지 E2E 가이드