LogoSkills

presentation-layer-agent

Clean Architecture Presentation Layer specialist. Used for BLoC, Page, Widget, and Route work

ํ•ญ๋ชฉ๋‚ด์šฉ
Invoke/feature:presentation
Aliases/presentation:create, /layer:presentation
ToolsRead, Edit, Write, Glob, Grep
Modelsonnet
Skillsfeature

/feature:presentation โ€” ํ™”๋ฉด(UI) ๋งŒ๋“ค์–ด์ฃผ๋Š” ์ผ๊พผ#

ํ•œ๋งˆ๋””๋กœ#

์•ฑ์—์„œ ์‚ฌ์šฉ์ž ๋ˆˆ์— ๋ณด์ด๋Š” ํ™”๋ฉด ๋ถ€๋ถ„์„ ์ •ํ•ด์ง„ ๊ทœ์น™๋Œ€๋กœ ์ž๋™์œผ๋กœ ๋งŒ๋“ค์–ด์ฃผ๋Š” ์ „๋‹ด ์ผ๊พผ์ž…๋‹ˆ๋‹ค. ์ง‘์œผ๋กœ ์น˜๋ฉด ๊ณจ์กฐยท๋ฐฐ๊ด€(๋ฐ์ดํ„ฐ)์ด ์•„๋‹ˆ๋ผ ๋ˆˆ์— ๋ณด์ด๋Š” ์ธํ…Œ๋ฆฌ์–ด์™€ ์Šค์œ„์น˜(ํ™”๋ฉดยท๋ฒ„ํŠผ) ๋ฅผ ํ‘œ์ค€ ์ž์žฌ๋กœ ๋˜‘๊ฐ™์ด ์‹œ๊ณตํ•ด ์ฃผ๋Š” ์—ญํ• ์ด์—์š”.

๋ˆ„๊ฐ€ยท์–ธ์ œ ์“ฐ๋‚˜์š”#

  • ์ƒˆ ๊ธฐ๋Šฅ์˜ ํ™”๋ฉด(ํŽ˜์ด์ง€)๊ณผ ๋ฒ„ํŠผยท์นด๋“œ ๊ฐ™์€ ์œ„์ ฏ์„ ๋งŒ๋“ค์–ด์•ผ ํ•  ๋•Œ
  • ํ™”๋ฉด ์ƒํƒœ ๊ด€๋ฆฌ(BLoC)์™€ ํ™”๋ฉด ์ „ํ™˜(Route)์„ ์ผ๊ด€๋œ ๋ฐฉ์‹์œผ๋กœ ๊น”๊ณ  ์‹ถ์„ ๋•Œ
  • ์ „์ฒด ๊ธฐ๋Šฅ ์ž๋™ ์ƒ์„ฑ(/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 ๊ทœ์น™)์— ๋งž์ถฐ ์ผ๊ด€๋˜๊ฒŒ ์ƒ์„ฑ๋ฉ๋‹ˆ๋‹ค.

์–ด๋–ป๊ฒŒ ์“ฐ๋‚˜์š”#

# ๊ธฐ๋ณธ: ๊ธฐ๋Šฅ ์ด๋ฆ„๊ณผ ์—”ํ‹ฐํ‹ฐ ์ด๋ฆ„์„ ๋„˜๊น๋‹ˆ๋‹ค
/feature:presentation

# ๋ณ„์นญ์œผ๋กœ๋„ ํ˜ธ์ถœ ๊ฐ€๋Šฅ
/presentation:create
/layer:presentation

๋„˜๊ธธ ์ˆ˜ ์žˆ๋Š” ๊ฐ’:

  • feature_name (ํ•„์ˆ˜) โ€” ๊ธฐ๋Šฅ ๋ชจ๋“ˆ ์ด๋ฆ„ (snake_case ์˜ˆ: user_list)
  • entity_name (ํ•„์ˆ˜) โ€” ์—”ํ‹ฐํ‹ฐ ์ด๋ฆ„ (PascalCase ์˜ˆ: User)
  • location (์„ ํƒ) โ€” application(๊ธฐ๋ณธ) / common / console ์ค‘ ์–ด๋””์— ๋งŒ๋“ค์ง€
  • pages (์„ ํƒ) โ€” ๋งŒ๋“ค ํŽ˜์ด์ง€ ๋ชฉ๋ก

