# Superport 코드 패턴 가이드

## 1. 파일 구조 및 네이밍 규칙

### 1.1 디렉토리 구조
```
lib/
├── models/          # 데이터 모델 (접미사: _model.dart)
├── screens/         # 화면 구성
│   ├── common/      # 공통 컴포넌트 및 레이아웃
│   └── [feature]/   # 기능별 디렉토리
├── services/        # 비즈니스 로직 및 데이터 서비스
└── utils/           # 유틸리티 함수 및 상수
```

### 1.2 파일 네이밍 규칙
- **모델**: `entity_name_model.dart` (예: `user_model.dart`)
- **화면**: `feature_screen.dart` (예: `login_screen.dart`)
- **리스트**: `entity_list.dart` (예: `user_list.dart`)
- **폼**: `entity_form_screen.dart` (예: `user_form_screen.dart`)
- **컨트롤러**: `feature_controller.dart` (예: `login_controller.dart`)
- **위젯**: `widget_name.dart` (예: `custom_button.dart`)

## 2. 코드 패턴

### 2.1 모델 클래스 패턴
```dart
class EntityModel {
  final String id;
  final String name;
  final DateTime? createdAt;
  
  EntityModel({
    required this.id,
    required this.name,
    this.createdAt,
  });
  
  // copyWith 메서드 필수
  EntityModel copyWith({
    String? id,
    String? name,
    DateTime? createdAt,
  }) {
    return EntityModel(
      id: id ?? this.id,
      name: name ?? this.name,
      createdAt: createdAt ?? this.createdAt,
    );
  }
  
  // JSON 직렬화 (선택적)
  Map<String, dynamic> toJson() => {
    'id': id,
    'name': name,
    'createdAt': createdAt?.toIso8601String(),
  };
}
```

### 2.2 화면(Screen) 패턴
```dart
class FeatureScreen extends StatefulWidget {
  final String? id; // 선택적 파라미터
  
  const FeatureScreen({Key? key, this.id}) : super(key: key);
  
  @override
  State<FeatureScreen> createState() => _FeatureScreenState();
}

class _FeatureScreenState extends State<FeatureScreen> {
  late final FeatureController _controller;
  
  @override
  void initState() {
    super.initState();
    _controller = FeatureController();
    _controller.initialize(widget.id);
  }
  
  @override
  void dispose() {
    _controller.dispose();
    super.dispose();
  }
  
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: _buildBody(),
    );
  }
}
```

### 2.3 컨트롤러 패턴
```dart
class FeatureController extends ChangeNotifier {
  final MockDataService _dataService = MockDataService();
  
  bool _isLoading = false;
  String? _error;
  List<Model> _items = [];
  
  // Getters
  bool get isLoading => _isLoading;
  String? get error => _error;
  List<Model> get items => _items;
  
  // 초기화
  Future<void> initialize() async {
    _setLoading(true);
    try {
      _items = await _dataService.getItems();
    } catch (e) {
      _error = e.toString();
    } finally {
      _setLoading(false);
    }
  }
  
  // 상태 업데이트
  void _setLoading(bool value) {
    _isLoading = value;
    notifyListeners();
  }
  
  @override
  void dispose() {
    // 정리 작업
    super.dispose();
  }
}
```

### 2.4 리스트 화면 패턴 (리디자인 버전)
```dart
class EntityListRedesign extends StatefulWidget {
  const EntityListRedesign({Key? key}) : super(key: key);
  
  @override
  State<EntityListRedesign> createState() => _EntityListRedesignState();
}

class _EntityListRedesignState extends State<EntityListRedesign> {
  final EntityListController _controller = EntityListController();
  
  @override
  Widget build(BuildContext context) {
    return AppLayoutRedesign(
      currentRoute: Routes.entity,
      child: Column(
        crossAxisAlignment: CrossAxisAlignment.start,
        children: [
          // 헤더
          _buildHeader(),
          SizedBox(height: ShadcnTheme.spacing.lg),
          // 컨텐츠
          Expanded(
            child: ShadcnCard(
              padding: EdgeInsets.zero,
              child: _buildContent(),
            ),
          ),
        ],
      ),
    );
  }
  
  Widget _buildHeader() {
    return Row(
      mainAxisAlignment: MainAxisAlignment.spaceBetween,
      children: [
        Text(
          '총 ${_controller.items.length}개',
          style: ShadcnTheme.typography.bodyMuted,
        ),
        ShadcnButton(
          onPressed: () => _navigateToForm(),
          icon: Icons.add,
          label: '추가',
        ),
      ],
    );
  }
}
```

