본문으로 건너뛰기

메시지 흐름

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앱이 포그라운드/백그라운드로 전환될 때
onScanQR 코드 또는 바코드 스캔 결과 수신 시
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(); // 항상 안전