네이티브 앱과 웹앱 간 양방향 통신 구현
Vue/React/Angular 웹앱
↓ (JavaScript)
WebBridge.request()
↓
Bridge Channel
↓
네이티브 앱
↓
Native 기능
↓
응답 반환
↓
Promise resolve
↓
웹앱 수신
// JavaScript에서 Native로 메시지 전송
const bridge = new WebBridge();
// 위치 요청
const location = await bridge.getLocation();
console.log(location); // { lat: 37.123, lng: 127.456 }
// 1. 요청 ID 생성
const requestId = generateId();
// 2. Promise 생성 및 저장
const promise = new Promise((resolve, reject) => {
pendingRequests[requestId] = { resolve, reject };
});
// 3. Native로 메시지 전송
window.NativeBridge.postMessage(JSON.stringify({
action: 'GET_LOCATION',
payload: { requestId }
}));
// 4. Promise 반환 (응답 대기)
return promise;
async request(action, payload = {}) {
const requestId = this.generateId();
return new Promise((resolve, reject) => {
// 타임아웃 설정
const timeout = setTimeout(() => {
delete this.pendingRequests[requestId];
reject(new Error('Request timeout'));
}, this.timeout);
// 요청 저장
this.pendingRequests[requestId] = {
resolve,
reject,
timeout
};
// Native로 전송
this.sendToNative(action, { ...payload, requestId });
});
}
handleResponse(requestId, status, data) {
const request = this.pendingRequests[requestId];
if (!request) return;
clearTimeout(request.timeout);
delete this.pendingRequests[requestId];
if (status === 'SUCCESS') {
request.resolve(data);
} else {
request.reject(new Error(data.message));
}
}
try {
const location = await bridge.getLocation();
} catch (error) {
if (error.message === 'Request timeout') {
console.log('요청 시간 초과');
}
}
try {
const location = await bridge.getLocation();
} catch (error) {
if (error.message.includes('permission')) {
console.log('위치 권한이 필요합니다');
}
}
<script setup>
import { ref } from 'vue';
import WebBridge from './bridge';
const bridge = new WebBridge();
const location = ref(null);
const error = ref(null);
async function getLocation() {
try {
location.value = await bridge.getLocation();
} catch (err) {
error.value = err.message;
}
}
</script>
import { useState } from 'react';
import WebBridge from './bridge';
const bridge = new WebBridge();
function App() {
const [location, setLocation] = useState(null);
const getLocation = async () => {
try {
const loc = await bridge.getLocation();
setLocation(loc);
} catch (error) {
console.error(error);
}
};
return <button onClick={getLocation}>위치</button>;
}
{
"action": "GET_LOCATION",
"payload": {
"requestId": "req_1234567890",
// 기타 파라미터
}
}
{
"requestId": "req_1234567890",
"status": "SUCCESS",
"data": {
"lat": 37.123,
"lng": 127.456,
"accuracy": 10
}
}
앱 설정에서 네이티브 연동과 파일 저장 권한을 활성화하면, 별도의 코드 없이 파일 링크 클릭 시 자동으로 다운로드가 실행됩니다.
<a download="file.pdf" href="..."> 속성이 있는 링크CDN 방식:
<script src="web-bridge.js"></script>
<script>
const bridge = new WebBridge();
// 간단한 호출
bridge.downloadFile('https://example.com/file.pdf', 'document.pdf');
// 옵션 객체
bridge.downloadFile({
url: 'https://example.com/file.pdf',
filename: 'document.pdf',
mode: 'save' // 'save' | 'share' | 'open'
});
// 진행률 콜백
bridge.downloadFile('https://.../file.zip', null, (progress) => {
console.log('진행률:', progress + '%');
});
</script>
ESM / Vue / React:
import { bridge } from '@/utils/bridge';
// 다운로드 실행
const result = await bridge.downloadFile('https://example.com/file.pdf');
console.log(result.path); // 저장된 로컬 경로
console.log(result.success); // true
// 옵션 객체
await bridge.downloadFile({
url: 'https://example.com/file.pdf',
filename: 'document.pdf',
mode: 'save' // 'save' | 'share' | 'open'
});
mode 옵션: save (기기 저장) | share (공유 다이얼로그) | open (외부 앱 열기)