refactor: Clean Architecture 적용 및 코드베이스 전면 리팩토링
Some checks failed
Flutter Test & Quality Check / Test on macos-latest (push) Has been cancelled
Flutter Test & Quality Check / Test on ubuntu-latest (push) Has been cancelled
Flutter Test & Quality Check / Build APK (push) Has been cancelled

## 주요 변경사항

### 아키텍처 개선
- Clean Architecture 패턴 적용 (Domain, Data, Presentation 레이어 분리)
- Use Case 패턴 도입으로 비즈니스 로직 캡슐화
- Repository 패턴으로 데이터 접근 추상화
- 의존성 주입 구조 개선

### 상태 관리 최적화
- 모든 Controller에서 불필요한 상태 관리 로직 제거
- 페이지네이션 로직 통일 및 간소화
- 에러 처리 로직 개선 (에러 메시지 한글화)
- 로딩 상태 관리 최적화

### Mock 서비스 제거
- MockDataService 완전 제거
- 모든 화면을 실제 API 전용으로 전환
- 불필요한 Mock 관련 코드 정리

### UI/UX 개선
- Overview 화면 대시보드 기능 강화
- 라이선스 만료 알림 위젯 추가
- 사이드바 네비게이션 개선
- 일관된 UI 컴포넌트 사용

### 코드 품질
- 중복 코드 제거 및 함수 추출
- 파일별 책임 분리 명확화
- 테스트 코드 업데이트

## 영향 범위
- 모든 화면의 Controller 리팩토링
- API 통신 레이어 구조 개선
- 에러 처리 및 로깅 시스템 개선

## 향후 계획
- 단위 테스트 커버리지 확대
- 통합 테스트 시나리오 추가
- 성능 모니터링 도구 통합
This commit is contained in:
JiWoong Sul
2025-08-11 00:04:28 +09:00
parent 6b5d126990
commit 162fe08618
113 changed files with 11072 additions and 3319 deletions

View File

@@ -0,0 +1,167 @@
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import '../../services/auth_service.dart';
import '../../data/models/auth/auth_user.dart';
/// 역할 기반 접근 제어를 위한 AuthGuard 위젯
///
/// 사용자의 역할을 확인하여 허용된 역할만 child 위젯에 접근할 수 있도록 제어합니다.
/// 권한이 없는 경우 UnauthorizedScreen을 표시합니다.
class AuthGuard extends StatelessWidget {
/// 접근을 허용할 역할 목록
final List<String> allowedRoles;
/// 권한이 있을 때 표시할 위젯
final Widget child;
/// 권한이 없을 때 표시할 커스텀 위젯 (선택사항)
final Widget? unauthorizedWidget;
/// 권한 체크를 건너뛸지 여부 (개발 모드용)
final bool skipCheck;
const AuthGuard({
super.key,
required this.allowedRoles,
required this.child,
this.unauthorizedWidget,
this.skipCheck = false,
});
@override
Widget build(BuildContext context) {
// 개발 모드에서 권한 체크 건너뛰기
if (skipCheck) {
return child;
}
// AuthService에서 현재 사용자 정보 가져오기
final authService = context.read<AuthService>();
return FutureBuilder<AuthUser?>(
future: authService.getCurrentUser(),
builder: (context, snapshot) {
// 로딩 중
if (snapshot.connectionState == ConnectionState.waiting) {
return const Center(child: CircularProgressIndicator());
}
// 현재 사용자 정보
final currentUser = snapshot.data;
// 로그인하지 않은 경우
if (currentUser == null) {
return unauthorizedWidget ?? const UnauthorizedScreen(
message: '로그인이 필요합니다.',
);
}
// 역할 확인 - 대소문자 구분 없이 비교
final userRole = currentUser.role.toLowerCase();
final hasPermission = allowedRoles.any(
(role) => role.toLowerCase() == userRole,
);
// 권한이 있는 경우
if (hasPermission) {
return child;
}
// 권한이 없는 경우
return unauthorizedWidget ?? UnauthorizedScreen(
message: '이 페이지에 접근할 권한이 없습니다.',
userRole: currentUser.role,
requiredRoles: allowedRoles,
);
},
);
}
}
/// 권한이 없을 때 표시되는 기본 화면
class UnauthorizedScreen extends StatelessWidget {
final String message;
final String? userRole;
final List<String>? requiredRoles;
const UnauthorizedScreen({
super.key,
required this.message,
this.userRole,
this.requiredRoles,
});
@override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: Container(
padding: const EdgeInsets.all(32),
constraints: const BoxConstraints(maxWidth: 400),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
const Icon(
Icons.lock_outline,
size: 80,
color: Colors.grey,
),
const SizedBox(height: 24),
Text(
'접근 권한 없음',
style: Theme.of(context).textTheme.headlineSmall,
),
const SizedBox(height: 16),
Text(
message,
textAlign: TextAlign.center,
style: Theme.of(context).textTheme.bodyLarge,
),
if (userRole != null) ...[
const SizedBox(height: 16),
Text(
'현재 권한: $userRole',
style: Theme.of(context).textTheme.bodyMedium?.copyWith(
color: Colors.grey,
),
),
],
if (requiredRoles != null && requiredRoles!.isNotEmpty) ...[
const SizedBox(height: 8),
Text(
'필요한 권한: ${requiredRoles!.join(", ")}',
style: Theme.of(context).textTheme.bodyMedium?.copyWith(
color: Colors.grey,
),
),
],
const SizedBox(height: 32),
ElevatedButton(
onPressed: () {
Navigator.of(context).pushReplacementNamed('/dashboard');
},
child: const Text('대시보드로 이동'),
),
],
),
),
),
);
}
}
/// 역할 상수 정의
class UserRole {
static const String admin = 'Admin';
static const String manager = 'Manager';
static const String member = 'Member';
/// 관리자 및 매니저 권한
static const List<String> adminAndManager = [admin, manager];
/// 모든 권한
static const List<String> all = [admin, manager, member];
/// 관리자 전용
static const List<String> adminOnly = [admin];
}