๐ [React Native] Android Native-Module Event Listener ๋ง๋ค๊ธฐ
Native ๋ชจ๋์ ๋ง๋๋ ๊ฒ์ ๊ฐ๋จํ์ง๋ง ์ฌ๊ธฐ์ Event Listener๊ฐ ๋ํด์ง๋ค๋ฉด?
์ฐพ์๋ณด์๋ ๋ฑํ ์ฐธ๊ณ ํ ๋งํ ์์ ๋ ์๊ณ , ์ ๋ฌด ์ค๋น์ ๊ณต๋ถ๋ ํ ๊ฒธ ์ ๊ฐ A๋ถํฐ Z๊น์ง ๋ง๋ค์ด๋ณด๊ณ ๊ธฐ๋กํฉ๋๋ค.
๐ ํด๋ ๋ฐ ํ์ผ ์์ฑ
์๋๋ก์ด๋ ๋ค์ดํฐ๋ธ ๋ชจ๋์ ๋ง๋ค๊ธฐ ์ํด์๋ ์๋ฐ ํน์ ์ฝํ๋ฆฐ ํ์ผ์ ๋ค์๊ณผ ๊ฐ์ ๊ฒฝ๋ก๋ก ์์ฑํด์ฃผ์ด์ผ ํฉ๋๋ค.
/android/app/src/main/java/ํจํค์ง๋ช
/

