초기 커밋
This commit is contained in:
114
lib/core/router/app_router.dart
Normal file
114
lib/core/router/app_router.dart
Normal file
@@ -0,0 +1,114 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:go_router/go_router.dart';
|
||||
import 'package:riverpod_annotation/riverpod_annotation.dart';
|
||||
|
||||
import '../../features/admin/presentation/screens/admin_home_screen.dart';
|
||||
import '../../features/admin/presentation/screens/system_settings_screen.dart';
|
||||
import '../../features/admin/presentation/screens/user_management_screen.dart';
|
||||
import '../../features/auth/presentation/screens/login_screen.dart';
|
||||
import '../../features/auth/presentation/screens/register_screen.dart';
|
||||
import '../../features/dashboard/presentation/screens/dashboard_screen.dart';
|
||||
import '../../features/user/presentation/screens/user_home_screen.dart';
|
||||
import '../../features/user/presentation/screens/user_profile_screen.dart';
|
||||
import '../../shared/providers/auth_provider.dart';
|
||||
import '../../shared/widgets/app_scaffold.dart';
|
||||
import 'auth_guard.dart';
|
||||
import 'route_names.dart';
|
||||
|
||||
part 'app_router.g.dart';
|
||||
|
||||
@Riverpod(keepAlive: true)
|
||||
GoRouter appRouter(Ref ref) {
|
||||
final authGuard = AuthGuard(ref);
|
||||
final authState = ref.watch(authStateProvider);
|
||||
|
||||
return GoRouter(
|
||||
initialLocation: RoutePaths.login,
|
||||
debugLogDiagnostics: true,
|
||||
refreshListenable: _GoRouterRefreshStream(ref, authState),
|
||||
redirect: (context, state) {
|
||||
final location = state.uri.toString();
|
||||
return authGuard.redirect(context, location);
|
||||
},
|
||||
routes: [
|
||||
// Auth Routes (비인증)
|
||||
GoRoute(
|
||||
name: RouteNames.login,
|
||||
path: RoutePaths.login,
|
||||
builder: (context, state) => const LoginScreen(),
|
||||
),
|
||||
GoRoute(
|
||||
name: RouteNames.register,
|
||||
path: RoutePaths.register,
|
||||
builder: (context, state) => const RegisterScreen(),
|
||||
),
|
||||
|
||||
// User Shell Route
|
||||
ShellRoute(
|
||||
builder: (context, state, child) => AppScaffold(
|
||||
currentPath: state.uri.toString(),
|
||||
child: child,
|
||||
),
|
||||
routes: [
|
||||
GoRoute(
|
||||
name: RouteNames.userHome,
|
||||
path: RoutePaths.userHome,
|
||||
builder: (context, state) => const UserHomeScreen(),
|
||||
),
|
||||
GoRoute(
|
||||
name: RouteNames.userProfile,
|
||||
path: RoutePaths.userProfile,
|
||||
builder: (context, state) => const UserProfileScreen(),
|
||||
),
|
||||
GoRoute(
|
||||
name: RouteNames.userDashboard,
|
||||
path: RoutePaths.userDashboard,
|
||||
builder: (context, state) => const DashboardScreen(),
|
||||
),
|
||||
],
|
||||
),
|
||||
|
||||
// Admin Shell Route
|
||||
ShellRoute(
|
||||
builder: (context, state, child) => AppScaffold(
|
||||
currentPath: state.uri.toString(),
|
||||
isAdmin: true,
|
||||
child: child,
|
||||
),
|
||||
routes: [
|
||||
GoRoute(
|
||||
name: RouteNames.adminHome,
|
||||
path: RoutePaths.adminHome,
|
||||
builder: (context, state) => const AdminHomeScreen(),
|
||||
),
|
||||
GoRoute(
|
||||
name: RouteNames.adminUsers,
|
||||
path: RoutePaths.adminUsers,
|
||||
builder: (context, state) => const UserManagementScreen(),
|
||||
),
|
||||
GoRoute(
|
||||
name: RouteNames.adminDashboard,
|
||||
path: RoutePaths.adminDashboard,
|
||||
builder: (context, state) => const DashboardScreen(),
|
||||
),
|
||||
GoRoute(
|
||||
name: RouteNames.adminSettings,
|
||||
path: RoutePaths.adminSettings,
|
||||
builder: (context, state) => const SystemSettingsScreen(),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
/// GoRouter에서 Riverpod 상태 변경 시 리프레시하기 위한 Listenable 래퍼
|
||||
class _GoRouterRefreshStream extends ChangeNotifier {
|
||||
_GoRouterRefreshStream(this.ref, dynamic _) {
|
||||
// authState 변경 시 GoRouter refresh 트리거
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
final Ref ref;
|
||||
}
|
||||
64
lib/core/router/auth_guard.dart
Normal file
64
lib/core/router/auth_guard.dart
Normal file
@@ -0,0 +1,64 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
|
||||
import '../../shared/models/user_role.dart';
|
||||
import '../../shared/providers/auth_provider.dart';
|
||||
import 'route_names.dart';
|
||||
|
||||
/// 인증/역할 기반 라우트 가드
|
||||
class AuthGuard {
|
||||
const AuthGuard(this.ref);
|
||||
|
||||
final Ref ref;
|
||||
|
||||
/// GoRouter redirect 콜백
|
||||
String? redirect(BuildContext context, String location) {
|
||||
final authState = ref.read(authStateProvider);
|
||||
|
||||
return authState.when(
|
||||
data: (user) {
|
||||
final isLoggedIn = user != null;
|
||||
final isAuthRoute =
|
||||
location.startsWith(RoutePaths.login) ||
|
||||
location.startsWith(RoutePaths.register);
|
||||
|
||||
// 비로그인 사용자가 인증 페이지 외 접근 시 → 로그인으로
|
||||
if (!isLoggedIn && !isAuthRoute) {
|
||||
return RoutePaths.login;
|
||||
}
|
||||
|
||||
// 로그인 사용자가 인증 페이지 접근 시 → 역할에 따라 리다이렉트
|
||||
if (isLoggedIn && isAuthRoute) {
|
||||
return _redirectByRole(user!.role);
|
||||
}
|
||||
|
||||
// 역할 기반 접근 제어
|
||||
if (isLoggedIn) {
|
||||
return _checkRoleAccess(location, user!.role);
|
||||
}
|
||||
|
||||
return null;
|
||||
},
|
||||
loading: () => null,
|
||||
error: (_, __) => RoutePaths.login,
|
||||
);
|
||||
}
|
||||
|
||||
/// 역할에 따른 기본 페이지 리다이렉트
|
||||
String _redirectByRole(UserRole role) {
|
||||
return switch (role) {
|
||||
UserRole.admin => RoutePaths.adminHome,
|
||||
UserRole.user => RoutePaths.userHome,
|
||||
};
|
||||
}
|
||||
|
||||
/// 역할 기반 접근 제어
|
||||
String? _checkRoleAccess(String location, UserRole role) {
|
||||
// 일반 사용자가 관리자 페이지 접근 시도
|
||||
if (location.startsWith(RoutePaths.admin) && role != UserRole.admin) {
|
||||
return RoutePaths.userHome;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
39
lib/core/router/route_names.dart
Normal file
39
lib/core/router/route_names.dart
Normal file
@@ -0,0 +1,39 @@
|
||||
/// 라우트 이름 상수
|
||||
abstract final class RouteNames {
|
||||
// Auth
|
||||
static const String login = 'login';
|
||||
static const String register = 'register';
|
||||
|
||||
// User
|
||||
static const String userShell = 'user-shell';
|
||||
static const String userHome = 'user-home';
|
||||
static const String userProfile = 'user-profile';
|
||||
static const String userDashboard = 'user-dashboard';
|
||||
|
||||
// Admin
|
||||
static const String adminShell = 'admin-shell';
|
||||
static const String adminHome = 'admin-home';
|
||||
static const String adminUsers = 'admin-users';
|
||||
static const String adminDashboard = 'admin-dashboard';
|
||||
static const String adminSettings = 'admin-settings';
|
||||
}
|
||||
|
||||
/// 라우트 경로 상수
|
||||
abstract final class RoutePaths {
|
||||
// Auth
|
||||
static const String login = '/login';
|
||||
static const String register = '/register';
|
||||
|
||||
// User
|
||||
static const String user = '/user';
|
||||
static const String userHome = '/user/home';
|
||||
static const String userProfile = '/user/profile';
|
||||
static const String userDashboard = '/user/dashboard';
|
||||
|
||||
// Admin
|
||||
static const String admin = '/admin';
|
||||
static const String adminHome = '/admin/home';
|
||||
static const String adminUsers = '/admin/users';
|
||||
static const String adminDashboard = '/admin/dashboard';
|
||||
static const String adminSettings = '/admin/settings';
|
||||
}
|
||||
Reference in New Issue
Block a user