### 2.5 폼 화면 패턴
```dart
class EntityFormScreen extends StatefulWidget {
  final String? id;
  
  const EntityFormScreen({Key? key, this.id}) : super(key: key);
  
  @override
  State<EntityFormScreen> createState() => _EntityFormScreenState();
}

class _EntityFormScreenState extends State<EntityFormScreen> {
  final _formKey = GlobalKey<FormState>();
  late final EntityFormController _controller;
  
  @override
  void initState() {
    super.initState();
    _controller = EntityFormController(id: widget.id);
    _controller.loadData();
  }
  
  Future<void> _handleSave() async {
    if (!_formKey.currentState!.validate()) return;
    
    _formKey.currentState!.save();
    
    try {
      await _controller.save();
      if (mounted) {
        Navigator.pop(context, true);
      }
    } catch (e) {
      // 에러 처리
    }
  }
  
  @override
  Widget build(BuildContext context) {
    return MainLayout(
      title: widget.id == null ? '새 항목 추가' : '항목 수정',
      showBackButton: true,
      child: Form(
        key: _formKey,
        child: Column(
          children: [
            // 폼 필드들
          ],
        ),
      ),
    );
  }
}
```

## 3. 위젯 사용 패턴

### 3.1 shadcn 컴포넌트 사용 (리디자인)
```dart
// 카드
ShadcnCard(
  child: Column(
    children: [...],
  ),
);

// 버튼
ShadcnButton(
  onPressed: () {},
  label: '저장',
  variant: ShadcnButtonVariant.primary,
);

// 입력 필드
ShadcnInput(
  value: _controller.name,
  onChanged: (value) => _controller.name = value,
  placeholder: '이름을 입력하세요',
);

// 배지
ShadcnBadge(
  label: '활성',
  variant: ShadcnBadgeVariant.success,
);
```

### 3.2 테이블/리스트 패턴
```dart
// DataTable 사용
DataTable(
  columns: [
    DataColumn(label: Text('이름')),
    DataColumn(label: Text('상태')),
    DataColumn(label: Text('작업')),
  ],
  rows: _controller.items.map((item) => DataRow(
    cells: [
      DataCell(Text(item.name)),
      DataCell(ShadcnBadge(label: item.status)),
      DataCell(Row(
        children: [
          IconButton(
            icon: Icon(Icons.edit),
            onPressed: () => _handleEdit(item),
          ),
          IconButton(
            icon: Icon(Icons.delete),
            onPressed: () => _handleDelete(item),
          ),
        ],
      )),
    ],
  )).toList(),
);
```

## 4. 서비스 레이어 패턴

### 4.1 Mock 데이터 서비스
```dart
class MockDataService {
  static final MockDataService _instance = MockDataService._internal();
  factory MockDataService() => _instance;
  MockDataService._internal();
  
  // 데이터 저장소
  final List<Model> _items = [];
  
  // CRUD 메서드
  Future<List<Model>> getItems() async {
    await Future.delayed(Duration(milliseconds: 300)); // 네트워크 지연 시뮬레이션
    return List.from(_items);
  }
  
  Future<Model> addItem(Model item) async {
    await Future.delayed(Duration(milliseconds: 300));
    _items.add(item);
    return item;
  }
  
  Future<void> updateItem(String id, Model item) async {
    await Future.delayed(Duration(milliseconds: 300));
    final index = _items.indexWhere((i) => i.id == id);
    if (index != -1) {
      _items[index] = item;
    }
  }
  
  Future<void> deleteItem(String id) async {
    await Future.delayed(Duration(milliseconds: 300));
    _items.removeWhere((i) => i.id == id);
  }
}
```

## 5. 유틸리티 패턴

### 5.1 Validator
```dart
class Validators {
  static String? required(String? value, {String? fieldName}) {
    if (value == null || value.trim().isEmpty) {
      return '${fieldName ?? '이 필드'}는 필수입니다.';
    }
    return null;
  }
  
  static String? email(String? value) {
    if (value == null || value.isEmpty) return null;
    final emailRegex = RegExp(r'^[\w-\.]+@([\w-]+\.)+[\w-]{2,4}$');
    if (!emailRegex.hasMatch(value)) {
      return '올바른 이메일 형식이 아닙니다.';
    }
    return null;
  }
}
```

