/cc-flutter:feature:presentation — 화면(UI) 만들어주는 일꾼#
| 항목 | 내용 |
|---|---|
| 실행 명령 | /cc-flutter:feature:presentation |
| 별칭 | /presentation:create, /layer:presentation |
| 모델 | sonnet |
| 사용 도구 | Read, Edit, Write, Glob, Grep |
| 연계 스킬 | feature |
한마디로#
앱에서 사용자 눈에 보이는 화면 부분을 정해진 규칙대로 자동으로 만들어주는 전담 일꾼입니다. 집으로 치면 골조·배관(데이터)이 아니라 눈에 보이는 인테리어와 스위치(화면·버튼) 를 표준 자재로 똑같이 시공해 주는 역할이에요.
누가·언제 쓰나요#
- 새 기능의 화면(페이지)과 버튼·카드 같은 위젯을 만들어야 할 때
- 화면 상태 관리(BLoC)와 화면 전환(Route)을 일관된 방식으로 깔고 싶을 때
- 전체 기능 자동 생성(
/cc-flutter:feature:create)의 마지막 5단계로 자동 호출될 때
무엇을 해주나요#
기능 이름 하나만 주면, 화면 관련 파일 묶음을 한 번에 만들어 줍니다. 실제로 생기는 것:
-
상태 관리 코드(BLoC) —
{feature}_list_bloc.dart,..._event.dart,..._state.dart - 화면(Page) —
{feature}_page.dart - 위젯(부품) —
{entity}_card.dart - 화면 전환(Route) —
{feature}_route.dart - 테스트 + Widgetbook — 화면이 제대로 동작하는지 확인하는 자동 검사 파일
이 모든 것이 회사 표준 패턴(BLoC, Clean Architecture 규칙)에 맞춰 일관되게 생성됩니다.
어떻게 쓰나요#
# 기본: 기능 이름과 엔티티 이름을 넘깁니다
/cc-flutter:feature:presentation
# 별칭으로도 호출 가능
/presentation:create
/layer:presentation
넘길 수 있는 값:
feature_name(필수) — 기능 모듈 이름 (snake_case 예:user_list)entity_name(필수) — 엔티티 이름 (PascalCase 예:User)-
location(선택) —application(기본) /common/console중 어디에 만들지 pages(선택) — 만들 페이지 목록
안에서 무슨 일이 벌어지나요#
-
상태 관리(BLoC) 생성 — 화면이 가질 수 있는 사건(Event)과 상태(State)를 표준 형태로 만들고, 비동기 처리 후 안전 점검(
isClosed)을 넣습니다. - 화면·위젯 생성 — 페이지를 BlocSignalProvider로 감싸고, 카드 같은 부품 위젯을 정해진 규칙으로 만듭니다.
- 화면 전환(Route) 생성 —
@TypedGoRoute방식으로 화면 이동 경로를 정의합니다. -
금지 패턴 차단 — 옛날 방식(
Key? key)이나 상대경로 import, BLoC이 데이터 저장소에 직접 손대는 잘못된 구조를 막습니다. - 자체 점검(체크리스트) + 테스트 — 만든 결과가 규칙에 맞는지 항목별로 확인하고 테스트 코드까지 작성합니다.
⚙️ 상세 옵션·실행 명세 (개발자 / AI 에이전트용)
Role#
Consistently generates BLoC (Event sealed class, State), Page/Widget, Route, tests, and Widgetbook.
Activation Conditions#
/cc-flutter:feature:presentationActivated when command is invoked- Invoked from
/cc-flutter:feature:createorchestration Step 5
Parameters#
| Parameter | Required | Description |
|---|---|---|
feature_name | ✅ | Feature module name (snake_case) |
entity_name | ✅ | Entity name (PascalCase) |
location | ❌ | application, common, console (default: application) |
pages | ❌ | To generate Page List |
Generated Files#
feature/{location}/{feature_name}/lib/src/presentation/
├── bloc/
│ └── {feature}_list/
│ ├── {feature}_list_bloc.dart
│ ├── {feature}_list_event.dart
│ └── {feature}_list_state.dart
├── page/
│ └── {feature}_page.dart
├── widget/
│ └── {entity}_card.dart
└── route/
└── {feature}_route.dart
# Tests 파일
feature/{location}/{feature_name}/test/presentation/
├── bloc/{feature}_list_bloc_test.dart
└── page/{feature}_page_test.dartCore Patterns Summary#
BLoC Event/State#
📚 Details: BLoC Pattern
- Event:
sealed class+ factory constructor + private Implementation - State:
sealed classor Freezed union type isClosedCheck: Required after await, before emit!
UseCase Integration Pattern#
📚 Details: UseCase Pattern
| Pattern | Description |
|---|---|
| Optional Constructor Injection | ✅ Standard pattern - Direct creation/Test without getIt |
Widget Generation Rules#
const MyWidget({
required this.data,
this.onTap,
super.key, // ✅ Always last
});
Route Pattern#
@TypedGoRoute<FeatureRoute>(path: '/feature')
class FeatureRoute extends GoRouteData { ... }
Prohibited Patterns#
// ❌ Key? key 패턴 금지 (super.key 사용)
const MyWidget({Key? key, required this.data}) : super(key: key);
// ❌ Relative path import prohibited
import '../domain/entity/user.dart';
// ❌ BLoC에서 Repository 직접 접근 금지 (UseCase 통해서만)
class MyBloc {
final IRepository _repository; // UseCase 우회!
}
Checklist#
Common (Required)#
- Event: sealed class + factory + private implementation pattern
- State: sealed class or Freezed union
- BLoC:
if (isClosed) return;Check (await After Required) - Page: BlocSignalProvider wrapping
- Widget:
super.keylast position - Route: @TypedGoRoute annotation
- package import only (relative path prohibited)
Tests#
- BLoC Test Writing
- Widget Test Writing
- Widgetbook UseCase Writing
UseCase Patterns#
- Optional Constructor Injection:
Bloc(useCase: mockUseCase)Direct injection Test