์•ˆ์—์„œ ๋ฌด์Šจ ์ผ์ด ๋ฒŒ์–ด์ง€๋‚˜์š”#

  1. ์ƒํƒœ ๊ด€๋ฆฌ(BLoC) ์ƒ์„ฑ โ€” ํ™”๋ฉด์ด ๊ฐ€์งˆ ์ˆ˜ ์žˆ๋Š” ์‚ฌ๊ฑด(Event)๊ณผ ์ƒํƒœ(State)๋ฅผ ํ‘œ์ค€ ํ˜•ํƒœ๋กœ ๋งŒ๋“ค๊ณ , ๋น„๋™๊ธฐ ์ฒ˜๋ฆฌ ํ›„ ์•ˆ์ „ ์ ๊ฒ€(isClosed)์„ ๋„ฃ์Šต๋‹ˆ๋‹ค.
  2. ํ™”๋ฉดยท์œ„์ ฏ ์ƒ์„ฑ โ€” ํŽ˜์ด์ง€๋ฅผ BlocProvider๋กœ ๊ฐ์‹ธ๊ณ , ์นด๋“œ ๊ฐ™์€ ๋ถ€ํ’ˆ ์œ„์ ฏ์„ ์ •ํ•ด์ง„ ๊ทœ์น™์œผ๋กœ ๋งŒ๋“ญ๋‹ˆ๋‹ค.
  3. ํ™”๋ฉด ์ „ํ™˜(Route) ์ƒ์„ฑ โ€” @TypedGoRoute ๋ฐฉ์‹์œผ๋กœ ํ™”๋ฉด ์ด๋™ ๊ฒฝ๋กœ๋ฅผ ์ •์˜ํ•ฉ๋‹ˆ๋‹ค.
  4. ๊ธˆ์ง€ ํŒจํ„ด ์ฐจ๋‹จ โ€” ์˜›๋‚  ๋ฐฉ์‹(Key? key)์ด๋‚˜ ์ƒ๋Œ€๊ฒฝ๋กœ import, BLoC์ด ๋ฐ์ดํ„ฐ ์ €์žฅ์†Œ์— ์ง์ ‘ ์†๋Œ€๋Š” ์ž˜๋ชป๋œ ๊ตฌ์กฐ๋ฅผ ๋ง‰์Šต๋‹ˆ๋‹ค.
  5. ์ž์ฒด ์ ๊ฒ€(์ฒดํฌ๋ฆฌ์ŠคํŠธ) + ํ…Œ์ŠคํŠธ โ€” ๋งŒ๋“  ๊ฒฐ๊ณผ๊ฐ€ ๊ทœ์น™์— ๋งž๋Š”์ง€ ํ•ญ๋ชฉ๋ณ„๋กœ ํ™•์ธํ•˜๊ณ  ํ…Œ์ŠคํŠธ ์ฝ”๋“œ๊นŒ์ง€ ์ž‘์„ฑํ•ฉ๋‹ˆ๋‹ค.

โš™๏ธ ์ƒ์„ธ ์˜ต์…˜ยท์‹คํ–‰ ๋ช…์„ธ (๊ฐœ๋ฐœ์ž / AI ์—์ด์ „ํŠธ์šฉ)

Role#

Consistently generates BLoC (Event sealed class, State), Page/Widget, Route, tests, and Widgetbook.


Activation Conditions#

  • /feature:presentation Activated when command is invoked
  • Invoked from /feature:create orchestration Step 5

Parameters#

ParameterRequiredDescription
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.dart

Core Patterns Summary#

BLoC Event/State#

๐Ÿ“š Details: BLoC Pattern

  • Event: sealed class + factory constructor + private Implementation
  • State: sealed class or Freezed union type
  • isClosed Check: Required after await, before emit!

UseCase Integration Pattern#

๐Ÿ“š Details: UseCase Pattern

PatternDescription
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: BlocProvider wrapping
  • Widget: super.key last 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