### 5.2 상수 정의
```dart
class Routes {
  static const String home = '/';
  static const String login = '/login';
  static const String equipment = '/equipment';
  // ...
}

class AppColors {
  static const Color primary = Color(0xFF3B82F6);
  static const Color secondary = Color(0xFF64748B);
  // ...
}
```

## 6. 베스트 프랙티스

### 6.1 일반 규칙
1. **단일 책임 원칙**: 각 클래스/함수는 하나의 책임만 가져야 함
2. **DRY 원칙**: 코드 중복을 피하고 재사용 가능한 컴포넌트 작성
3. **명확한 네이밍**: 변수, 함수, 클래스명은 용도를 명확히 표현
4. **일관성**: 프로젝트 전체에서 동일한 패턴과 스타일 사용

### 6.2 Flutter 특화
1. **const 생성자 사용**: 가능한 모든 위젯에 const 사용
2. **Key 사용**: 리스트나 동적 위젯에는 적절한 Key 제공
3. **BuildContext 주의**: async 작업 후 context 사용 시 mounted 체크
4. **메모리 누수 방지**: Controller, Stream 등은 dispose에서 정리

### 6.3 리디자인 관련
1. **테마 시스템 사용**: 하드코딩된 스타일 대신 ShadcnTheme 사용
2. **컴포넌트 재사용**: shadcn_components의 표준 컴포넌트 활용
3. **일관된 레이아웃**: AppLayoutRedesign으로 모든 화면 감싸기
4. **반응형 디자인**: 다양한 화면 크기 고려

## 7. 코드 예제

### 7.1 완전한 리스트 화면 예제
```dart
// user_list_redesign.dart
class UserListRedesign extends StatefulWidget {
  const UserListRedesign({Key? key}) : super(key: key);
  
  @override
  State<UserListRedesign> createState() => _UserListRedesignState();
}

class _UserListRedesignState extends State<UserListRedesign> {
  final UserListController _controller = UserListController();
  
  @override
  void initState() {
    super.initState();
    _loadData();
  }
  
  Future<void> _loadData() async {
    await _controller.loadUsers();
    if (mounted) setState(() {});
  }
  
  @override
  Widget build(BuildContext context) {
    return AppLayoutRedesign(
      currentRoute: Routes.user,
      child: Column(
        crossAxisAlignment: CrossAxisAlignment.start,
        children: [
          // 헤더
          Row(
            mainAxisAlignment: MainAxisAlignment.spaceBetween,
            children: [
              Text(
                '총 ${_controller.users.length}명',
                style: ShadcnTheme.typography.bodyMuted,
              ),
              ShadcnButton(
                onPressed: _navigateToAdd,
                icon: Icons.add,
                label: '사용자 추가',
              ),
            ],
          ),
          SizedBox(height: ShadcnTheme.spacing.lg),
          // 테이블
          Expanded(
            child: ShadcnCard(
              padding: EdgeInsets.zero,
              child: _controller.users.isEmpty
                  ? _buildEmptyState()
                  : _buildDataTable(),
            ),
          ),
        ],
      ),
    );
  }
  
  Widget _buildEmptyState() {
    return Center(
      child: Column(
        mainAxisAlignment: MainAxisAlignment.center,
        children: [
          Icon(Icons.people_outline, size: 64, color: ShadcnTheme.muted),
          SizedBox(height: ShadcnTheme.spacing.md),
          Text('사용자가 없습니다', style: ShadcnTheme.typography.bodyMuted),
        ],
      ),
    );
  }
  
  Widget _buildDataTable() {
    return SingleChildScrollView(
      scrollDirection: Axis.horizontal,
      child: DataTable(
        // 테이블 구현
      ),
    );
  }
  
  Future<void> _navigateToAdd() async {
    final result = await Navigator.pushNamed(context, Routes.userAdd);
    if (result == true) {
      _loadData();
    }
  }
}
```

---

*이 가이드는 Superport 프로젝트의 코드 일관성을 위해 작성되었습니다.*
*마지막 업데이트: 2025-07-07*