์ ๋ ๋ชจ๋์ ์ด๋ฆ์ Event๋ก ์ง์์ต๋๋ค.
๐ ๋ชจ๋ ํ์ผ ์์ฑ
// EventModule.java
package com.ํจํค์ง๋ช
.event;
import android.os.Handler;
import android.os.Looper;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.bridge.ReactContextBaseJavaModule;
import com.facebook.react.bridge.ReactMethod;
import com.facebook.react.modules.core.DeviceEventManagerModule;
public class EventModule extends ReactContextBaseJavaModule {
ReactApplicationContext reactContext;
private final Handler handler = new Handler(Looper.getMainLooper());
private Runnable runnable;
private boolean isSending = false;
EventModule(ReactApplicationContext context) {
super(context);
this.reactContext = context;
}
@Override
public String getName() {
return "EventModule";
}
private void sendEvent(String eventName, String eventData) {
if (reactContext.hasActiveCatalystInstance()) {
reactContext
.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class)
.emit(eventName, eventData);
}
}
public void startSendingEvents() {
if (isSending) return; // ์ด๋ฏธ ์คํ ์ค์ด๋ฉด ์ค๋ณต ๋ฐฉ์ง
isSending = true;
runnable = new Runnable() {
@Override
public void run() {
sendEvent("MyCustomEvent", "Hello from Native!");
handler.postDelayed(this, 1000); // 1์ด๋ง๋ค ์คํ
}
};
handler.post(runnable);
}
public void stopSendingEvents() {
if (runnable != null) {
handler.removeCallbacks(runnable);
runnable = null;
isSending = false;
}
}
@ReactMethod
public void start() {
startSendingEvents();
}
@ReactMethod
public void stop() {
stopSendingEvents();
}
}
๊ทธ๋ผ ๋ชจ๋์ ๋ง๋ค๊ธฐ ์ํ ์ฃผ์ ๋ด์ฉ๋ค์ ํ๋์ฉ ์ค๋ช ๋๋ฆฌ๊ฒ ์ต๋๋ค.
๐ ํจํค์ง๋ช
package com.ํจํค์ง๋ช
.event;
์ฐ์ ํด๋น ๋ผ์ธ์ ํด๋น ํด๋์ค๊ฐ ์ํ ํจํค์ง๋ฅผ ์ ์ํ๋ ์ ์ธ์ ๋๋ค.
ํด๋์ค๋ฅผ ๋ ผ๋ฆฌ์ ์ผ๋ก ๊ทธ๋ฃนํํ๊ธฐ ์ํ ๋ค์์คํ์ด์ค ์ญํ ์ ํ๋ ํด๋น ํ๋ก์ ํธ์ ํจํค์ง ์ด๋ฆ์ ๋ฃ์ด์ฃผ๊ณ , ์์ค ํ์ผ์ ์ค์ ๋๋ ํฐ๋ฆฌ ๊ตฌ์กฐ์ ์ผ์น์์ผ ์ค๋๋ค.
๐ ReactContextBaseJavaModule
ReactContextBaseJavaModule์ React Native์์ Java๋ก ์์ฑ๋ Native Module์ ๊ตฌํํ๊ธฐ ์ํด ์ ๊ณต๋๋ ๊ธฐ๋ณธ ํด๋์ค์ ๋๋ค. ์ด ํด๋์ค๋ฅผ ์์ํ๋ฉด React Native์ Android ๊ฐ์ ๋ฐ์ดํฐ๋ฅผ ์ฃผ๊ณ ๋ฐ๊ฑฐ๋ JavaScript์์ Native ๊ธฐ๋ฅ์ ํธ์ถํ ์ ์์ต๋๋ค.
import com.facebook.react.bridge.ReactContextBaseJavaModule;
public class EventModule extends ReactContextBaseJavaModule {}
์๋๋ก์ด๋ ๋ค์ดํฐ๋ธ ๋ชจ๋์ ๋ง๋ค๊ธฐ ์ํ ํต์ฌ ํด๋์ค์ด๊ธฐ์ ๋ฐ๋์ extends ํด์ค๋๋ค.
๐ getName ๋ฉ์๋ Override
public class EventModule extends ReactContextBaseJavaModule {
@Override
public String getName() {
return "EventModule";
}
}
getName ๋ฉ์๋๋ฅผ Override ํ๋ ๊ฒ์ ๋ค์ดํฐ๋ธ ๋ชจ๋์ ์ด๋ฆ์ ์ ์ํ๋ ๊ฒ์ ๋๋ค.
์ฆ ๋ค์๊ณผ ๊ฐ์ด ์ฌ๊ธฐ์ return ํ๋ ๋ฌธ์์ด๋ก RN์์ ์ ๊ทผ์ด ๊ฐ๋ฅํ๋ค๋ ์๋ฏธ์ ๋๋ค.
import { NativeModules } from 'react-native';
const { EventModule } = NativeModules;
๐ ReactApplicationContext์ EventListener
import com.facebook.react.bridge.ReactApplicationContext;
public class EventModule extends ReactContextBaseJavaModule {
ReactApplicationContext reactContext;
EventModule(ReactApplicationContext context) {
super(context);
reactContext = context;
}
private void sendEvent(String eventName, String eventData) {
if (reactContext.hasActiveCatalystInstance()) {
reactContext
.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class)
.emit(eventName, eventData);
}
}
}
ReactApplicationContext๋ Android์ Context๋ฅผ ์์๋ฐ์, Activity, Service, BroadcastReceiver ๋ฑ Android ์ปดํฌ๋ํธ์ ์ํธ์์ฉํ ์ ์์ต๋๋ค.
๊ทธ์ ๋ํด JavaScript์ DeviceEventEmitter๋ฅผ ํตํด ๋ค์ดํฐ๋ธ ์ด๋ฒคํธ๋ฅผ ์ ๋ฌํ๋ ๋ฐ ์ฌ์ฉํ ์ ์์ต๋๋ค.
๋ค์ดํฐ๋ธ ์ด๋ฒคํธ๋ฅผ ์ ๋ฌํ๊ธฐ ์ํด ๋ค์๊ณผ ๊ฐ์ ๋จ๊ณ๊ฐ ํ์ํฉ๋๋ค.
- context(reactContext) field ์ ์ธ
- ์์ฑ์๋ก context ์ด๊ธฐํ ํด์ฃผ๊ธฐ
- ์ด๋ฒคํธ ์ ์กํ๋ ํจ์ ๋ง๋ค๊ธฐ
์ด๋ฒคํธ ์ ์ก ๋์์ ์ํ ์ฃผ์ ๋ชจ๋๊ณผ ๋ฉ์๋๋ ๋ค์๊ณผ ๊ฐ์ต๋๋ค.
- getJSModule()
- reactContext.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class)๋ JavaScript ์ด๋ฒคํธ ๋ชจ๋์ธ RCTDeviceEventEmitter์ ์ ๊ทผํฉ๋๋ค.
- ์ด ๋ชจ๋์ React Native์์ ์ด๋ฒคํธ๋ฅผ ์์ ํ๋ ๋ฐ ์ฌ์ฉ๋ฉ๋๋ค.
- emit(eventName, eventData)
- emit ๋ฉ์๋๋ ์ด๋ฒคํธ ์ด๋ฆ(eventName)๊ณผ ๋ฐ์ดํฐ๋ฅผ JavaScript๋ก ์ ๋ฌํฉ๋๋ค.
- JavaScript ์ชฝ์์๋ EventEmitter๋ฅผ ์ฌ์ฉํด ์ด ์ด๋ฒคํธ๋ฅผ ์์ ํ ์ ์์ต๋๋ค.
๐ ๋ค๋ฅธ ํจ์๋ค
์ด์ธ์ ๋ค๋ฅธ ํจ์๋ค์ ์ค์ ๋ฐ์ดํฐ๋ฅผ ์์ ํ๋ ๋ถ๋ถ์ด ์์ด ํ ์คํธ ํ๊ฒฝ์ ์กฐ์ฑํ๊ธฐ ์ํด ๋ง๋ ํจ์๋ค์ ๋๋ค.
import com.facebook.react.bridge.ReactMethod;
public class EventModule extends ReactContextBaseJavaModule {
public void startSendingEvents() {
if (isSending) return; // ์ด๋ฏธ ์คํ ์ค์ด๋ฉด ์ค๋ณต ๋ฐฉ์ง
isSending = true;
runnable = new Runnable() {
@Override
public void run() {
sendEvent("MyCustomEvent", "Hello from Native!");
handler.postDelayed(this, 1000); // 1์ด๋ง๋ค ์คํ
}
};
handler.post(runnable);
}
public void stopSendingEvents() {
if (runnable != null) {
handler.removeCallbacks(runnable);
runnable = null;
isSending = false;
}
}
@ReactMethod
public void start() {
startSendingEvents();
}
@ReactMethod
public void stop() {
stopSendingEvents();
}
}
@ReactMethod ์ด๋
ธํ
์ด์
์ ๋ฉ์๋์ ์ถ๊ฐํด ์ฃผ๋ฉด RN์์ ์ฌ์ฉ๊ฐ๋ฅํ ํจ์๊ฐ ๋๊ณ , handler๋ runnable์ 1์ด๋ง๋ค RN์ ์ด์ฃผ๊ธฐ ์ํด ์ถ๊ฐํ์ต๋๋ค.
๐ ํจํค์ง ํ์ผ ์์ฑ
// EventPackage.java
package com.ํจํค์ง๋ช
.event;
import com.facebook.react.ReactPackage;
import com.facebook.react.bridge.NativeModule;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.uimanager.ViewManager;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
public class EventPackage implements ReactPackage {
@Override
public List<NativeModule> createNativeModules(ReactApplicationContext reactContext) {
List<NativeModule> modules = new ArrayList<>();
modules.add(new EventModule(reactContext));
return modules;
}
@Override
public List<ViewManager> createViewManagers(ReactApplicationContext reactContext) {
return Collections.emptyList();
}
}
๊ทธ๋ผ React Native ๋ค์ดํฐ๋ธ ๋ชจ๋์ ๋ง๋ค์์ผ๋ ํจํค์ง ํ์ผ์ ์ด์ฉํด Android ํ๋ก์ ํธ์ ์ฐ๊ฒฐํด ์ค๋๋ค.
์ฌ๊ธฐ๋ ํน๋ณํ ๊ฒ ์์ผ๋ ๊ฐ๋จํ๊ฒ ์ค๋ช ํ๊ณ ๋์ด๊ฐ๊ฒ ์ต๋๋ค.
ReactPackage๋ฅผ implements ํ ๋ค createNativeModules, createViewManagers ๋ฉ์๋๋ฅผ ๊ตฌํํด ์ฃผ๋ฉด ๋ฉ๋๋ค.
modules.add(new EventModule(reactContext));
๊ทธ๋ฆฌ๊ณ ์ฌ๊ธฐ์๋ ๋ฐ๋์ ๋ด๊ฐ ๋ง๋ ๋ชจ๋์ ๋ฃ์ด์ฃผ๋๋ก ํฉ๋๋ค.
๐ MainApplication์ ๋ชจ๋ ๋ฑ๋ก ์ค์
// MainApplication.kt
package com.ํจํค์ง๋ช
import com.ํจํค์ง๋ช
.event.EventPackage
import ...
class MainApplication : Application(), ReactApplication {
override val reactNativeHost: ReactNativeHost =
object : DefaultReactNativeHost(this) {
override fun getPackages(): List<ReactPackage> =
PackageList(this).packages.apply {
add(EventPackage())
}
// ...
}
// ...
}
๋ฐฉ๊ธ ๋ง๋ ํจํค์ง ํ์ผ์ ๊ฒฝ๋ก์ ๋ง์ถฐ import ํด์ค๋๋ค.
๊ทธ๋ค์ getPackages ๋ฉ์๋ ์์ add ํด์ฃผ๋ฉด ์ ๋ง ๋์ ๋๋ค.
์ ๋ ์ต์ ๋ฒ์ ์ผ๋ก ํ๋ก์ ํธ๋ฅผ ์์ฑํ์ฌ ์ฝํ๋ฆฐ์ด์ง๋ง, ์๋ฐ ํ์ผ์ด์ด๋ ์ฃผ์์ผ๋ก ์์ ๊ฐ ์น์ ํ๊ฒ ๋์์๊ธฐ ๋๋ฌธ์ ๋๊ฐ์ด ๋ฐ๋ผ ํ๊ธฐ๋ง ํ๋ฉด ๋ฉ๋๋ค.
๐ React Native์์ ๋ค์ดํฐ๋ธ ๋ชจ๋ ์ฌ์ฉํ๊ธฐ
// App.tsx
import React from 'react';
import {NativeEventEmitter, NativeModules, View} from 'react-native';
const {EventModule} = NativeModules;
const eventEmitter = new NativeEventEmitter(EventModule);
EventModule?.start();
eventEmitter.addListener('MyCustomEvent', data => console.log(data));
function App(): React.JSX.Element {
return <View></View>;
}
export default App;
๊ฐ๋จํ๊ฒ ์คํ ํ ์คํธ๋ง ํด๋ณด๊ธฐ ์ํด App.tsx์์ ์ ์ญ์ผ๋ก ๋ถ๋ฌ์์ต๋๋ค.
๋ค์ดํฐ๋ธ ๋ชจ๋์ ์ฌ์ฉํ๊ธฐ ์ํ ์ฃผ์ ๊ตฌ์ฑ์์๋ ๋ค์๊ณผ ๊ฐ์ต๋๋ค.
๐ NativeModules
const {EventModule} = NativeModules;
NativeModules๋ React Native์ ๋ค์ดํฐ๋ธ ๋ชจ๋์ ์ ๊ทผํ ์ ์๋๋ก ์ ๊ณต๋๋ ๊ฐ์ฒด์ ๋๋ค.
๋ค์ดํฐ๋ธ ๋ชจ๋์ getNames์์ return ํด์ค ๋ฌธ์์ด๋ก ์ ๊ทผํ๋ฉด ๋ฉ๋๋ค.
๐ NativeEventEmitter
const eventEmitter = new NativeEventEmitter(EventModule);
NativeEventEmitter๋ ๋ค์ดํฐ๋ธ ๋ชจ๋์์ ๋ฐ์ํ๋ ์ด๋ฒคํธ๋ฅผ ๊ฐ์งํ๊ณ , ์ด๋ฅผ React Native์์ ์ฌ์ฉํ ์ ์๋๋ก ํด์ฃผ๋ ํด๋์ค์ ๋๋ค.
EventModule์์ ๋ฐ์ํ๋ ์ด๋ฒคํธ๋ฅผ ์์ ํ๋๋ก ์ค์ ํฉ๋๋ค.
๐ ๊ฒฐ๊ณผ
EventModule?.start();
eventEmitter.addListener('MyCustomEvent', data => console.log(data));
@ReactMethod๋ก ์ด๋ฒคํธ ์์์ ํธ๋ฆฌ๊ฑฐํด์ฃผ์๋ start ํจ์๋ฅผ ๋ถ๋ฌ์จ ๋ค ์ด๋ฒคํธ ๋ฆฌ์ค๋๋ก ์์ ํ ๋ฐ์ดํฐ๋ฅผ ์ฝ์๋ก ์ฐ์ด๋ณธ ๊ฒฐ๊ณผ ์์ฃผ ์ ์๋ํฉ๋๋ค.

