From 32ca6a9b6704a07cfb25bdff52fc4a1a59a13fb4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=9D=B4=EC=A4=80=EC=98=81?= Date: Wed, 25 Feb 2026 16:48:45 +0900 Subject: [PATCH 1/2] =?UTF-8?q?refactor:=20=ED=91=B8=EC=8B=9C=20=EC=95=8C?= =?UTF-8?q?=EB=A6=BC=20=ED=86=A0=ED=81=B0=20=EB=A1=9C=EC=A7=81=20=EB=B8=8C?= =?UTF-8?q?=EB=A6=BF=EC=A7=80=20=EB=B0=A9=EC=8B=9D=EC=9C=BC=EB=A1=9C=20?= =?UTF-8?q?=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/apis/client.ts | 4 ++ src/apis/notification/index.ts | 5 +++ src/global.d.ts | 5 +++ src/pages/User/MyPage/hooks/useLogout.ts | 3 ++ src/stores/authStore.ts | 54 +++--------------------- 5 files changed, 22 insertions(+), 49 deletions(-) create mode 100644 src/global.d.ts diff --git a/src/apis/client.ts b/src/apis/client.ts index 4fe088fd..2697daa5 100644 --- a/src/apis/client.ts +++ b/src/apis/client.ts @@ -163,6 +163,10 @@ async function handleUnauthorized(endPoint, options, timeout); } catch { useAuthStore.getState().clearAuth(); diff --git a/src/apis/notification/index.ts b/src/apis/notification/index.ts index 41e25d49..f66b61ba 100644 --- a/src/apis/notification/index.ts +++ b/src/apis/notification/index.ts @@ -1,6 +1,11 @@ import { apiClient } from '../client'; export const registerPushToken = async (token: string) => { + if (window.ReactNativeWebView) { + console.log('RN WebView 환경: 웹에서 푸시 토큰 등록 생략 (네이티브가 처리)'); + return; + } + const response = await apiClient.post('notifications/tokens', { body: { token }, requiresAuth: true, diff --git a/src/global.d.ts b/src/global.d.ts new file mode 100644 index 00000000..c591fa31 --- /dev/null +++ b/src/global.d.ts @@ -0,0 +1,5 @@ +interface Window { + ReactNativeWebView?: { + postMessage: (message: string) => void; + }; +} diff --git a/src/pages/User/MyPage/hooks/useLogout.ts b/src/pages/User/MyPage/hooks/useLogout.ts index 2481f535..e7d2e327 100644 --- a/src/pages/User/MyPage/hooks/useLogout.ts +++ b/src/pages/User/MyPage/hooks/useLogout.ts @@ -10,6 +10,9 @@ export const useLogoutMutation = () => { return useMutation({ mutationFn: logout, onSuccess: () => { + if (window.ReactNativeWebView) { + window.ReactNativeWebView.postMessage(JSON.stringify({ type: 'LOGOUT' })); + } clearAuth(); navigate('/'); }, diff --git a/src/stores/authStore.ts b/src/stores/authStore.ts index 2a22139d..c97d8719 100644 --- a/src/stores/authStore.ts +++ b/src/stores/authStore.ts @@ -1,29 +1,6 @@ import { create } from 'zustand'; import { getMyInfo, refreshAccessToken } from '@/apis/auth'; import type { MyInfoResponse } from '@/apis/auth/entity'; -import { registerPushToken } from '@/apis/notification'; - -const PUSH_TOKEN_STORAGE_KEY = 'REGISTERED_PUSH_TOKEN'; -const PENDING_PUSH_TOKEN_KEY = 'PENDING_PUSH_TOKEN'; - -async function registerPushTokenIfNeeded() { - const token = localStorage.getItem(PENDING_PUSH_TOKEN_KEY); - if (!token) return; - - const lastRegisteredToken = localStorage.getItem(PUSH_TOKEN_STORAGE_KEY); - if (lastRegisteredToken === token) { - localStorage.removeItem(PENDING_PUSH_TOKEN_KEY); - return; - } - - try { - await registerPushToken(token); - localStorage.setItem(PUSH_TOKEN_STORAGE_KEY, token); - localStorage.removeItem(PENDING_PUSH_TOKEN_KEY); - } catch (error) { - console.error('푸시 토큰 등록 실패:', error); - } -} interface AuthState { user: MyInfoResponse | null; @@ -50,14 +27,16 @@ export const useAuthStore = create((set, get) => ({ } try { - // 1. 토큰 갱신 (필수 - 이후 API 호출에 필요) const accessToken = await refreshAccessToken(); set({ accessToken }); - // 2. 사용자 정보와 푸시 토큰 등록을 병렬로 실행 - const [user] = await Promise.all([getMyInfo(), registerPushTokenIfNeeded()]); + const user = await getMyInfo(); set({ user, isAuthenticated: true, isLoading: false }); + + if (window.ReactNativeWebView) { + window.ReactNativeWebView.postMessage(JSON.stringify({ type: 'LOGIN_COMPLETE', accessToken })); + } } catch { set({ user: null, accessToken: null, isAuthenticated: false, isLoading: false }); } @@ -71,26 +50,3 @@ export const useAuthStore = create((set, get) => ({ clearAuth: () => set({ user: null, accessToken: null, isAuthenticated: false, isLoading: false }), })); - -window.addEventListener('message', (event: MessageEvent) => { - try { - const data = JSON.parse(event.data); - if (data.type !== 'PUSH_TOKEN' || !data.token) return; - - const lastToken = localStorage.getItem(PUSH_TOKEN_STORAGE_KEY); - if (lastToken === data.token) return; - - const { accessToken } = useAuthStore.getState(); - if (!accessToken) { - // initialize() 완료 전 도착한 경우 — pending으로 저장 후 initialize()에서 처리 - localStorage.setItem(PENDING_PUSH_TOKEN_KEY, data.token); - return; - } - - registerPushToken(data.token) - .then(() => localStorage.setItem(PUSH_TOKEN_STORAGE_KEY, data.token)) - .catch((error) => console.error('푸시 토큰 등록 실패:', error)); - } catch { - // JSON 파싱 실패 등 무시 - } -}); From 10d976b61a7f8aefe51025671bc3a212108b6e34 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=9D=B4=EC=A4=80=EC=98=81?= Date: Wed, 25 Feb 2026 17:08:41 +0900 Subject: [PATCH 2/2] =?UTF-8?q?fix:=20try=20catch=20=EC=A0=81=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/apis/client.ts | 8 ++++++-- src/apis/notification/index.ts | 4 +++- src/pages/User/MyPage/hooks/useLogout.ts | 8 ++++++-- src/stores/authStore.ts | 8 ++++++-- 4 files changed, 21 insertions(+), 7 deletions(-) diff --git a/src/apis/client.ts b/src/apis/client.ts index 2697daa5..624a0ec6 100644 --- a/src/apis/client.ts +++ b/src/apis/client.ts @@ -163,8 +163,12 @@ async function handleUnauthorized(endPoint, options, timeout); diff --git a/src/apis/notification/index.ts b/src/apis/notification/index.ts index f66b61ba..d90d626d 100644 --- a/src/apis/notification/index.ts +++ b/src/apis/notification/index.ts @@ -2,7 +2,9 @@ import { apiClient } from '../client'; export const registerPushToken = async (token: string) => { if (window.ReactNativeWebView) { - console.log('RN WebView 환경: 웹에서 푸시 토큰 등록 생략 (네이티브가 처리)'); + if (import.meta.env.DEV) { + console.log('RN WebView 환경: 웹에서 푸시 토큰 등록 생략 (네이티브가 처리)'); + } return; } diff --git a/src/pages/User/MyPage/hooks/useLogout.ts b/src/pages/User/MyPage/hooks/useLogout.ts index e7d2e327..89e085ff 100644 --- a/src/pages/User/MyPage/hooks/useLogout.ts +++ b/src/pages/User/MyPage/hooks/useLogout.ts @@ -10,8 +10,12 @@ export const useLogoutMutation = () => { return useMutation({ mutationFn: logout, onSuccess: () => { - if (window.ReactNativeWebView) { - window.ReactNativeWebView.postMessage(JSON.stringify({ type: 'LOGOUT' })); + try { + if (window.ReactNativeWebView) { + window.ReactNativeWebView.postMessage(JSON.stringify({ type: 'LOGOUT' })); + } + } catch { + // 브릿지 전달 실패가 로그아웃 흐름을 중단시키지 않도록 무시 } clearAuth(); navigate('/'); diff --git a/src/stores/authStore.ts b/src/stores/authStore.ts index c97d8719..fb8b7e38 100644 --- a/src/stores/authStore.ts +++ b/src/stores/authStore.ts @@ -34,8 +34,12 @@ export const useAuthStore = create((set, get) => ({ set({ user, isAuthenticated: true, isLoading: false }); - if (window.ReactNativeWebView) { - window.ReactNativeWebView.postMessage(JSON.stringify({ type: 'LOGIN_COMPLETE', accessToken })); + try { + if (window.ReactNativeWebView) { + window.ReactNativeWebView.postMessage(JSON.stringify({ type: 'LOGIN_COMPLETE', accessToken })); + } + } catch { + // 브릿지 전달 실패가 인증 성공 상태를 롤백시키지 않도록 무시 } } catch { set({ user: null, accessToken: null, isAuthenticated: false, isLoading: false });