Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
62 commits
Select commit Hold shift + click to select a range
c90736d
Chore: turbo.json globalEnv 추가
miraclee1226 Jan 8, 2025
eb053ae
Feat: WebView 환경 감지하여 API URL 분기 처리
miraclee1226 Jan 8, 2025
bd1c2bf
Feat: postMessage 처리 추가하여 WebView 로그인/로그아웃 상태 동기화
miraclee1226 Jan 8, 2025
22ff9da
Feat: 개발용 WebView 설정 및 message handler 연결
miraclee1226 Jan 8, 2025
2327638
Feat: Webview 환경에서 페이지 전환 로직 분기 처리
miraclee1226 Jan 8, 2025
e5fe6bf
Chore: 줄바꿈 (prettier)
miraclee1226 Jan 8, 2025
2f0b978
Chore: @react-native-async-storage/async-storage install
miraclee1226 Jan 8, 2025
77a8806
Chore: 의존성 업데이트트
miraclee1226 Jan 8, 2025
0e82561
Feat: window.d.ts 추가
miraclee1226 Jan 8, 2025
c2c1666
Chore: 모바일 앱 개발을 위한 환경 변수 업데이트
miraclee1226 Jan 10, 2025
c0c220f
Refactor: messageHandler를 webviewLoginBridge로 이름 변경
miraclee1226 Jan 10, 2025
b33fd22
Feat: 웹뷰에서 자동로그인을 위한 토큰 전송 구현
miraclee1226 Jan 10, 2025
0279355
Feat: 웹뷰 자동로그인을 위한 웹 측 리스너 구현
miraclee1226 Jan 10, 2025
1aad308
Feat: 웹에서 앱으로 로그인/로그아웃 메시지 전송 구현
miraclee1226 Jan 10, 2025
9456b8a
Feat: 웹뷰 환경에서 자동로그인을 위한 토큰 검증 로직 추가
miraclee1226 Jan 10, 2025
9ebdc3f
Chore: prettier 적용용
miraclee1226 Jan 10, 2025
f9740e9
Chore: 의존성 업데이트
miraclee1226 Jan 10, 2025
050107d
Refactor: window.d.ts -> global.d.ts로 변경
miraclee1226 Jan 13, 2025
3508d40
Refactor: 로그인 API 로직 개선 및 토큰 검증 로직직 추가
miraclee1226 Jan 13, 2025
341d3de
Refactor: useIsReactNativeWebview-> useWebview로 훅 이름 변경
miraclee1226 Jan 13, 2025
2cc7189
Chore: globalEnv 수정
miraclee1226 Jan 13, 2025
470a4dd
Chore: 의존성 업데이트
miraclee1226 Jan 13, 2025
8c266b9
Chore: global.d.ts platform 제거거
miraclee1226 Jan 13, 2025
054f0aa
Refactor: 웹뷰 이벤트 핸들러 로직 분리
miraclee1226 Jan 13, 2025
4f123f4
Refactor: 웹뷰 메시지 처리 로직 개선
miraclee1226 Jan 13, 2025
5269de5
Refactor: 웹뷰 메세지 타입 상수화
miraclee1226 Jan 13, 2025
32347cc
Remove: useWebview.ts 삭제
miraclee1226 Jan 13, 2025
fbf04fc
Chore: Merge branch 'develop' of https://github.com/codeit-internship…
miraclee1226 Jan 13, 2025
031691c
Chore: globalEnv 설정 추가
miraclee1226 Jan 15, 2025
0d78266
Chore: 파일명 대소문자 변경
miraclee1226 Jan 16, 2025
ef843a9
Chore: React import 구문 추가
miraclee1226 Jan 16, 2025
583ebb5
Refactor: getBaseUrl -> getNativeApiUrl로 변경
miraclee1226 Jan 16, 2025
3a5dbe9
Refactor: parseMessageEvent -> parseMessage로 변경
miraclee1226 Jan 16, 2025
d45af81
Fix: token 유효성 검사 로직 제거
miraclee1226 Jan 16, 2025
f00ba88
Refactor: WebView API URL 체크 로직을 getWebApiUrl 유틸 함수로 분리
miraclee1226 Jan 16, 2025
9734517
Chore: 불필요한 className 제거
miraclee1226 Jan 16, 2025
7cf18cd
Style: MembersSettingModal의 gap 간격 제거
miraclee1226 Jan 16, 2025
4fbc13e
Refactor: WebView 메시지 핸들러에 accessToken 체크 로직 추가 및 불필요한 코드 제거
miraclee1226 Jan 16, 2025
b1866a9
Chore: AuthGuard에서 웹뷰 리다이렉트 로직 제거
miraclee1226 Jan 16, 2025
5d45e13
Feat: WebView 메시지에 따른 분기처리
miraclee1226 Jan 16, 2025
4fd40cf
Chore: 의존성 업데이트트
miraclee1226 Jan 16, 2025
656a9ba
Chore: react-device-detect 라이브러리 제거
miraclee1226 Jan 21, 2025
c7df4cb
Refactor: JSON.stringify를 stringifyJson 유틸함수로 대체
miraclee1226 Jan 21, 2025
4925109
Refactor: WebView 감지 로직 개선
miraclee1226 Jan 21, 2025
9e33165
Refactor: react-device-detect 의존성 제거 후 내부 유틸함수로 대체
miraclee1226 Jan 21, 2025
feb13bf
Refactor: WebView 이벤트 핸들링 구조 개선
miraclee1226 Jan 21, 2025
a57e38b
Refactor: 프로젝트 전반적인 네이밍 Native -> WebView로 통일
miraclee1226 Jan 21, 2025
4305856
Refactor: AsyncStorage 핸들링 구조 개선
miraclee1226 Jan 21, 2025
5343fce
Refactor: WebView 메시지 브릿지 구조 개선
miraclee1226 Jan 21, 2025
26f0889
Chore: detectWebView.ts 삭제
miraclee1226 Jan 21, 2025
d05d646
Refactor: 메시지 핸들러 파라미터 수정 및 불필요한 WebView 속성 제거
miraclee1226 Jan 22, 2025
57fab4a
Chore: import 문 줄바꿈
miraclee1226 Jan 22, 2025
f60439c
Refactor: WebView 디바이스 감지 로직 간소화
miraclee1226 Jan 22, 2025
a1ea33f
Refactor: WebView 디바이스 감지 로직을 별도 유틸리티로 분리하여 hook 간소화
miraclee1226 Jan 22, 2025
f11ecfd
Refactor: WebView 메시지 브릿지를 이벤트 리스너로 변경
miraclee1226 Jan 22, 2025
41ff8e4
Chore: sendMessageToWebView import 경로를 절대 경로로 변경
miraclee1226 Jan 22, 2025
7ac1522
Refactor: switch 문의 case 블록 스코프 처리
miraclee1226 Jan 22, 2025
ac573c1
Chore: import React 문 제거
miraclee1226 Jan 23, 2025
9857a2b
Chore: Merge branch 'develop' of https://github.com/codeit-internship…
miraclee1226 Jan 23, 2025
7a114d1
Chore: ReactNativeWebView 객체 접근 방식을 옵셔널 체이닝으로 변경
miraclee1226 Jan 23, 2025
9f8524f
Fix: 불필요한 return 제거
miraclee1226 Jan 23, 2025
d46b87c
Merge branch 'develop' into 194-feat-rn-webview-브릿지-통신-구현
miraclee1226 Jan 23, 2025
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
4 changes: 2 additions & 2 deletions apps/mobile/app/(route)/dashboard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,10 @@ import WebView, { WebViewMessageEvent } from "react-native-webview";

