아래 케이스에서 자유게시판 탭 최신 데이터가 갱신되지 않는 문제가 발생했다.
현재 라우트 구조는 바텀 네비게이션 영역(Shell)과 네비게이션 위 오버레이(Root)로 분리되어 있다.
ShellRoute (바텀 네비게이션 영역)
└ /boards/:code (TeamBoardPage)
RootNavigator (바텀 네비게이션 위)
└ /boards/:code/post/new
└ /boards/:code/posts/:postId
자유게시판 탭은 ShellRoute 내부에 있는데, 상세/작성 화면은 Root Navigator에 올라가므로:
결과적으로 RouteObserver/RouteAware 기반의 자동 감지 접근이 깨진 것이다.
먼저 라우트 계층을 그대로 두고, 상세/작성 복귀 시점에 refresh 신호를 명시적으로 전달하는 방식을 생각했다.
글쓰기 화면으로 이동한 뒤 돌아왔을 때 tick을 증가시켜 “복귀 이벤트”를 신호로 만든다.
onPressed: () async {
await context.push(
AppRoutePaths.boardPostWriteLocation(widget.teamCode),
);
_refreshTick.value += 1;
}
FreeBoardTab은 ValueListenable<int> 형태의 신호를 받아, tick 변화가 감지되면 refresh를 실행한다.
class FreeBoardTab extends StatefulWidget {
final ValueListenable<int>? refreshSignal;
}
void _handleRefreshSignal() {
final signal = widget.refreshSignal;
if (signal == null) return;
if (signal.value == _lastRefreshTick) return;
_lastRefreshTick = signal.value;
_vm.refresh();
}
라우트 구조 변경이 없어 빨리 해결할 수 있다는 장점이 있지만,
게시판 플로우가 확장될수록 수동 refresh 로직을 하나하나 추가해줘야 하기 때문에 유지보수 비용이 커질 가능성이 있다고 판단했다.
게시글 상세/작성 화면을 ShellRoute 내부로 편입하고, 해당 라우트에서는 바텀 네비를 숨기는(fullscreen 전환) 방식으로 전환한다.
RouteAware.didPopNext() 기반 refresh가 정상 동작GoRoute(
path: AppRoutePaths.board,
builder: (_, state) {
final code = state.pathParameters['code']!;
final tabParam = state.uri.queryParameters['tab'] ?? 'onlywan';
return TeamBoardPage(
teamCode: code,
activeTab: boardTabFromPath(tabParam),
);
},
routes: [
GoRoute(
path: AppRoutePaths.boardPostWrite,
name: AppRouteNames.writingPost,
builder: (_, state) {
final code = state.pathParameters['code']!;
return WritingPostPage(teamCode: code);
},
),
GoRoute(
path: AppRoutePaths.boardPostDetail,
name: AppRouteNames.postDetail,
builder: (context, state) {
final teamCode = state.pathParameters['code']!;
final postId = int.parse(state.pathParameters['postId']!);
return PostDetailPage(teamCode: teamCode, postId: postId);
},
),
],
),
bool _shouldHideBottomNav(GoRouter router) {
final config = router.routerDelegate.currentConfiguration;
if (config.matches.isEmpty) return false;
// 중첩 라우트에서 실제로 활성화된 GoRoute name을 찾는다.
final name = _findLastGoRouteName(config.matches);
return name != null && hideBottomNavRouteNames.contains(name);
}
String? _findLastGoRouteName(List<RouteMatchBase> matches) {
// ShellRoute 등을 통과하므로, 가장 안쪽 GoRoute를 찾기 위해 역순으로 탐색한다.
for (final match in matches.reversed) {
final route = match.route;
if (route is GoRoute) {
return route.name;
}
final nested = match is ShellRouteMatch ? match.matches : null;
if (nested != null && nested.isNotEmpty) {
// ShellRoute 내부 매칭이 있으면 재귀적으로 더 내려간다.
final nestedName = _findLastGoRouteName(nested);
if (nestedName != null) return nestedName;
}
}
return null;
}
@override
void didPopNext() {
if (!mounted) return;
// 상세/작성 화면에서 뒤로가기(pop)로 돌아오면 목록을 새로고침한다.
_vm.refresh();
}
장점
단점
RouteAware / RouteObserver는 “구독한 스택” 기준이라, Shell(탭)에서 Root pop을 자동 감지하기 어려움➡️ 로그인/설정 같이 앱 전역 기능에는 적합하지만, 게시판 상세/작성처럼 “커뮤니티 도메인 화면”은 동기화 비용이 커짐.
장점
didPopNext, push result)가 자연스러움단점
➡️ 커뮤니티 게시판 내 화면 등 특정 도메인에 귀속된 화면에는 적합하다고 생각, 전역적인 온보딩 같은 화면은 Root로 분리하는 혼합 전략이 맞다고 판단