Core 네트워크/권한 주석 보강
This commit is contained in:
@@ -1,6 +1,7 @@
|
|||||||
import 'package:flutter/widgets.dart';
|
import 'package:flutter/widgets.dart';
|
||||||
import 'package:lucide_icons_flutter/lucide_icons.dart' as lucide;
|
import 'package:lucide_icons_flutter/lucide_icons.dart' as lucide;
|
||||||
|
|
||||||
|
/// 사이드바/내비게이션용 페이지 정보.
|
||||||
class AppPageDescriptor {
|
class AppPageDescriptor {
|
||||||
const AppPageDescriptor({
|
const AppPageDescriptor({
|
||||||
required this.path,
|
required this.path,
|
||||||
@@ -15,6 +16,7 @@ class AppPageDescriptor {
|
|||||||
final String summary;
|
final String summary;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// 메뉴 섹션을 나타내는 데이터 클래스.
|
||||||
class AppSectionDescriptor {
|
class AppSectionDescriptor {
|
||||||
const AppSectionDescriptor({required this.label, required this.pages});
|
const AppSectionDescriptor({required this.label, required this.pages});
|
||||||
|
|
||||||
@@ -22,9 +24,13 @@ class AppSectionDescriptor {
|
|||||||
final List<AppPageDescriptor> pages;
|
final List<AppPageDescriptor> pages;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// 로그인 라우트 경로.
|
||||||
const loginRoutePath = '/login';
|
const loginRoutePath = '/login';
|
||||||
|
|
||||||
|
/// 대시보드 라우트 경로.
|
||||||
const dashboardRoutePath = '/dashboard';
|
const dashboardRoutePath = '/dashboard';
|
||||||
|
|
||||||
|
/// 네비게이션 구성을 정의한 섹션 목록.
|
||||||
const appSections = <AppSectionDescriptor>[
|
const appSections = <AppSectionDescriptor>[
|
||||||
AppSectionDescriptor(
|
AppSectionDescriptor(
|
||||||
label: '대시보드',
|
label: '대시보드',
|
||||||
|
|||||||
@@ -1,5 +1,3 @@
|
|||||||
// ignore_for_file: public_member_api_docs
|
|
||||||
|
|
||||||
import 'package:dio/dio.dart';
|
import 'package:dio/dio.dart';
|
||||||
|
|
||||||
import 'api_error.dart';
|
import 'api_error.dart';
|
||||||
@@ -93,6 +91,7 @@ class ApiClient {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Dio 요청을 실행하고 `DioException`을 공통 예외 유형으로 변환한다.
|
||||||
Future<Response<T>> _wrap<T>(Future<Response<T>> Function() request) async {
|
Future<Response<T>> _wrap<T>(Future<Response<T>> Function() request) async {
|
||||||
try {
|
try {
|
||||||
return await request();
|
return await request();
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import 'package:dio/dio.dart';
|
import 'package:dio/dio.dart';
|
||||||
|
|
||||||
|
/// API 호출 실패 유형.
|
||||||
enum ApiErrorCode {
|
enum ApiErrorCode {
|
||||||
badRequest,
|
badRequest,
|
||||||
unauthorized,
|
unauthorized,
|
||||||
@@ -12,6 +13,7 @@ enum ApiErrorCode {
|
|||||||
unknown,
|
unknown,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// API 호출 시 공통으로 던지는 예외 모델.
|
||||||
class ApiException implements Exception {
|
class ApiException implements Exception {
|
||||||
const ApiException({
|
const ApiException({
|
||||||
required this.code,
|
required this.code,
|
||||||
@@ -32,9 +34,11 @@ class ApiException implements Exception {
|
|||||||
'ApiException(code: $code, statusCode: $statusCode, message: $message)';
|
'ApiException(code: $code, statusCode: $statusCode, message: $message)';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Dio 예외를 [ApiException]으로 변환하는 매퍼.
|
||||||
class ApiErrorMapper {
|
class ApiErrorMapper {
|
||||||
const ApiErrorMapper();
|
const ApiErrorMapper();
|
||||||
|
|
||||||
|
/// Dio 예외 세부 정보를 분석해 적절한 [ApiException]을 생성한다.
|
||||||
ApiException map(DioException error) {
|
ApiException map(DioException error) {
|
||||||
final status = error.response?.statusCode;
|
final status = error.response?.statusCode;
|
||||||
final data = error.response?.data;
|
final data = error.response?.data;
|
||||||
@@ -124,6 +128,7 @@ class ApiErrorMapper {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// 응답 바디 혹은 Dio 예외 객체에서 사용자용 메시지를 추출한다.
|
||||||
String _resolveMessage(DioException error, dynamic data) {
|
String _resolveMessage(DioException error, dynamic data) {
|
||||||
if (data is Map<String, dynamic>) {
|
if (data is Map<String, dynamic>) {
|
||||||
final message = data['message'] ?? data['error'];
|
final message = data['message'] ?? data['error'];
|
||||||
@@ -136,6 +141,7 @@ class ApiErrorMapper {
|
|||||||
return error.message ?? '요청 처리 중 알 수 없는 오류가 발생했습니다.';
|
return error.message ?? '요청 처리 중 알 수 없는 오류가 발생했습니다.';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// 422/409 등에서 제공되는 필드별 오류 정보를 추출한다.
|
||||||
Map<String, dynamic>? _extractDetails(dynamic data) {
|
Map<String, dynamic>? _extractDetails(dynamic data) {
|
||||||
if (data is Map<String, dynamic>) {
|
if (data is Map<String, dynamic>) {
|
||||||
final errors = data['errors'];
|
final errors = data['errors'];
|
||||||
|
|||||||
@@ -2,8 +2,10 @@ import 'package:flutter/widgets.dart';
|
|||||||
|
|
||||||
import '../config/environment.dart';
|
import '../config/environment.dart';
|
||||||
|
|
||||||
|
/// 권한 체크를 위한 액션 종류.
|
||||||
enum PermissionAction { view, create, edit, delete, restore, approve }
|
enum PermissionAction { view, create, edit, delete, restore, approve }
|
||||||
|
|
||||||
|
/// 기능별 권한을 확인하고 오버라이드를 지원하는 매니저.
|
||||||
class PermissionManager extends ChangeNotifier {
|
class PermissionManager extends ChangeNotifier {
|
||||||
PermissionManager({Map<String, Set<PermissionAction>>? overrides}) {
|
PermissionManager({Map<String, Set<PermissionAction>>? overrides}) {
|
||||||
if (overrides != null) {
|
if (overrides != null) {
|
||||||
@@ -13,6 +15,7 @@ class PermissionManager extends ChangeNotifier {
|
|||||||
|
|
||||||
final Map<String, Set<PermissionAction>> _overrides = {};
|
final Map<String, Set<PermissionAction>> _overrides = {};
|
||||||
|
|
||||||
|
/// 지정한 리소스/행동이 허용되는지 여부를 반환한다.
|
||||||
bool can(String resource, PermissionAction action) {
|
bool can(String resource, PermissionAction action) {
|
||||||
final override = _overrides[resource];
|
final override = _overrides[resource];
|
||||||
if (override != null) {
|
if (override != null) {
|
||||||
@@ -25,6 +28,7 @@ class PermissionManager extends ChangeNotifier {
|
|||||||
return Environment.hasPermission(resource, action.name);
|
return Environment.hasPermission(resource, action.name);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// 개발/테스트 환경에서 사용할 임시 오버라이드 값을 설정한다.
|
||||||
void updateOverrides(Map<String, Set<PermissionAction>> overrides) {
|
void updateOverrides(Map<String, Set<PermissionAction>> overrides) {
|
||||||
_overrides
|
_overrides
|
||||||
..clear()
|
..clear()
|
||||||
@@ -33,6 +37,7 @@ class PermissionManager extends ChangeNotifier {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// 위젯 트리에 [PermissionManager]를 전달하는 Inherited 위젯.
|
||||||
class PermissionScope extends InheritedNotifier<PermissionManager> {
|
class PermissionScope extends InheritedNotifier<PermissionManager> {
|
||||||
const PermissionScope({
|
const PermissionScope({
|
||||||
super.key,
|
super.key,
|
||||||
@@ -50,6 +55,7 @@ class PermissionScope extends InheritedNotifier<PermissionManager> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// 권한에 따라 child/fallback을 노출하거나 숨기는 헬퍼 위젯.
|
||||||
class PermissionGate extends StatelessWidget {
|
class PermissionGate extends StatelessWidget {
|
||||||
const PermissionGate({
|
const PermissionGate({
|
||||||
super.key,
|
super.key,
|
||||||
@@ -83,5 +89,6 @@ class PermissionGate extends StatelessWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
extension PermissionActionKey on PermissionAction {
|
extension PermissionActionKey on PermissionAction {
|
||||||
|
/// GoRouter 등에서 사용할 문자열 키.
|
||||||
String get key => name;
|
String get key => name;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,8 +23,10 @@ import '../../features/util/postal_search/presentation/pages/postal_search_page.
|
|||||||
import '../../widgets/app_shell.dart';
|
import '../../widgets/app_shell.dart';
|
||||||
import '../constants/app_sections.dart';
|
import '../constants/app_sections.dart';
|
||||||
|
|
||||||
|
/// 전역 네비게이터 키(로그인/셸 라우터 공용).
|
||||||
final _rootNavigatorKey = GlobalKey<NavigatorState>(debugLabel: 'root');
|
final _rootNavigatorKey = GlobalKey<NavigatorState>(debugLabel: 'root');
|
||||||
|
|
||||||
|
/// 애플리케이션 전체 라우팅 구성을 담당하는 GoRouter 인스턴스.
|
||||||
final appRouter = GoRouter(
|
final appRouter = GoRouter(
|
||||||
navigatorKey: _rootNavigatorKey,
|
navigatorKey: _rootNavigatorKey,
|
||||||
initialLocation: loginRoutePath,
|
initialLocation: loginRoutePath,
|
||||||
|
|||||||
Reference in New Issue
Block a user