메시지 흐름
SDK 메서드 호출부터 응답 수신까지의 상세 흐름을 설명합니다.
요청-응답 생명주기
SDK 메서드를 호출하면 다음 순서로 처리됩니다.
MESSAGE_KEY 기반 라우팅
모든 메시지는 고유한 MESSAGE_KEY를 가집니다. 브릿지는 요청을 보낼 때 이 키를 포함하고, 응답을 받을 때도 같은 키로 매칭합니다.
// 내부 동작 (직접 사용하지 않음)
Bridge.postMessage({
key: 'CAMERA_SCAN',
value: { mode: 'qr' },
});
응답 메시지에도 동일한 키가 포함됩니다. 브릿지는 키를 기준으로 대기 중인 Promise를 찾아 resolve합니다.
키가 일치하지 않거나 핸들러가 등록되어 있지 않으면 응답이 오지 않으며, 타임아웃이 발생합니다.
요청 큐잉
동일한 MESSAGE_KEY에 대해 동시에 여러 요청이 들어오면, 브릿지는 이를 큐에 넣고 순차적으로 처리합니다. 첫 번째 요청의 응답이 도착한 뒤 두 번째 요청이 전송됩니다.
요청 A (CAMERA_SCAN) 전송
요청 B (CAMERA_SCAN) 큐에 대기
↓
응답 A 수신 → 요청 A Promise resolve
↓
요청 B 전송
↓
응답 B 수신 → 요청 B Promise resolve
이 동작은 자동으로 적용됩니다. 호출 코드에서 별도로 처리할 필요가 없습니다.
직렬화 모드 (serializedPermissions)
권한 요청처럼 사용자 상호작용이 필요한 메서드는 직렬화 모드로 동작합니다. 직렬화 모드에서는 메시지 키와 무관하게 모든 요청이 전역 큐에서 하나씩 처리됩니다.
예를 들어, 카메라 권한과 위치 권한을 동시에 요청하면 다음과 같이 처리됩니다.
// 동시에 호출해도 순차 처리됨
const [cameraPermission, locationPermission] = await Promise.all([
appify.camera.requestPermission(),
appify.location.requestPermission(),
]);
내부적으로는 카메라 권한 요청이 완료된 후 위치 권한 요청이 전송됩니다. 사용자에게 두 개의 권한 팝업이 동시에 표시되지 않습니다.
타임아웃
브릿지는 요청을 보낸 뒤 응답이 오지 않으면 기본 30초 후에 해당 Promise를 TimeoutError로 reject합니다.
try {
const result = await appify.camera.scan();
} catch (error) {
if (error.name === 'TimeoutError') {
// 30초 내에 응답이 없음
console.error('스캔 요청이 시간 초과되었습니다.');
}
}
타임아웃이 자주 발생한다면 다음 원인을 확인합니다.
- 앱이 백그라운드 상태로 전환되어 WebView가 일시 정지됨
- 해당 키에 대한 핸들러가 네이티브에 등록되어 있지 않음
- 핸들러가 응답을 반환하지 않고 종료됨
이벤트 구독
단발성 요청-응답 외에, 지속적으로 이벤트를 수신하는 구독 패턴을 지원합니다.
subscribe: 이벤트가 발생할 때마다 콜백을 실행합니다. unsubscribe()를 호출하기 전까지 계속 수신합니다.
const subscription = appify.app.onAppStateChange((state) => {
console.log('앱 상태 변경:', state); // 'active' | 'background'
});
// 더 이상 수신하지 않을 때
subscription.unsubscribe();
once: 이벤트를 한 번만 수신하고 자동으로 구독을 해제합니다.
appify.camera.onScan.once((result) => {
console.log('스캔 결과:', result.data);
});
구독 이벤트의 예시는 다음과 같습니다.
| 이벤트 | 설명 |
|---|---|
onAppStateChange | 앱이 포그라운드/백그라운드로 전환될 때 |
onScan | QR 코드 또는 바코드 스캔 결과 수신 시 |
onDeepLink | 딥링크로 앱이 열릴 때 |
onPushReceived | 푸시 알림 수신 시 |
컴포넌트가 언마운트될 때 반드시 unsubscribe()를 호출해야 합니다. 호출하지 않으면 메모리 누수가 발생합니다.
초기화 대기
SDK가 초기화되기 전에 메서드를 호출하면, 브릿지는 최대 100ms 동안 초기화 완료를 기다립니다. 100ms 내에 초기화가 완료되면 요청이 정상 처리됩니다. 초기화가 완료되지 않으면 NotInitializedError가 발생합니다.
// 초기화와 메서드 호출이 거의 동시에 실행되는 경우
appify.initialize(); // 비동기 초기화 시작
const info = await appify.device.getInfo(); // 100ms 내에 초기화 완료되면 정상 동작
초기화 완료를 보장하려면 initialize()를 await하고 이후에 다른 메서드를 호출합니다.
await appify.initialize();
const info = await appify.device.getInfo(); // 항상 안전