import { ROUTES } from "@/constants/routes";
import { useHandleNavigationActions } from "@/hooks/useHandleNavigationActions";
import { getBaseUrl } from "@/utils/getBaseUrl";
import { getWebViewApiUrl } from "@/utils/getWebViewApiUrl";

export default function DashboardScreen() {
const baseUrl = getBaseUrl();
const baseUrl = getWebViewApiUrl();
const handleNavigationActions = useHandleNavigationActions();

const requestOnMessage = (e: WebViewMessageEvent) => {
Expand Down
28 changes: 22 additions & 6 deletions apps/mobile/app/(route)/index.tsx
Original file line number Diff line number Diff line change
@@ -1,17 +1,33 @@
import { useRef } from "react";
import { WebView, WebViewMessageEvent } from "react-native-webview";

import { useHandleNavigationActions } from "@/hooks/useHandleNavigationActions";
import { getBaseUrl } from "@/utils/getBaseUrl";
import { handleAuthStorage } from "@/store/authStorage";
import { getWebViewApiUrl } from "@/utils/getWebViewApiUrl";
import { parseMessage } from "@/utils/parseMessage";
import { webViewLoadHandler } from "@/utils/webViewLoadHandler";

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

const baseUrl = getBaseUrl();
const baseUrl = getWebViewApiUrl();
const handleNavigationActions = useHandleNavigationActions();
const { event, handler } = webViewLoadHandler(webviewRef);

const requestOnMessage = (e: WebViewMessageEvent) => {
handleNavigationActions(e);
const handleMessage = (e: WebViewMessageEvent) => {
const { type, data } = parseMessage(e);

handleNavigationActions({ type, data });
handleAuthStorage({ type, data });
};

return <WebView className="flex-1" source={{ uri: `${baseUrl}` }} onMessage={requestOnMessage} />;
return (
<WebView
ref={webviewRef}
source={{ uri: `${baseUrl}` }}
onMessage={handleMessage}
{...{ [event]: handler }}
className="flex-1"
/>
);
}
4 changes: 2 additions & 2 deletions apps/mobile/app/(route)/meetings.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,10 @@ import WebView, { WebViewMessageEvent } from "react-native-webview";

import { ROUTES } from "@/constants/routes";
import { useHandleNavigationActions } from "@/hooks/useHandleNavigationActions";
import { getBaseUrl } from "@/utils/getBaseUrl";
import { getWebViewApiUrl } from "@/utils/getWebViewApiUrl";

export default function MeetingsScreen() {
const baseUrl = getBaseUrl();
const baseUrl = getWebViewApiUrl();
const handleNavigationActions = useHandleNavigationActions();

const requestOnMessage = (e: WebViewMessageEvent) => {
Expand Down
4 changes: 2 additions & 2 deletions apps/mobile/app/(route)/seats.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,10 @@ import WebView, { WebViewMessageEvent } from "react-native-webview";

import { ROUTES } from "@/constants/routes";
import { useHandleNavigationActions } from "@/hooks/useHandleNavigationActions";
import { getBaseUrl } from "@/utils/getBaseUrl";
import { getWebViewApiUrl } from "@/utils/getWebViewApiUrl";

export default function SeatsScreen() {
const baseUrl = getBaseUrl();
const baseUrl = getWebViewApiUrl();
const handleNavigationActions = useHandleNavigationActions();

const requestOnMessage = (e: WebViewMessageEvent) => {
Expand Down
4 changes: 2 additions & 2 deletions apps/mobile/app/(route)/settings.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,10 @@ import WebView, { WebViewMessageEvent } from "react-native-webview";

import { ROUTES } from "@/constants/routes";
import { useHandleNavigationActions } from "@/hooks/useHandleNavigationActions";
import { getBaseUrl } from "@/utils/getBaseUrl";
import { getWebViewApiUrl } from "@/utils/getWebViewApiUrl";

export default function SettingsScreen() {
const baseUrl = getBaseUrl();
const baseUrl = getWebViewApiUrl();
const handleNavigationActions = useHandleNavigationActions();

const requestOnMessage = (e: WebViewMessageEvent) => {
Expand Down
File renamed without changes.
11 changes: 5 additions & 6 deletions apps/mobile/hooks/useHandleNavigationActions.ts
Original file line number Diff line number Diff line change
@@ -1,20 +1,19 @@
import { StackActions } from "@react-navigation/native";
import { WEBVIEW_MESSAGE_TYPES } from "@repo/constants";
import { Message } from "@ui/src/types/WebViewMessageTypes";
import { useNavigation, usePathname } from "expo-router";
import { WebViewMessageEvent } from "react-native-webview";

import { DIR_NAME } from "@/constants/routes";
import { parseMessageEvent } from "@/utils/parseMessageEvent";

export const useHandleNavigationActions = () => {
const pathname = usePathname();
const navigation = useNavigation();

const handleNavigationActions = (e: WebViewMessageEvent) => {
const parsedMessage = parseMessageEvent(e);
if (!parsedMessage || parsedMessage.type !== WEBVIEW_MESSAGE_TYPES.ROUTER_EVENT) return;
const handleNavigationActions = ({ type, data }: Message<string>) => {
if (type !== WEBVIEW_MESSAGE_TYPES.ROUTER_EVENT) return;

const path = data;

const { data: path } = parsedMessage;
if (pathname === path) return;

const action = path === "back" ? StackActions.pop(1) : StackActions.push(`${DIR_NAME}${path}`);
Expand Down
1 change: 1 addition & 0 deletions apps/mobile/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
},
"dependencies": {
"@expo/vector-icons": "^14.0.2",
"@react-native-async-storage/async-storage": "^2.1.0",
"@react-navigation/native": "^6.1.18",
"@repo/ui": "workspace:*",
"babel-preset-expo": "^12.0.5",
Expand Down
38 changes: 38 additions & 0 deletions apps/mobile/store/authStorage.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import AsyncStorage from "@react-native-async-storage/async-storage";
import { WEBVIEW_MESSAGE_TYPES } from "@repo/constants";
import { IUser } from "@repo/types/userType";
import { LoginData, Message } from "@repo/ui/src/types/WebViewMessageTypes";
import { stringifyJson } from "@repo/ui/src/utils/stringifyJson";

export const getAuthData = async () => {
const [accessToken, userStr] = await Promise.all([AsyncStorage.getItem("accessToken"), AsyncStorage.getItem("user")]);

return {
accessToken,
user: userStr ? JSON.parse(userStr) : null,
};
};

export const setAuthData = async ({ accessToken, user }: LoginData) => {
await Promise.all([
AsyncStorage.setItem("accessToken", accessToken),
AsyncStorage.setItem("user", stringifyJson(user)),
]);
};

export const clearAuthData = async () => {
await Promise.all([AsyncStorage.removeItem("accessToken"), AsyncStorage.removeItem("user")]);
};

export const handleAuthStorage = async ({ type, data }: Message<LoginData>) => {
switch (type) {
case WEBVIEW_MESSAGE_TYPES.SIGN_IN_SUCCESS: {
const { user, accessToken } = data;
await setAuthData({ accessToken, user });
break;
}
case WEBVIEW_MESSAGE_TYPES.SIGN_OUT_SUCCESS:
await clearAuthData();
break;
}
};
5 changes: 3 additions & 2 deletions apps/mobile/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,16 +8,17 @@
"@ui/*": ["../../packages/ui/*"],
"@/public/*": ["./public/*"],
"@repo/types/*": ["../../packages/types/src/*"]
}
},
"jsx": "preserve"
},
"include": [
"**/*.ts",
"**/*.tsx",
".expo/types/**/*.ts",
"nativewind-env.d.ts",
"**/__tests__/**/*.ts",
"**/__tests__/**/*.tsx",
"**/*.d.ts",
".expo/types/**/*.ts",
"expo-env.d.ts"
],
"exclude": ["node_modules"]
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { Platform } from "react-native";

export const getBaseUrl = () => {
export const getWebViewApiUrl = () => {
if (Platform.OS === "ios") return process.env.EXPO_PUBLIC_API_URL_IOS;
if (Platform.OS === "android") return process.env.EXPO_PUBLIC_API_URL_ANDROID;
};
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { WebViewMessageEvent } from "react-native-webview";

export const parseMessageEvent = (e: WebViewMessageEvent) => {
export const parseMessage = (e: WebViewMessageEvent) => {
return JSON.parse(e.nativeEvent.data);
};
14 changes: 14 additions & 0 deletions apps/mobile/utils/sendMessageToWeb.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { stringifyJson } from "@repo/ui/src/utils/stringifyJson";
import { Message } from "@ui/src/types/WebViewMessageTypes";
import { RefObject } from "react";
import WebView from "react-native-webview";

export interface SendMessage<T> extends Message<T> {
webViewRef: RefObject<WebView<object>>;
}

export const sendMessageToWeb = <T>({ webViewRef, type, data }: SendMessage<T>) => {
if (webViewRef.current) {
webViewRef.current.postMessage(stringifyJson({ type, data }));
}
};
44 changes: 44 additions & 0 deletions apps/mobile/utils/webViewLoadHandler.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import { WEBVIEW_MESSAGE_TYPES } from "@repo/constants";
import { RefObject } from "react";
import { Platform } from "react-native";
import WebView from "react-native-webview";
import { WebViewProgressEvent } from "react-native-webview/lib/WebViewTypes";

import { getAuthData } from "@/store/authStorage";

import { sendMessageToWeb } from "./sendMessageToWeb";

interface WebViewLoadHandler {
event: "onLoadProgress" | "onLoad";
handler: (e: WebViewProgressEvent) => void;
}

const sendAuthData = async (webViewRef: RefObject<WebView<object>>) => {
const authData = await getAuthData();

sendMessageToWeb({
webViewRef,
type: WEBVIEW_MESSAGE_TYPES.AUTO_LOGIN,
data: authData,
});
};

export const webViewLoadHandler = (webViewRef: RefObject<WebView<object>>): WebViewLoadHandler => {
if (Platform.OS === "ios") {
return {
event: "onLoadProgress",
handler: (e: WebViewProgressEvent) => {
const progress = e.nativeEvent.progress;

if (progress === 1) {
sendAuthData(webViewRef);
}
},
};
}

return {
event: "onLoad",
handler: () => sendAuthData(webViewRef),
};
};
13 changes: 4 additions & 9 deletions apps/web/api/auth.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,12 @@ import { API_ENDPOINTS } from "@repo/constants";
import { type FieldValues } from "react-hook-form";
import axios from "axios";
import { type SignInResponseType } from "@repo/types/src/responseType";
import { getWebApiUrl } from "@/api/getWebApiUrl";

const baseUrl = getWebApiUrl();

export const postSignIn = async (payload: FieldValues): Promise<SignInResponseType> => {
const { data } = await axios.post<SignInResponseType>(
`${process.env.NEXT_PUBLIC_API_URL}${API_ENDPOINTS.AUTH.SIGN_IN}`,
payload,
{
headers: {
"Content-Type": "application/json",
},
},
);
const { data } = await axios.post<SignInResponseType>(`${baseUrl}${API_ENDPOINTS.AUTH.SIGN_IN}`, payload);

return data;
};
10 changes: 10 additions & 0 deletions apps/web/api/getWebApiUrl.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { detectWebView } from "@/lib/bridge/detectWebView";

export const getWebApiUrl = (): string | undefined => {
const { isAndroid, isIOS } = detectWebView();

if (isAndroid) return process.env.NEXT_PUBLIC_ANDROID_API_URL;
if (isIOS) return process.env.NEXT_PUBLIC_IOS_API_URL;

return process.env.NEXT_PUBLIC_API_URL;
};
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ export default function PanelHeader({ selectedMember, onClose }: PanelHeaderProp

return (
<>
<button onClick={onClose} type="button" className="flex flex-row">
<button onClick={onClose} type="button">
<DoubleChevron />
</button>
<div className={selectedMember ? "flex justify-between" : ""}>
Expand Down
11 changes: 11 additions & 0 deletions apps/web/app/_components/AuthGuard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,23 @@ import { useEffect, useState } from "react";
import { PAGE_NAME } from "@ui/src/utils/constants/pageNames";
import { useRouter } from "next/navigation";
import { useAuthStore } from "@/app/store/useAuthStore";
import { createWebViewEventListener } from "../../lib/bridge/createWebViewEventListener";
import { parseWebViewAuthMessage } from "../../lib/bridge/parseWebViewAuthMessage";
import { useDetectWebView } from "../_hooks/useDetectWebView";
import SignInForm from "./SignInForm";

export default function AuthGuard(): JSX.Element | null {
const router = useRouter();
const [isLoading, setIsLoading] = useState(true);
const { isLoggedIn } = useAuthStore();
const { isIOSWebView, isAndroidWebView } = useDetectWebView();

const webViewEventListener = createWebViewEventListener({ isIOSWebView, isAndroidWebView });

useEffect(() => {
webViewEventListener(parseWebViewAuthMessage);
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);

useEffect(() => {
if (isLoggedIn) {
Expand Down
7 changes: 3 additions & 4 deletions apps/web/app/_hooks/useAppRouter.ts
Original file line number Diff line number Diff line change
@@ -1,24 +1,23 @@
import { useRouter } from "next/navigation";
import { WEBVIEW_MESSAGE_TYPES } from "@repo/constants";
import { sendMessageToNative } from "../utils/sendMessageToNative";
import { sendMessageToWebView } from "@/lib/bridge/sendMessageToWebView";
import { useDetectWebView } from "./useDetectWebView";

interface UseAppRouterResult {
push: (url: string) => void;
}

export const useAppRouter = (): UseAppRouterResult => {
const { isWebView } = useDetectWebView();
const router = useRouter();
const { isWebView } = useDetectWebView();

const push = (url: string): void => {
// web view 실행
if (isWebView) {
sendMessageToNative({
sendMessageToWebView({
type: WEBVIEW_MESSAGE_TYPES.ROUTER_EVENT,
data: url,
});
return;
}

// web 실행
Expand Down
Loading
Loading