Logocob

feature.yaml 스키마

cob compose 매니페스트 v1 전체 키 레퍼런스

feature.yaml 스키마 (매니페스트 v1)#

cob compose가 소비하는 feature 매니페스트의 전체 키 레퍼런스입니다. 파싱·검증 규칙의 단일 소스는 lib/src/models/feature_manifest.dart이며, 검증 실패는 전부 FormatException으로 즉시 종료(hard fail) 합니다 — 부분 생성은 없습니다.

v1에는 contract(메서드 시그니처) 선언이 없습니다. usecase 이름이 곧 repository 메서드 이름이라는 1:1 이름 규약으로 stub을 생성합니다 (예: get_noticesgetNotices). 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} 참조)
constanttrueConstant 클래스
exceptiontrueException 클래스
validationtrueValidation 클래스
helpertrueHelper 클래스
testtrue백엔드 테스트 골격
cachefalseCache 상수
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 모델
readmetruefeature 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_boardNoticeBoard)를 참조합니다. 따라서 다음 중 한 곳에서 반드시 공급되어야 합니다:

  • 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.yamlclass:/enum:/exception: 선언)과도 겹치면 안 됩니다.

충돌 시 출처별 해결책(이름 변경, 선언 제거로 기존 모델 재사용, 해당 토글 끄기)과 함께 hard fail합니다. 상세 메시지는 cob compose — 실행 전 가드를 참고하세요.

검증된 전체 샘플#

아래 매니페스트는 compose 통합 테스트 게이트(test/src/integration/compose_integration_test.dart)로 검증된 예시입니다 — fixture 모노레포에서 부품 배치, barrel 재생성, 라우터 등록, 구문 파싱 0오류, 잔여 Mustache 토큰 0건을 통과합니다. 동명 엔티티 계약을 models.entitiesnotice_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 선언으로 인해 자동 falseNoticeBoard는 placeholder가 아닌 b-entity로 생성
  • serverpod 모델: NoticeBoard, Notice (b-entity), NoticeBoardStatus (enum), DTO 5종

최소 매니페스트는 두 필수 키만으로 유효합니다 (frontend 부품 0개 + 백엔드 기본 토글):

feature: wallet
project_name: myproject

관련 문서#