“특정화면에서 다른 화면으로 라우팅을 할때 초기에는 Navigator.push
를 사용했다. 물론 직관적이어서 편하게 느껴졌지만 앱 개발이 깊어 질 수록 라우팅을 관리하기가 복잡하고 여려워진다는 문제가 있었다.”
Navigator.push
사용
1
2
3
4
Navigator.push(
context,
MaterialPageRoute(builder: (context) => NewScreen()),
);
장점:
직관적이고 간단: 코드가 직관적이며 각 화면에서 바로 다른 화면으로 전환하는 것이 간단하다.
유연한 화면 전환: 특정 상황에 따라 다른 화면으로 전환하는 로직을 구현하기 쉽다.
커스터마이징 용이: 전환 애니메이션을 쉽게 사용자 정의할 수 있다.
단점:
라우팅 경로 관리의 분산: 라우팅 로직이 여러 화면에 분산되어 있어 관리가 어려울 수 있다.
상태 전달 복잡성: 화면 간의 상태 전달이 복잡해질 수 있다.
“따라서, 다음과 같은 라우팅 방식을 적용했다.”
메인 화면에서 모든 라우팅 정의
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'My Application',
theme: ThemeData(
primarySwatch: Colors.blue,
),
initialRoute: '/',
routes: {
'/': (context) => HomeScreen(),
'/second': (context) => SecondScreen(),
'/third': (context) => ThirdScreen(),
},
);
}
}
장점:
중앙 집중식 라우팅 관리: 모든 라우팅 경로가 한 곳에서 관리되어 구조가 명확해진다.
유지보수 용이: 새로운 라우팅 경로 추가나 수정이 용이하다.
일관된 라우팅 패턴: 애플리케이션 전반에 걸쳐 일관된 라우팅 방식을 적용할 수 있다.
단점:
유연성 감소: 특정 화면에서만 사용되는 복잡한 라우팅 로직을 구현하기 어려울 수 있다.
라우팅 경로의 종속성: 모든 라우팅 경로가
MaterialApp
에 종속되어, 라우팅 구조가 앱의 시작점에 고정된다.
간단한 앱이나 특정 상황에 맞춘 라우팅:
Navigator.push
앱의 라우팅 구조 복잡 & 일관된 관리의 필요: 메인 화면에서 모든 라우팅을 정의
“하지만 이 방식도 라우팅이 많아질수록 점점 복잡해졌고, 라우팅 시나리오를 처리하는데도 한계가 있었다. 따라서 GoRoute 패키지를 활용하여 라우팅을 하나의 파일로 통합하여 구현하고자 하였다.”
GoRouter 사용
GoRoute는 왜 사용되는가?
메인 화면에서 모든 라우팅을 정의하는 방식의 제약
- 복잡한 라우팅 로직 처리 어려움:
- 메인 화면에서 모든 라우팅을 정의하는 방식은 기본적인 라우팅 요구사항에는 잘 작동하지만, 조건부 라우팅, 복잡한 데이터 전달, 중첩 라우트 등 복잡한 라우팅 시나리오를 처리하는 데 한계가 있다.
- 딥 링크 지원 부족:
- URL 기반의 딥 링크를 처리하는 것은 기본
Navigator
방식으로는 복잡하고 제한적이다. 웹 URL과의 통합이나 앱 외부 링크에서의 라우팅 처리가 어렵다.
- URL 기반의 딥 링크를 처리하는 것은 기본
- 상태 및 인증 관리의 분리:
- 라우팅과 관련된 상태 관리와 인증 관리는 라우팅 로직과 분리되어 있어, 이 둘을 연계하는 것이 어렵다. 예를 들어, 사용자의 로그인 상태에 따라 라우팅을 제어하거나 리다이렉션하는 로직을 구현하기 어렵다.
- 코드의 가독성과 유지보수 문제:
- 라우팅 로직이 복잡해질수록
MaterialApp
의routes
정의가 거대하고 관리하기 어려운 형태로 변할 수 있다. 이는 코드의 가독성과 유지보수에 부정적인 영향을 미친다.
- 라우팅 로직이 복잡해질수록
장점:
복잡한 라우팅 관리 용이: 대규모 앱이나 복잡한 라우팅 로직을 쉽게 관리할 수 있다.
딥 링크 지원: 딥 링크와 URL 기반 라우팅을 원활하게 지원한다.
상태 및 인증 관리 통합: 라우팅과 관련된 상태 및 인증 관리를 통합하여 구현할 수 있다.
리다이렉션 및 가드 지원: 사용자 권한에 따른 리다이렉션 및 라우팅 가드를 쉽게 구현할 수 있다.
단점:
학습 곡선:
GoRouter
와 같은 고급 라우팅 패키지는Navigator
보다 학습 곡선이 더 높다.오버헤드: 작은 규모의 앱에서는
GoRouter
의 기능이 과도하게 느껴질 수 있다.유연성 제한: 일부 커스텀 라우팅 요구사항에 대해
Navigator
만큼의 유연성을 제공하지 않을 수 있다.
“다음은 기존 Naviagtor방식에서, GoRouter 방식으로 전환하여 로그인 화면에서 조건부 라우팅 기능을 구현한 것이다.”
main.dart
routes.dart
주요 부분의 역할 및 로직 설명
router.dart
파일:이 파일은 애플리케이션의 라우팅 로직을 정의한다.
GoRouter
객체를 생성하고, 이를Provider
를 통해 제공한다.Provider
는 애플리케이션 전체에 걸쳐GoRouter
객체에 접근할 수 있도록 한다.
GoRouter
객체:GoRouter
는 라우팅을 관리하는 객체로, 여러GoRoute
객체를 포함한다.각
GoRoute
는 특정 경로에 대한 빌더와 페이지 빌더를 제공하고, 이를 통해 해당 경로에 접근할 때 보여줄 위젯이 결정된다.
- Provider를 사용하는 이유:
routerProvider
는GoRouter
객체를 생성하고 관리한다.ref.read(authRepo).isLoggedIn
을 통해 인증 상태를 확인하고, 사용자가 로그인하지 않았을 경우 로그인 또는 회원가입 화면으로 리디렉션한다.Provider
를 사용함으로써, 라우터의 상태(예: 사용자의 로그인 상태)에 따라 동적으로 라우팅을 변경할 수 있다.
main.dart
파일과SocialNetworkApp
위젯:main.dart
파일은 애플리케이션의 진입점입니다.ProviderScope
를 사용하여 Riverpod 상태 관리 시스템을 초기화한다.SocialNetworkApp
은ConsumerWidget
을 상속받아, Riverpod의 상태를 구독할 수 있다.MaterialApp.router
위젯에서routerConfig
프로퍼티에ref.watch(routerProvider)
를 사용하여 라우터 설정을 제공한다. 이를 통해 앱 내의 라우팅이GoRouter
객체에 의해 관리된다.
“이렇게 기존 Navigator
라우팅 방식을 GoRoute 패키지를 이용하여 변경해보았다. 아주 간단한 앱에서 물론 이 정도의 라우팅 방식은 오히려 비효율 적일 것이다. 하지만, 복잡한 라우팅 로직과 딥 링크, 상태 관리가 필요한 대규모 앱에서는 GoRouter
와 같은 고급 라우팅 패키지가 더 적합한 듯하다.”