최근 프로젝트에서 웹뷰 안의 React 페이지에서 AppsFlyer URL을 앱으로부터 전달받아야 하는 요구사항이 있었습니다.
이걸 해결하기 위해 사용한 기술이 바로 '브릿지(Bridge)'였는데요.
처음 접했을 땐 막막했지만, 원리를 이해하고 나니 생각보다 단순했습니다.
오늘은 그 과정을 정리하며 브릿지가 무엇이고, 왜 필요한지, 어떻게 사용하는지 공유해보려 합니다.
브릿지란 무엇일까요?
브릿지(Bridge)는 말 그대로 두 환경을 연결해주는 다리 역할을 합니다.
앱(Native)과 웹(Web)은 기본적으로 서로 다른 세계에서 작동하기 때문에 직접적으로 통신하기 어렵습니다.
하지만 브릿지를 활용하면 웹에서 앱의 기능을 호출하거나, 반대로 앱에서 웹으로 데이터를 넘겨주는 게 가능해집니다.
왜 브릿지가 필요했을까요?
이번 프로젝트에서는 클라이언트 앱에서 만들어둔 AppsFlyer URL을 웹뷰 안에서 사용해야 했어요.
그런데 이 URL은 앱에서만 접근 가능한 값이기 때문에, 웹 단에서는 직접 가져올 수 없는 구조였습니다.
결국 앱에서 이 URL을 웹으로 전달해주는 통로가 필요했고, 그 역할을 브릿지가 해주게 된 거죠.
어떻게 구현했을까요?
React 환경에서는 웹뷰를 ReactNativeWebView 같은 컴포넌트로 띄우는 방식으로 사용합니다.
이때 앱에서 웹으로 데이터를 전달하려면 보통 postMessage를 사용하는데요.
1. 앱(Native) → 웹 : postMessage로 데이터 보내기
앱(Native) 쪽에서는 아래와 같이 메시지를 전달합니다.
(React Native 기준 예시입니다.)
// Native(Android/iOS) 앱 코드
webViewRef.postMessage(JSON.stringify({
appsFlyerUrl: 'https://example.onelink.me/abc/123456',
}));
2. 웹(React) → 메시지 수신하기
웹뷰 안의 React 페이지에서는 message 이벤트를 수신하여 데이터를 처리할 수 있습니다.
useEffect(() => {
const handleMessage = (event: MessageEvent) => {
try {
const data = JSON.parse(event.data);
if (data.appsFlyerUrl) {
console.log('앱에서 받은 URL:', data.appsFlyerUrl);
// 이후 필요한 로직 처리
}
} catch (err) {
console.error('메시지 파싱 오류:', err);
}
};
window.addEventListener('message', handleMessage);
return () => {
window.removeEventListener('message', handleMessage);
};
}, []);
3. 웹 → 앱 : 준비 완료 메시지 보내기 (선택적)
앱이 너무 빠르게 메시지를 보내면 웹이 아직 준비되지 않아 놓치는 경우가 생길 수 있어요.
이럴 때는 웹이 먼저 "준비 완료" 메시지를 앱에 보내고,
앱은 그 이후에 데이터를 전달하도록 구현하면 안정적입니다.
useEffect(() => {
// 웹 페이지 로딩 완료 후 앱에 신호 보내기
if (window.ReactNativeWebView?.postMessage) {
window.ReactNativeWebView.postMessage(JSON.stringify({ ready: true }));
}
}, []);
이 코드는 웹뷰 페이지가 마운트될 때 한 번 실행되며, 앱에 "ready": true 메시지를 보냅니다.
앱은 이 메시지를 수신한 뒤에만 데이터를 보내도록 처리하면,
웹 쪽에서 메시지를 놓치는 경우를 방지할 수 있어요.
전체 코드 예시:
import { useEffect } from 'react';
function WebViewPage() {
useEffect(() => {
// 1. 앱에게 준비 완료 메시지 보내기
if (window.ReactNativeWebView?.postMessage) {
window.ReactNativeWebView.postMessage(JSON.stringify({ ready: true }));
}
// 2. 앱에서 메시지 받기
const handleMessage = (event: MessageEvent) => {
try {
const data = JSON.parse(event.data);
if (data.appsFlyerUrl) {
console.log('앱에서 받은 URL:', data.appsFlyerUrl);
// 필요한 로직 처리
}
} catch (err) {
console.error('메시지 파싱 오류:', err);
}
};
window.addEventListener('message', handleMessage);
return () => {
window.removeEventListener('message', handleMessage);
};
}, []);
return (
<div>
<h1>웹뷰 페이지입니다</h1>
</div>
);
}
주의할 점은 없을까요?
브릿지를 사용할 때는 몇 가지 신경 써야 할 부분도 있습니다.
- 앱과 웹 간에 주고받는 데이터 포맷은 JSON으로 명확히 통일해두는 게 좋습니다.
- 보안상으로는 이벤트의 출처(origin) 를 검증하는 것도 고려해야 합니다.
- 앞서 언급한 것처럼, 메시지 타이밍 이슈로 인해 앱의 데이터가 웹에서 누락되지 않도록 처리하는 것도 중요합니다.
마무리하며
이번 경험을 통해 앱과 웹 간의 통신 구조를 실제로 구현해보면서 브릿지에 대해 많이 배우게 됐습니다.
막연하게 어렵게 느껴졌던 부분이었지만, 한 번 구조를 이해하고 나니 생각보다 단순한 방식으로 잘 연결되더라고요.
혹시 저처럼 웹뷰 기반 앱에서 Native 기능과 통신해야 하는 상황이 있다면,
브릿지를 적극적으로 활용해보는 걸 추천드립니다.
이번 경험을 통해 앱과 웹 간의 통신 구조를 실제로 구현해보면서 브릿지에 대해 많이 배우게 됐습니다.
막연하게 어렵게 느껴졌던 부분이었지만, 한 번 구조를 이해하고 나니 생각보다 단순한 방식으로 잘 연결되더라고요.
혹시 저처럼 웹뷰 기반 앱에서 Native 기능과 통신해야 하는 상황이 있다면, 브릿지를 적극적으로 활용해보는 걸 추천드립니다.
