Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 7 additions & 1 deletion lib/screen/tabs/community_tab.dart
Original file line number Diff line number Diff line change
Expand Up @@ -261,7 +261,13 @@ class _CommunityTabState extends State<CommunityTab>
});
} catch (_) {
if (!mounted || myReq != _reqId) return;
if (silent) return; // 조용한 갱신 실패는 기존 목록 유지(에러 화면으로 안 덮음)
// 조용한 갱신 실패는 기존 목록을 유지한다(에러 화면으로 안 덮음).
//
// 단 **스피너가 떠 있으면 그냥 반환하면 안 된다.** 최초 로드가 비행 중일 때
// 상세에서 돌아오며 silent 갱신이 _reqId 를 가져가면, 최초 로드의 응답은
// 위 가드에서 버려진다 — 이 실패마저 조용히 반환하면 _loading 을 풀 사람이
// 아무도 남지 않아 스피너가 영원히 돈다.
if (silent && !_loading) return;
setState(() {
_error = '게시글을 불러오지 못했어요';
_loading = false;
Expand Down
26 changes: 23 additions & 3 deletions lib/services/push_service.dart
Original file line number Diff line number Diff line change
Expand Up @@ -177,16 +177,36 @@ class PushService {
why: '토큰을 못 읽으면 서버 해제를 건너뛴다 — 아래 기기측 삭제는 그대로 시도',
);
}
// 세션이 이미 회수돼 RPC 를 부를 자격이 없는 상태인가.
// app.uid() 가 token_version 불일치·비활성 상태에서 null 을 주면 RLS 가
// 42501(not_authenticated)로 막는다 — 강제 로그아웃의 정상 결과다.
bool isSessionRevoked(Object e) =>
e is PostgrestException &&
(e.code == '42501' || e.message.contains('not_authenticated'));

if (token != null && SessionManager.instance.isLoggedIn) {
try {
await Supabase.instance.client.rpc(
'release_device_token',
params: {'p_token': token},
);
} catch (e, st) {
// 오프라인 로그아웃이 정확히 이 경로다. 로그아웃 자체는 막지 않되,
// **얼마나 자주 새는지는 알아야 한다** — 이 실패가 곧 #237 의 재현이다.
ErrorReporter.report(e, where: 'push.releaseToken', stackTrace: st);
// 세션이 이미 회수된 경우(다른 기기 비밀번호 변경·정지·탈퇴)는 실패가
// **정상이다.** app.uid() 가 token_version 을 보므로 부를 자격이 없고,
// 기기 토큰은 DB 트리거(users_revoke_device_tokens)가 이미 껐다.
// 이걸 오류로 올리면 있지도 않은 누수를 알리는 거짓 경보가 된다.
//
// 그 밖의 실패(오프라인 로그아웃 등)는 진짜 누수다 — #237 의 재현이므로
// 얼마나 자주 새는지 계속 본다.
if (isSessionRevoked(e)) {
ErrorReporter.ignored(
e,
where: 'push.releaseToken',
why: '세션이 이미 회수됨 — 기기 토큰은 DB 트리거가 껐다',
);
} else {
ErrorReporter.report(e, where: 'push.releaseToken', stackTrace: st);
}
}
}
try {
Expand Down
Loading