
Security News
Lovable’s OJ Rewrites Vite’s Dev Server in Rust as AI Lowers the Cost of Forking Open Source
Lovable’s OJ rewrites Vite’s dev server in Rust, reducing memory use and preview times as AI lowers the cost of open source reimplementation.
rnww-plugin-background
Advanced tools
React Native WebView 백그라운드 실행 플러그인 (Expo Native Module)
앱 라이프사이클 이벤트 발생 시 메인 WebView에서 JavaScript 콜백을 실행합니다. appBridge 등 앱의 전역 객체에 접근할 수 있습니다.
npm install rnww-plugin-background
import { registerBackgroundHandlers } from 'rnww-plugin-background';
registerBackgroundHandlers({
bridge: yourBridgeImplementation,
platform: { OS: Platform.OS },
});
// 외부 변수 사용 가능
const API_URL = 'https://api.example.com';
// 태스크 등록
await bridge.call('registerTask', {
taskId: 'my-task',
triggers: ['app_foreground', 'app_background'],
callback: (trigger, data) => {
// 메인 WebView 컨텍스트에서 실행됨
// appBridge, 전역 변수 등 모두 접근 가능!
fetch(API_URL + '/sync', {
method: 'POST',
body: JSON.stringify({ event: trigger })
});
appBridge.call('api:sync', { event: trigger });
},
notification: {
title: '백그라운드 실행 중',
body: '서비스가 실행 중입니다'
}
});
// 태스크 시작
await bridge.call('startTask', { taskId: 'my-task' });
이 플러그인은 Headless WebView를 사용하여 앱 종료 후에도 JavaScript 콜백을 실행합니다.
init, terminate): 항상 자동 호출됨app_foreground, app_background): triggers 배열에 포함 시에만 호출bridge.call('registerTask', {
taskId: 'my-task',
triggers: ['app_foreground', 'app_background'],
callback: (trigger, data) => {
switch (trigger) {
case 'init':
console.log('Task initialized');
break;
case 'terminate':
console.log('Task terminating');
break;
case 'app_background':
saveCurrentState();
break;
case 'app_foreground':
syncData();
break;
}
},
notification: {
title: '백그라운드 실행 중',
body: '서비스가 실행 중입니다'
}
});
백그라운드 작업을 등록합니다.
const result = await bridge.call('registerTask', {
taskId: string; // 필수: 작업 고유 ID
triggers?: TriggerType[]; // 라이프사이클 트리거 ['app_foreground', 'app_background']
callback?: BackgroundCallback; // WebView에서 실행될 콜백
notification?: NotificationConfig; // Android 필수
});
// 결과
// { success: true, taskId: 'my-task' }
// { success: false, error: 'TASK_ALREADY_EXISTS' }
등록된 작업을 해제합니다.
const result = await bridge.call('unregisterTask', { taskId: 'my-task' });
작업을 시작합니다. WebView가 자동으로 감지되어 연결됩니다.
const result = await bridge.call('startTask', { taskId: 'my-task' });
// { success: true, taskId: 'my-task' }
// { success: false, error: 'NOTIFICATION_PERMISSION_DENIED' }
// { success: false, error: 'WEBVIEW_NOT_FOUND' }
작업을 중지합니다.
const result = await bridge.call('stopTask', { taskId: 'my-task' });
모든 작업을 중지합니다.
const result = await bridge.call('stopAllTasks');
실행 중인 알림 내용을 업데이트합니다. (Android only)
const result = await bridge.call('updateNotification', {
taskId: 'my-task',
title: '동기화 중',
body: '작업 진행 중...',
silent: true
});
작업 상태를 조회합니다.
const result = await bridge.call('getTaskStatus', { taskId: 'my-task' });
const allResult = await bridge.call('getAllTasksStatus');
// 백그라운드 권한 확인/요청
await bridge.call('checkBackgroundPermission');
await bridge.call('requestBackgroundPermission');
// 알림 권한 확인/요청 (Android 13+)
await bridge.call('checkNotificationPermission');
await bridge.call('requestNotificationPermission');
// 저장
await bridge.call('setTaskData', {
taskId: 'my-task',
data: { lastSync: Date.now() }
});
// 조회
const result = await bridge.call('getTaskData', { taskId: 'my-task' });
// 삭제
await bridge.call('removeTaskData', { taskId: 'my-task' });
메인 WebView에서 JavaScript를 직접 실행합니다.
const result = await bridge.call('evaluateInMainWebView', {
script: 'window.myApp.getState()'
});
| 트리거 | 호출 시점 | 설명 |
|---|---|---|
init | 태스크 시작 직후 | 초기화 로직 실행 |
terminate | 태스크 종료 직전 | 상태 저장, 리소스 정리 |
triggers 배열에 명시적으로 포함해야 호출됩니다.
| 트리거 | 호출 시점 | 설명 |
|---|---|---|
app_foreground | 앱이 포그라운드로 전환 | 데이터 동기화, UI 갱신 |
app_background | 앱이 백그라운드로 전환 | 상태 저장, 네트워크 요청 |
메인 WebView에서 실행됩니다. 백그라운드에서도 동작합니다. 외부 변수와 앱 전역 객체에 접근할 수 있습니다.
const API_URL = 'https://api.example.com';
callback: (trigger, data) => {
switch (trigger) {
case 'init':
// 초기화
window.__taskStartedAt = Date.now();
break;
case 'terminate':
// 종료 직전 정리
fetch(API_URL + '/final', {
method: 'POST',
body: JSON.stringify({ duration: Date.now() - window.__taskStartedAt })
});
break;
case 'app_background':
// 앱이 백그라운드로 전환
appBridge.call('saveState', { timestamp: data.timestamp });
break;
case 'app_foreground':
// 앱이 포그라운드로 복귀
appBridge.call('syncData');
break;
}
}
interface NotificationConfig {
title: string;
body: string;
icon?: string; // drawable 리소스명
color?: string; // '#4CAF50'
priority?: 'min' | 'low' | 'default' | 'high' | 'max';
ongoing?: boolean; // 기본값: true
silent?: boolean; // 기본값: false
channelId?: string;
channelName?: string;
channelDescription?: string;
}
type BackgroundError =
| 'TASK_NOT_FOUND'
| 'TASK_ALREADY_EXISTS'
| 'TASK_ALREADY_RUNNING'
| 'NOTIFICATION_PERMISSION_DENIED'
| 'WEBVIEW_NOT_FOUND'
| 'INVALID_INPUT'
| 'UNKNOWN';
AndroidManifest.xml에 자동 추가:
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_DATA_SYNC" />
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
<uses-permission android:name="android.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS" />
Android 13+ (API 33): POST_NOTIFICATIONS 런타임 권한 필수
Info.plist에 추가:
<key>UIBackgroundModes</key>
<array>
<string>fetch</string>
<string>processing</string>
</array>
<key>BGTaskSchedulerPermittedIdentifiers</key>
<array>
<string>expo.modules.custombackground.refresh</string>
</array>
┌─────────────────────────────────────────────────────────────┐
│ 앱 실행 중 │
│ │
│ Main WebView ←→ Bridge ←→ React Native │
│ ↓ │
│ BackgroundService │
│ ↓ │
│ Headless WebView (독립) │
└─────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────┐
│ 앱 종료 후 │
│ │
│ BackgroundService (Foreground Service로 유지) │
│ ↓ │
│ Headless WebView (독립적으로 JavaScript 실행) │
│ ↓ │
│ backgr:* 핸들러 → Native에서 직접 처리 │
└─────────────────────────────────────────────────────────────┘
import {
setBridgeScript,
isHeadlessActive,
sendToHeadless,
connectHeadlessToBridge
} from 'rnww-plugin-background';
// 1. Bridge 스크립트 설정 (앱 초기화 시)
await setBridgeScript(bridgeClientScript);
// 2. Headless WebView 상태 확인
const active = await isHeadlessActive();
// 3. Headless WebView로 메시지 전송
await sendToHeadless('customAction', { data: 'value' });
// 4. RN Template BridgeExtension 연동
connectHeadlessToBridge(BridgeExtension);
Headless WebView에서 postMessage로 전송한 메시지는 다음과 같이 처리됩니다:
| 핸들러 패턴 | 처리 방식 | 설명 |
|---|---|---|
backgr:* | Native 직접 처리 | RN 없이도 동작 (앱 종료 후) |
| 기타 | React Native 전달 | onHeadlessMessage 이벤트 |
여러 WebView가 있는 경우 특정 WebView를 지정할 수 있습니다:
// React Native 컴포넌트
<WebView
nativeID="123"
source={{ uri: 'https://...' }}
/>
// 특정 WebView 연결
await bridge.call('attachWebView', { viewTag: 123 });
// 연결 해제
await bridge.call('detachWebView');
MIT
FAQs
React Native WebView Background Execution Plugin with Expo support
The npm package rnww-plugin-background receives a total of 1 weekly downloads. As such, rnww-plugin-background popularity was classified as not popular.
We found that rnww-plugin-background demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 1 open source maintainer collaborating on the project.

Security News
Lovable’s OJ rewrites Vite’s dev server in Rust, reducing memory use and preview times as AI lowers the cost of open source reimplementation.

Security News
It has been one year since Shai-Hulud made its first appearance on npm.

Research
/Security News
Operators behind PolinRider used a compromised GitHub account to plant malware in four development versions of a Packagist package with 700,000+ downloads.