Logocob

cob compose

매니페스트(feature.yaml) 선언으로 풀스택 feature 골격 일괄 생성

cob compose#

매니페스트 v1(feature.yaml) 선언 한 장으로 풀스택 feature 골격을 1회(one-shot) 생성합니다.

cob compose --manifest feature.yaml
cob compose -m feature.yaml -d ../my_project --force

실행 흐름:

  1. 매니페스트 v1(feature.yaml) 파싱·검증 (실패 시 hard fail)
  2. atomic brick(usecase/bloc/widget/entity/repository/b-entity) N회 generate
  3. 배치 규칙에 따라 단일 feature 패키지(feature/{target}/{feature})로 병합
  4. barrel(usecase/entity/bloc/widget) 재생성 — atomic brick은 자기 파일만 생성하므로 barrel은 cob 책임
  5. 라우터 마커 기반으로 라우트 등록 + workspace pubspec 갱신

백엔드는 backend_feature 브릭 1회 + (entities 선언 시) b-entity N회 generate로 backend/{project_name}_server에 합성됩니다. 기존 feature에 부품을 추가하려면 cob compose가 아닌 cob add를 사용하세요.

옵션#

옵션약어설명기본값
--manifest -m feature 매니페스트 파일 경로 feature.yaml
--project-dir-d대상 프로젝트 디렉토리현재 디렉토리
--bricks-dir -b bricks/ 디렉토리 경로 자동 탐지 (상위 디렉토리 순회)
--force-git-clean 가드 우회 (one-shot 가드는 우회 불가)false

실행 전 가드 4종#

compose는 파일을 하나라도 만들기 전에 아래 가드를 순서대로 검사합니다. 하나라도 실패하면 즉시 종료하고 아무것도 생성하지 않습니다.

1. one-shot 가드#

대상 feature 디렉토리(feature/{target}/{feature} 또는 백엔드 lib/src/feature/{feature})가 하나라도 이미 존재하면 hard fail합니다.

❌ 대상 feature가 이미 존재합니다 (compose는 1회용):
   • feature/application/notice_board

   기존 feature에 부품을 추가하려면 cob add를 사용하세요.:
   cob add usecase <name> --feature notice_board

--force로도 우회할 수 없습니다. compose는 빈 슬레이트 전제의 1회용 명령입니다.

2. git-clean 가드#

git status --porcelain으로 작업트리를 검사합니다. 더러우면 거부하고 커밋/스태시를 안내합니다 (변경 목록 최대 10줄 표시).

❌ git 작업트리가 깨끗하지 않습니다. compose 실패 시 복구를 위해
   커밋/스태시 후 다시 실행하세요 (--force로 우회 가능).
  • --force로 우회 가능 (경고 출력 후 계속)
  • git 저장소가 아니면 차단하지 않고 "실패 시 자동 복구 수단이 없습니다" 경고만 출력

3. 라우터 마커 pre-flight#

target별 라우트 파일(applicationapp_routes.dart, consoleconsole_routes.dart)을 찾고, 라우트 배열 안에 AUTO-REGISTER 마커가 있는지 생성 전에 확인합니다. 파일이 없거나 마커가 없으면 실패합니다.

// 🔧 AUTO-REGISTER: 새 라우트를 여기에 추가하세요

마커가 없으면 위 마커를 라우트 배열 안에 추가한 뒤 다시 실행하라고 안내합니다. 합성 성공 후에는 이 마커 위치에 {Feature}RouteName.base 라우트가 자동 등록됩니다.

4. 백엔드 모델 pre-flight#

serverpod 모델명(class/enum/exception)은 서버 전역 네임스페이스를 공유하므로, serverpod generate 단계까지 가지 않고 생성 전에 충돌을 4가지로 검사합니다. 백엔드 서버 디렉토리가 없으면 (백엔드 합성 자체가 스킵되므로) 이 검사도 스킵됩니다.

#검사실패 메시지 요지해결 안내
4-a 내부 중복 — 생성 예정 모델명이 매니페스트 안에서 서로 겹침 (entities ↔ backend_feature placeholder/enum/dto) 생성 예정 serverpod 모델명이 매니페스트 안에서 중복됩니다 + 충돌 쌍 목록 entity 이름 변경, 또는 겹치는 backend: 토글(entity/enum/dto) 끄기
4-b 기존 spy 충돌 — 생성 예정 모델명이 서버의 기존 .spy.yaml/protocol 모델과 겹침 serverpod 모델명이 기존 spy 모델과 충돌합니다 (전역 네임스페이스) + 기존/생성 예정 출처 entities가 출처면 이름 변경·선언 제거(기존 모델 재사용), backend_feature가 출처면 해당 토글 끄기 또는 feature 이름 변경
4-c 동명 엔티티 계약 — endpoint/service/dto 골격은 feature 동명 protocol 클래스( {Feature} )를 참조하는데 어디서도 공급되지 않음 endpoint/service/dto 골격은 feature 동명 엔티티({Feature})를 참조하지만 어디서도 공급되지 않습니다 models.entities 에 동명 엔티티 추가, backend.entity: true (placeholder 생성), 또는 endpoint/console_endpoint/service/dto 토글 전부 끄기
4-d placeholder → enum 역계약 — entity placeholder 템플릿이 status: {Feature}Status? 를 하드 참조하는데 enum 토글이 꺼져 있고 기존 모델에도 없음 entity placeholder는 {Feature}Status enum을 참조하지만 enum 토글이 꺼져 있고 기존 모델에도 없습니다 backend.enum: true로 되돌리거나, placeholder 대신 models.entities에 직접 선언

생성 후 수동 단계#

cob은 codegen을 자동 실행하지 않습니다. 합성 완료 후 출력되는 가이드를 따라 직접 실행하세요.

cd backend/{project_name}_server && serverpod generate   # 1. serverpod 산출물
melos run build                                          # 2. build_runner 산출물
melos run analyze                                        # 3. 검증

추가 수동 작업:

  • serverpod_service getter 스니펫: network: serverpod이면 클라이언트 래퍼 getter를 출력해 줍니다. getter가 2계열이라 자동 추가하지 않으므로 package/network/serverpod_service에 직접 붙여넣으세요.
// package/network/serverpod_service의 클라이언트 래퍼에 추가:
EndpointNoticeBoard get noticeBoard => client.noticeBoard;
  • BLoC 와이어링: 생성자 주입 전용입니다 (getIt/injectable 미사용, service_locator 등록 없음). 출력되는 BlocProvider 예시를 참고해 직접 연결하세요.

매니페스트 스키마#

feature.yaml(v1) 필드 상세는 Feature 매니페스트 레퍼런스를 참고하세요.

예시#

최소 매니페스트:

# feature.yaml
feature: notice_board
project_name: cocode
targets: [application]
network: serverpod
frontend:
  usecases: [get_notices, create_notice]
  blocs:
    - { name: notice_list, usecases: [get_notices] }
  widgets: [notice_list_tile]
models:
  entities:
    - { name: notice, fields: { title: String, content: String } }
backend:
  cache: false
  console_endpoint: false

실행:

# 프로젝트 루트에서 (feature.yaml이 프로젝트 루트에 있을 때)
cob compose

# 매니페스트·프로젝트 경로 지정
cob compose -m feature.yaml -d template/cocode

# 작업트리가 더럽지만 강행 (one-shot 가드는 여전히 적용)
cob compose -m feature.yaml --force