์ ์ฒด ํ๊ฒฝ์ ํฌํจํ ์์ ๋ ์ ๊นํ๋ธ์ ์ฌ๋ ค๋์์ผ๋ ํ์ํ์๋ค๋ฉด ์ฐธ๊ณ ๋ฐ๋๋๋ค.
https://github.com/devlasbe/rn_android_native_module_event_listener
GitHub - devlasbe/rn_android_native_module_event_listener: React Native์ Android Native Module๋ก Event Listener๋ฅผ ๋ง๋๋
React Native์ Android Native Module๋ก Event Listener๋ฅผ ๋ง๋๋ ์์ ์ ๋๋ค. - devlasbe/rn_android_native_module_event_listener
github.com
'React-Native' ์นดํ ๊ณ ๋ฆฌ์ ๋ค๋ฅธ ๊ธ
2025๋ React Native ํํฉ๊ณผ CLI vs Expo ๋น๊ต๋ถ์ (2) | 2025.01.15 |
---|---|
[React-Native] ScrollView ์คํฌ๋กค ๋ฒ๋ฒ ์ ์ต์ ํํ๊ธฐ (2) | 2023.10.14 |
[React-Native] BackHandler, ๋ค๋ก ๊ฐ๊ธฐ ์ ์ฑ ์ข ๋ฃ ๋ฌป๊ธฐ (0) | 2023.07.21 |
[React-Native + TypeScript] Stack Navigation, ์คํ ๋ค๋น๊ฒ์ด์ ์ฌ์ฉ๋ฒ (2) | 2023.07.18 |
React Native ์ฅ๋จ์ ๊ณผ CLI, Expo ๋น๊ต์ ํ๊ธฐ (0) | 2023.07.13 |
๋๊ธ