Flutter에서 명명된 라우트를 어떻게 만들고 사용하나요?
질문
Flutter에서 명명된 라우트를 만들 수 있나요? 가능하다면 어떻게 하나요?
답변
네, Flutter에서는 명명된 라우트(Named Routes)를 만들고 사용할 수 있습니다. 명명된 라우트는 경로 이름을 사용하여 앱 내에서 화면 간 이동을 정의하는 방법입니다. 이 접근 방식은 앱의 내비게이션 구조를 더 명확하게 만들고 유지보수성을 향상시킵니다.
명명된 라우트의 장점
- 집중화된 라우트 관리: 모든 라우트를 한 곳에서 정의하고 관리
- 더 깔끔한 네비게이션 코드: 경로 이름을 사용하여 화면 이동
- 딥 링크와의 통합: 웹 URL과 유사한 경로 구조로 딥 링크 지원 용이
- 라우트 매개변수 전달: 경로에 매개변수를 포함하여 데이터 전달 가능
기본 명명된 라우트 설정하기
명명된 라우트는 MaterialApp
또는 CupertinoApp
의 routes
매개변수를 사용하여 정의합니다.
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: '명명된 라우트 예제',
// 초기 경로 정의
initialRoute: '/',
// 라우트 맵 정의
routes: {
'/': (context) => HomeScreen(),
'/details': (context) => DetailsScreen(),
'/settings': (context) => SettingsScreen(),
'/profile': (context) => ProfileScreen(),
},
);
}
}
class HomeScreen extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('홈')),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
ElevatedButton(
onPressed: () {
// 명명된 라우트로 이동
Navigator.pushNamed(context, '/details');
},
child: Text('상세 화면으로 이동'),
),
ElevatedButton(
onPressed: () {
Navigator.pushNamed(context, '/settings');
},
child: Text('설정 화면으로 이동'),
),
],
),
),
);
}
}
// 다른 화면들...
class DetailsScreen extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('상세 정보')),
body: Center(
child: ElevatedButton(
onPressed: () {
// 이전 화면으로 돌아가기
Navigator.pop(context);
},
child: Text('뒤로 가기'),
),
),
);
}
}
// SettingsScreen과 ProfileScreen 구현...
라우트 간 데이터 전달하기
1. 매개변수를 사용하여 데이터 전달
// 데이터와 함께 라우트 이동
Navigator.pushNamed(
context,
'/details',
arguments: {'id': 123, 'title': '상품 상세 정보'},
);
// 데이터 받기
class DetailsScreen extends StatelessWidget {
@override
Widget build(BuildContext context) {
// 전달된 인수 받기
final args = ModalRoute.of(context)!.settings.arguments as Map<String, dynamic>;
return Scaffold(
appBar: AppBar(title: Text(args['title'])),
body: Center(
child: Text('ID: ${args['id']}'),
),
);
}
}
onGenerateRoute를 사용한 동적 라우팅
더 복잡한 라우팅 로직이 필요한 경우, onGenerateRoute
콜백을 사용하여 동적으로 라우트를 생성할 수 있습니다.
MaterialApp(
title: '동적 라우팅 예제',
initialRoute: '/',
// 기본 라우트
routes: {
'/': (context) => HomeScreen(),
'/settings': (context) => SettingsScreen(),
},
// 동적 라우트 생성
onGenerateRoute: (settings) {
// 경로와 인수 분석
if (settings.name!.startsWith('/details/')) {
// 예: /details/123
final id = settings.name!.split('/').last;
return MaterialPageRoute(
builder: (context) => DetailsScreen(id: int.parse(id)),
settings: settings,
);
}
// 프로필 경로
if (settings.name == '/profile') {
return MaterialPageRoute(
builder: (context) => ProfileScreen(),
settings: settings,
);
}
// 정의되지 않은 경로는 404 페이지로
return MaterialPageRoute(
builder: (context) => NotFoundScreen(),
settings: settings,
);
},
)
이 예제에서는 /details/123
과 같은 형식의 경로를 처리하고, ID 값을 추출하여 DetailsScreen
에 전달합니다.
명명된 라우트에서 결과 받기
화면 간 이동 후 결과를 받아야 하는 경우:
// 결과를 기다리는 화면에서
void _navigateAndGetResult() async {
// 명명된 라우트로 이동하고 결과 기다리기
final result = await Navigator.pushNamed(context, '/selection');
// 결과 처리
if (result != null) {
setState(() {
_selectedItem = result.toString();
});
}
}
// 결과를 반환하는 화면에서
ElevatedButton(
onPressed: () {
// 결과와 함께 이전 화면으로 돌아가기
Navigator.pop(context, '선택된 항목: 항목 1');
},
child: Text('항목 1 선택'),
)
중첩된 명명된 라우트
대규모 앱에서는 라우트를 더 체계적으로 구성하기 위해 중첩된 네비게이터를 사용할 수 있습니다:
MaterialApp(
initialRoute: '/',
routes: {
'/': (context) => HomeScreen(),
'/main': (context) => MainScreen(),
// 중첩된 라우트 예시
'/main/feed': (context) => FeedScreen(),
'/main/messages': (context) => MessagesScreen(),
'/main/profile': (context) => UserProfileScreen(),
},
)
명명된 라우트 모범 사례
- 일관된 명명 규칙 사용: 모든 경로에 일관된 명명 규칙을 사용하세요 (예:
/feature/subfeature
). 라우트 상수 정의: 문자열 리터럴 대신 상수를 사용하면 오타를 방지할 수 있습니다.
class Routes { static const String home = '/'; static const String details = '/details'; static const String settings = '/settings'; static const String profile = '/profile'; } // 사용할 때 Navigator.pushNamed(context, Routes.details);
라우트 생성을 위한 도우미 클래스: 복잡한 앱에서는 라우트 생성 로직을 분리하는 것이 좋습니다.
class AppRouter { static Route<dynamic> generateRoute(RouteSettings settings) { switch (settings.name) { case Routes.home: return MaterialPageRoute(builder: (_) => HomeScreen()); case Routes.details: final args = settings.arguments as Map<String, dynamic>; return MaterialPageRoute(builder: (_) => DetailsScreen(id: args['id'])); default: return MaterialPageRoute(builder: (_) => NotFoundScreen()); } } } // MaterialApp에서 사용 MaterialApp( onGenerateRoute: AppRouter.generateRoute, )
GoRouter 패키지 사용하기
더 강력한 라우팅 기능이 필요한 경우 go_router
패키지를 사용할 수 있습니다. 이 패키지는 선언적 라우팅, 라우트 매개변수, 중첩 라우팅, 라우트 리디렉션 등의 고급 기능을 제공합니다.
import 'package:flutter/material.dart';
import 'package:go_router/go_router.dart';
void main() {
runApp(MyApp());
}
final GoRouter _router = GoRouter(
routes: [
GoRoute(
path: '/',
builder: (context, state) => HomeScreen(),
routes: [
GoRoute(
path: 'details/:id',
builder: (context, state) {
final id = state.params['id'];
return DetailsScreen(id: int.parse(id!));
},
),
GoRoute(
path: 'settings',
builder: (context, state) => SettingsScreen(),
),
],
),
],
);
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp.router(
routerConfig: _router,
title: 'GoRouter 예제',
);
}
}
// 사용 예시
TextButton(
onPressed: () => context.go('/details/123'),
child: Text('상세 정보로 이동'),
)
결론
Flutter의 명명된 라우트 시스템은 앱 내 화면 간 이동을 관리하는 강력한 방법을 제공합니다. 간단한 앱에서는 기본 routes
맵을 사용하고, 복잡한 앱에서는 onGenerateRoute
나 GoRouter
와 같은 라우팅 패키지를 사용하여 더 유연한 내비게이션 구조를 구현할 수 있습니다.
명명된 라우트를 적절히 사용하면 앱의 구조를 더 명확하게 만들고, 코드를 더 깔끔하게 유지하며, 내비게이션 관련 버그를 줄일 수 있습니다. 또한 웹 URL과 유사한 경로 구조를 통해 딥 링크 지원이 용이해지고, 앱의 다양한 화면으로 외부에서 직접 접근할 수 있게 됩니다.