이전 게시글에서 React Webview와 ReactNative의 통신을 시켜보았다.
이때의 방식은 WebView가 onLoad되었을때, React Webview에서 필요한 토큰을 주입하게 하는 형태였다.
하지만, 프로젝트가 커지고, 다양한 API요청이 많아짐에 따라 우리팀은 자연스럽게 비용문제가 발생할 수 있을거라는 걱정을 하게되었고, 이에 서버 API호출을 줄이고자 데이터를 받아오는 부분에서 네이티브 기기의 DB를 사용하기로 하였다.
문제상황
기기 DB에 저장된 데이터를 받아와서, 웹뷰에서 보여줘야했다.
이때의 DB테이블은 우리팀원이 이미 만들어 둔 상태였기에, 나는 이것을 조회해서 웹뷰에 넣어만 주면 됐다.
그런데 DB조회는 비동기적으로 일어난다. Promise를 resolve시켜야 실제 데이터를 얻어올 수 있다.
그러나, 컴포넌트 함수 실행은 동기적으로 일어난다.
어떻게 해야 이 데이터를 효과적으로 웹뷰에 전달 할 수 있을까? 를 고민했다.
CustomWebView
우리 서비스는 여러개의 웹뷰 화면을 보여주기때문에 CustomWebView를 구성해주었었다.
따라서 여기에 data를 주입시킬 수 있어야했다.
export default function CustomWebView({uri}: {uri: string}) {
const webViewRef = useRef<WebView>(null);
const onLoad = async () => {
const accessToken = await storageGetValue('accessToken'));
const refreshToken = await storageGetValue('refreshToken');
if (webViewRef.current) {
webViewRef.current.postMessage(
JSON.stringify({accessToken, refreshToken}),
);
}
};
...
return <WebView
uri={{uri}}
ref={webViewRef as any}
onLoad={onLoad}
/>
}
현상 파악하기
원하는 것을 정리해보자.
- DB데이터를 비동기적으로 조회한다.
- 웹뷰 컴포넌트가 동기적으로 실행된다.
- onLoad될때 데이터를 주입시킨다. 이때, 데이터는 Promise면 안된다.
그러면 WebView로 전달할때가 아니라 onLoad시에 Promise를 resolve시켜주면된다!
CustomWebView props추가하기
일단 주입받을 데이터를 props로 받아주었다.
이때의 데이터는 그냥 객체가 아니라 Promise형태의 객체를 받게하여, onLoad시에 Promise를 평가하도록 만들었다.
interface CustomWebViewProps {
uri: string;
additionalData?: AdditionalData;
}
export default function CustomWebView({
uri,
additionalData,
}: CustomWebViewProps) {
...
}
따라서 additionalData의 데이터 타입을 이렇게 설계해주었다.
interface AdditionalData {
[key: string]: Promise<unknown>;
}
그리고 onLoad에서 해당 Promise를 resolve시킨 후에, webViewRef.current.postMessage로 WebView에 주입시켜주었다.
const onLoad = async () => {
setLoading(false);
const accessToken = (await storageGetValue('accessToken')) || '';
const refreshToken = await storageGetValue('refreshToken');
if (additionalData)
await Promise.all(
Object.entries(additionalData).map( //객체 value를 resolve시키고, value를 resolve된 값으로 변경한다.
async ([key, value]: [any, Promise<any>]) => {
additionalData[key] = await value;
},
),
);
if (webViewRef.current) {
webViewRef.current.postMessage(
JSON.stringify({accessToken, refreshToken, ...additionalData}),
);
}
};
사용하기
이제 화면단에서 웹뷰를 열때 아래와 같이 다양한 데이터를 객체로 묶어서 주입해줄 수 있다.
const Page = () => {
const data = {
schools: getSchools(), //Promise를 반환하는 비동기 DB조회 함수 호출
};
return (
<CustomWebView
uri="http://uri..."
additionalData={data}
/>
);
};
React에서 받기
아래와 같이 Provider를 통해 데이터를 받도록 처리했다.
import { ReactNode, useEffect, useState } from 'react';
import { School, SchoolInfo } from '@/hooks/useSchoolData';
export default function SchoolProvider({ children }: { children: ReactNode }) {
const [schoolData, setSchoolData] = useState<SchoolInfo[]>([]);
const schoolDataHandler = (event: MessageEvent) => {
const { schools } = JSON.parse(event.data);
setSchoolData(schools);
};
useEffect(() => {
document.addEventListener('message', schoolDataHandler as EventListener);
window.addEventListener('message', schoolDataHandler);
}, []);
return <School.Provider value={schoolData}>{children}</School.Provider>;
}