Skip to content

Commit 1b81e4b

Browse files
authored
[Feat] 웹뷰 자동 로그인 구현 (#195)
* Chore: turbo.json globalEnv 추가 * Feat: WebView 환경 감지하여 API URL 분기 처리 * Feat: postMessage 처리 추가하여 WebView 로그인/로그아웃 상태 동기화 * Feat: 개발용 WebView 설정 및 message handler 연결 * Feat: Webview 환경에서 페이지 전환 로직 분기 처리 * Chore: 줄바꿈 (prettier) * Chore: @react-native-async-storage/async-storage install * Chore: 의존성 업데이트트 * Feat: window.d.ts 추가 * Chore: 모바일 앱 개발을 위한 환경 변수 업데이트 * Refactor: messageHandler를 webviewLoginBridge로 이름 변경 * Feat: 웹뷰에서 자동로그인을 위한 토큰 전송 구현 * Feat: 웹뷰 자동로그인을 위한 웹 측 리스너 구현 * Feat: 웹에서 앱으로 로그인/로그아웃 메시지 전송 구현 * Feat: 웹뷰 환경에서 자동로그인을 위한 토큰 검증 로직 추가 * Chore: prettier 적용용 * Chore: 의존성 업데이트 * Refactor: window.d.ts -> global.d.ts로 변경 * Refactor: 로그인 API 로직 개선 및 토큰 검증 로직직 추가 * Refactor: useIsReactNativeWebview-> useWebview로 훅 이름 변경 * Chore: globalEnv 수정 * Chore: 의존성 업데이트 * Chore: global.d.ts platform 제거거 * Refactor: 웹뷰 이벤트 핸들러 로직 분리 * Refactor: 웹뷰 메시지 처리 로직 개선 * Refactor: 웹뷰 메세지 타입 상수화 * Remove: useWebview.ts 삭제 * Chore: globalEnv 설정 추가 * Chore: 파일명 대소문자 변경 * Chore: React import 구문 추가 * Refactor: getBaseUrl -> getNativeApiUrl로 변경 * Refactor: parseMessageEvent -> parseMessage로 변경 * Fix: token 유효성 검사 로직 제거 * Refactor: WebView API URL 체크 로직을 getWebApiUrl 유틸 함수로 분리 * Chore: 불필요한 className 제거 * Style: MembersSettingModal의 gap 간격 제거 * Refactor: WebView 메시지 핸들러에 accessToken 체크 로직 추가 및 불필요한 코드 제거 * Chore: AuthGuard에서 웹뷰 리다이렉트 로직 제거 * Feat: WebView 메시지에 따른 분기처리 * Chore: 의존성 업데이트트 * Chore: react-device-detect 라이브러리 제거 * Refactor: JSON.stringify를 stringifyJson 유틸함수로 대체 * Refactor: WebView 감지 로직 개선 * Refactor: react-device-detect 의존성 제거 후 내부 유틸함수로 대체 * Refactor: WebView 이벤트 핸들링 구조 개선 * Refactor: 프로젝트 전반적인 네이밍 Native -> WebView로 통일 * Refactor: AsyncStorage 핸들링 구조 개선 * Refactor: WebView 메시지 브릿지 구조 개선 * Chore: detectWebView.ts 삭제 * Refactor: 메시지 핸들러 파라미터 수정 및 불필요한 WebView 속성 제거 * Chore: import 문 줄바꿈 * Refactor: WebView 디바이스 감지 로직 간소화 * Refactor: WebView 디바이스 감지 로직을 별도 유틸리티로 분리하여 hook 간소화 * Refactor: WebView 메시지 브릿지를 이벤트 리스너로 변경 * Chore: sendMessageToWebView import 경로를 절대 경로로 변경 * Refactor: switch 문의 case 블록 스코프 처리 * Chore: import React 문 제거 * Chore: Merge branch 'develop' of https://github.com/codeit-internship-group-b/codeit-resources into 194-feat-rn-webview-브릿지-통신-구현 * Chore: ReactNativeWebView 객체 접근 방식을 옵셔널 체이닝으로 변경 * Fix: 불필요한 return 제거
1 parent a3e078b commit 1b81e4b

39 files changed

Lines changed: 2193 additions & 1646 deletions

apps/mobile/app/(route)/dashboard.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,10 @@ import WebView, { WebViewMessageEvent } from "react-native-webview";
22

33
import { ROUTES } from "@/constants/routes";
44
import { useHandleNavigationActions } from "@/hooks/useHandleNavigationActions";
5-
import { getBaseUrl } from "@/utils/getBaseUrl";
5+
import { getWebViewApiUrl } from "@/utils/getWebViewApiUrl";
66

77
export default function DashboardScreen() {
8-
const baseUrl = getBaseUrl();
8+
const baseUrl = getWebViewApiUrl();
99
const handleNavigationActions = useHandleNavigationActions();
1010

1111
const requestOnMessage = (e: WebViewMessageEvent) => {

apps/mobile/app/(route)/index.tsx

Lines changed: 22 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,33 @@
1+
import { useRef } from "react";
12
import { WebView, WebViewMessageEvent } from "react-native-webview";
23

34
import { useHandleNavigationActions } from "@/hooks/useHandleNavigationActions";
4-
import { getBaseUrl } from "@/utils/getBaseUrl";
5+
import { handleAuthStorage } from "@/store/authStorage";
6+
import { getWebViewApiUrl } from "@/utils/getWebViewApiUrl";
7+
import { parseMessage } from "@/utils/parseMessage";
8+
import { webViewLoadHandler } from "@/utils/webViewLoadHandler";
59

610
export default function HomeScreen() {
7-
// TODO : login상태에 따른 분기 처리 설정
11+
const webviewRef = useRef<WebView>(null);
812

9-
const baseUrl = getBaseUrl();
13+
const baseUrl = getWebViewApiUrl();
1014
const handleNavigationActions = useHandleNavigationActions();
15+
const { event, handler } = webViewLoadHandler(webviewRef);
1116

12-
const requestOnMessage = (e: WebViewMessageEvent) => {
13-
handleNavigationActions(e);
17+
const handleMessage = (e: WebViewMessageEvent) => {
18+
const { type, data } = parseMessage(e);
19+
20+
handleNavigationActions({ type, data });
21+
handleAuthStorage({ type, data });
1422
};
1523

16-
return <WebView className="flex-1" source={{ uri: `${baseUrl}` }} onMessage={requestOnMessage} />;
24+
return (
25+
<WebView
26+
ref={webviewRef}
27+
source={{ uri: `${baseUrl}` }}
28+
onMessage={handleMessage}
29+
{...{ [event]: handler }}
30+
className="flex-1"
31+
/>
32+
);
1733
}

apps/mobile/app/(route)/meetings.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,10 @@ import WebView, { WebViewMessageEvent } from "react-native-webview";
22

33
import { ROUTES } from "@/constants/routes";
44
import { useHandleNavigationActions } from "@/hooks/useHandleNavigationActions";
5-
import { getBaseUrl } from "@/utils/getBaseUrl";
5+
import { getWebViewApiUrl } from "@/utils/getWebViewApiUrl";
66

77
export default function MeetingsScreen() {
8-
const baseUrl = getBaseUrl();
8+
const baseUrl = getWebViewApiUrl();
99
const handleNavigationActions = useHandleNavigationActions();
1010

1111
const requestOnMessage = (e: WebViewMessageEvent) => {

apps/mobile/app/(route)/seats.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,10 @@ import WebView, { WebViewMessageEvent } from "react-native-webview";
22

33
import { ROUTES } from "@/constants/routes";
44
import { useHandleNavigationActions } from "@/hooks/useHandleNavigationActions";
5-
import { getBaseUrl } from "@/utils/getBaseUrl";
5+
import { getWebViewApiUrl } from "@/utils/getWebViewApiUrl";
66

77
export default function SeatsScreen() {
8-
const baseUrl = getBaseUrl();
8+
const baseUrl = getWebViewApiUrl();
99
const handleNavigationActions = useHandleNavigationActions();
1010

1111
const requestOnMessage = (e: WebViewMessageEvent) => {

apps/mobile/app/(route)/settings.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,10 @@ import WebView, { WebViewMessageEvent } from "react-native-webview";
22

33
import { ROUTES } from "@/constants/routes";
44
import { useHandleNavigationActions } from "@/hooks/useHandleNavigationActions";
5-
import { getBaseUrl } from "@/utils/getBaseUrl";
5+
import { getWebViewApiUrl } from "@/utils/getWebViewApiUrl";
66

77
export default function SettingsScreen() {
8-
const baseUrl = getBaseUrl();
8+
const baseUrl = getWebViewApiUrl();
99
const handleNavigationActions = useHandleNavigationActions();
1010

1111
const requestOnMessage = (e: WebViewMessageEvent) => {

apps/mobile/hooks/useHandleNavigationActions.ts

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,19 @@
11
import { StackActions } from "@react-navigation/native";
22
import { WEBVIEW_MESSAGE_TYPES } from "@repo/constants";
3+
import { Message } from "@ui/src/types/WebViewMessageTypes";
34
import { useNavigation, usePathname } from "expo-router";
4-
import { WebViewMessageEvent } from "react-native-webview";
55

66
import { DIR_NAME } from "@/constants/routes";
7-
import { parseMessageEvent } from "@/utils/parseMessageEvent";
87

98
export const useHandleNavigationActions = () => {
109
const pathname = usePathname();
1110
const navigation = useNavigation();
1211

13-
const handleNavigationActions = (e: WebViewMessageEvent) => {
14-
const parsedMessage = parseMessageEvent(e);
15-
if (!parsedMessage || parsedMessage.type !== WEBVIEW_MESSAGE_TYPES.ROUTER_EVENT) return;
12+
const handleNavigationActions = ({ type, data }: Message<string>) => {
13+
if (type !== WEBVIEW_MESSAGE_TYPES.ROUTER_EVENT) return;
14+
15+
const path = data;
1616

17-
const { data: path } = parsedMessage;
1817
if (pathname === path) return;
1918

2019
const action = path === "back" ? StackActions.pop(1) : StackActions.push(`${DIR_NAME}${path}`);

apps/mobile/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
},
1818
"dependencies": {
1919
"@expo/vector-icons": "^14.0.2",
20+
"@react-native-async-storage/async-storage": "^2.1.0",
2021
"@react-navigation/native": "^6.1.18",
2122
"@repo/ui": "workspace:*",
2223
"babel-preset-expo": "^12.0.5",

apps/mobile/store/authStorage.ts

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
import AsyncStorage from "@react-native-async-storage/async-storage";
2+
import { WEBVIEW_MESSAGE_TYPES } from "@repo/constants";
3+
import { IUser } from "@repo/types/userType";
4+
import { LoginData, Message } from "@repo/ui/src/types/WebViewMessageTypes";
5+
import { stringifyJson } from "@repo/ui/src/utils/stringifyJson";
6+
7+
export const getAuthData = async () => {
8+
const [accessToken, userStr] = await Promise.all([AsyncStorage.getItem("accessToken"), AsyncStorage.getItem("user")]);
9+
10+
return {
11+
accessToken,
12+
user: userStr ? JSON.parse(userStr) : null,
13+
};
14+
};
15+
16+
export const setAuthData = async ({ accessToken, user }: LoginData) => {
17+
await Promise.all([
18+
AsyncStorage.setItem("accessToken", accessToken),
19+
AsyncStorage.setItem("user", stringifyJson(user)),
20+
]);
21+
};
22+
23+
export const clearAuthData = async () => {
24+
await Promise.all([AsyncStorage.removeItem("accessToken"), AsyncStorage.removeItem("user")]);
25+
};
26+
27+
export const handleAuthStorage = async ({ type, data }: Message<LoginData>) => {
28+
switch (type) {
29+
case WEBVIEW_MESSAGE_TYPES.SIGN_IN_SUCCESS: {
30+
const { user, accessToken } = data;
31+
await setAuthData({ accessToken, user });
32+
break;
33+
}
34+
case WEBVIEW_MESSAGE_TYPES.SIGN_OUT_SUCCESS:
35+
await clearAuthData();
36+
break;
37+
}
38+
};

apps/mobile/tsconfig.json

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,16 +8,17 @@
88
"@ui/*": ["../../packages/ui/*"],
99
"@/public/*": ["./public/*"],
1010
"@repo/types/*": ["../../packages/types/src/*"]
11-
}
11+
},
12+
"jsx": "preserve"
1213
},
1314
"include": [
1415
"**/*.ts",
1516
"**/*.tsx",
16-
".expo/types/**/*.ts",
1717
"nativewind-env.d.ts",
1818
"**/__tests__/**/*.ts",
1919
"**/__tests__/**/*.tsx",
2020
"**/*.d.ts",
21+
".expo/types/**/*.ts",
2122
"expo-env.d.ts"
2223
],
2324
"exclude": ["node_modules"]

0 commit comments

Comments
 (0)