네이티브 모듈로 애디스쿱 sdk붙이는 연습을 해보았다
참고로 java를 모르는 사람이기에 생성자를 만들어야하는 것도 코드 짜면서 알게 되어서 중간중간 내용이 뜬금없을 수 있다.. ^-^
android/app/src/main/java/com/…/패키지파일명
여기서 모듈파일과 패키지 파일을 생성한다(java)
모듈파일
초기화
미디어 id와 시크릿 key를 등록하고, 광고 실행 시키기 전 준비 작업
AdiscopeSdk.initialize(Activity activity, AdiscopeInitializeListener listener)
// activity와 listener라는 2개의 매개변수를 받음
- Activity
- 현재 앱의 화면을 나타내는 객체 → sdk는 화면 전환이나 ui를 제어할 필요가 있으므로 현재 화면이 뭔지 알아야하기때문
- ReactApplicationContext의 getCurrentActivity()로 현재 화면 즉, activity를 얻을 수 있다
- Listener
- 리스너는 이벤트 발생 후 호출되는 콜백함수이다. 여기서는 애디스콥 초기화(=이벤트) 결과에 따라 다르게 실행시킬 수 있다
ReactApplicationContext reactContext; AdiscopeSdk.initialize(reactContext.getCurrentActivity(), new AdiscopeInitializeListener() { @Override public void onInitialized(boolean isSuccess) { if (isSuccess) { // 광고 인스턴스 생성 및 광고 이벤트 리스너 연결 mRewardedVideoAd = AdiscopeSdk.getRewardedVideoAdInstance(reactContext.getCurrentActivity()); mRewardedVideoAd.setRewardedVideoAdListener(mRewardedVideoAdListener()); } else { // Init 실패 에 대한 처리 Code } } }- ReactApplicationContext는 앱 전체에 대한 정보를 가지고 있는 객체로 Native Module에서 js와의 상호작용을 위해 사용하는 메서드들을 가지고 있다(sendEvent 등)
- onInitialized의 매개변수 isSuccess는 초기화 리스너의 콜백함수 매개변수로 초기화가 실행되면 내부 로직에 따라 알아서 초기화 되고 결과를 isSuccess로 넘겨준다
자동 import 안되는 에러
- 원래 공식문서 코드 그대로 붙이면 안드로이드 스튜디오에서 import 안된 건 빨갛게 표시되면서 자동으로 import 할수있게 되어있다
근데 import된 게 없어도 빨갛게 표시가 안됨
이상하다 싶었지만 그냥 npm run android 돌려버림
Cannot resolve symbol 'RewardedVideoAd'
create class RewardedVideoAd
sdk를 gradle에 추가하고 Gradle Sync를 해야하는데 안했다고함
→ 안드로이드 스튜디오 메뉴 File > Sync Project with Gradle Files 클릭
🚫 The project is using an incompatible version (AGP 8.8.2) of the Android Gradle plugin. Latest supported version is AGP 8.1.3
했더니 이렇게 뜬다
→ 버전 차이로 안드로이드 스튜디오를 업데이트 하기
https://dev-ej2.tistory.com/67
안드로이드 스튜디오 버전 업데이트 방법 (+기린 giraffe 새로운 UI 적용)
안드로이드 스튜디오 버전이 전기뱀장어였는데, 기린으로 업데이트를 해보았다. 1. Check for Updates... 를 클릭하여 업데이트 할 버전이 있는지 확인한다. 2. 홈페이지에서 최신 버전을 다운받는다.
dev-ej2.tistory.com
나는 M2 Apple 칩 탑재 Mac이라 android-studio-2024.3.2.14-mac_arm.dmg 로 다운로드
👉 최신버전의 안드로이드 스튜디오에서는 잘 뜬다

패키지 파일
public class AdiscopePackage implements ReactPackage {
@Override
public List<NativeModule> createNativeModules(ReactApplicationContext reactContext) {
List<NativeModule> modules = new ArrayList<>();
modules.add(new AdiscopeModule(reactContext));
return modules;
}
@Override
public List<ViewManager> createViewManagers(ReactApplicationContext reactContext) {
return Collections.emptyList();
}
}
에러발생
modules.add(new AdiscopeModule(reactContext));
이 부분에서 에러
→ 알고보니 모듈 파일의
public class adiscopeModule extends ReactContextBaseJavaModule {
이렇게 클래스명이 소문자로 되어있었다 ^^; 그래서 대문자로 이름 변경
modules.add(new AdiscopeModule(reactContext));
이번엔 reactContext에서 에러발생
🚫 Expected no arguments but found 1
→ 생성자를 만들어 줘야 했는데 안 만들었다 ^^;
public AdiscopeModule(ReactApplicationContext context) {
super(context); // 부모 클래스인 ReactContextBaseJavaModule에 context 전달
reactContext = context; // 현재 context를 필드에 저장
}
생성자를 만들었더니 js에서 RNAdiscope를 로그에 찍으면 출력이 된다.
하지만 초기화 함수는 실행되지 않는데..
초기화 실패
useEffect(() => {
RNAdiscope.initializeAdiscope();
}, []);
- 앱 실행 시 초기화시키도록 했으나 initializeAdiscope를 못찾음..

@Override
public String getName() {
return "RNAdiscope";
}
- 기본사항인 getName을 맞추지 않았다^^;
- initializeAdiscope는 실행되지만 초기화가 실패함

- 직접 mediaId, mediaSecret 넣는걸로 변경 → 안됨
- 패키지명 변경 : 실제 프로젝트에서 사용하는 패키지명으로 변경하기
- https://velog.io/@ddd92/Android-Studio-패키지-이름-바꾸기
[Android Studio] 패키지 이름 바꾸기
아래 보여지는 디렉터리 구조를 펼쳐서 보기위해 설정을 먼저 변경해줍니다.1\. 변경할 폴더 명에서 우클릭 - Refactor - Rename을 클릭합니다.2\. All Directories를 누릅니다. 3\. 변경할 이름을 입력 후 Re
velog.io
- npm run android 했더니 또 새로운 에러

🚫 INSTALL_FAILED_VERSION_DOWNGRADE: Downgrade detected: Update version code 1 is older than current 126
- 에뮬레이터에서 앱을 삭제해준다 👉 adb uninstall 패키지이름

→ 성공!
set user id
유저 아이디를 애디스콥에 전달해주는 작업
@ReactMethod
public void setUserId(String userId) {
AdiscopeSdk.setUserId(userId);
}
- @ReactMethod → 아래의 함수는 리액트에서 사용할 수 있는거라고 선언하는 것
- js에서 네이티브 코드 사용할 수 있도록
→ 초기화는 앱 실행 시, 유저 아이디 저장은 앱 실행 시와 id가 변경됐을 때 수행된다
여기까지는 광고 사전 작업!
실제 광고 연결 작업
일단 리스너를 설정해줘야 한다. 공식문서에 나와있는 onRewardedVideoAdLoaded, onRewarded 이런 이벤트 콜백함수들은 위에 인스턴스 생성할 때 넣은 mRewardedVideoAdListener() 이 리스너 안에 정의해준다.
public RewardedVideoAdListener mRewardedVideoAdListener() {
return new RewardedVideoAdListener() {
@Override
public void onRewardedVideoAdLoaded(String unitId) {
// 광고 가져오기 성공 시
}
// ...
@Override
public void onRewarded(String unitId, RewardItem rewardItem) {
// 사용자가 광고를 끝까지 시청하여 보상을 받을 자격이 생겼을 때
}
}
}
1. Load
광고를 보여주기 전, 보여줄 광고들을 가져온다. load가 성공하면 리스너의 onRewardedVideoAdLoaded, 로드 실패 시 onRewardedVideoAdFailedToLoad 콜백 함수가 호출된다
// 광고 다운로드(load) 시작
@ReactMethod
public void loadRewardedAd(String unitId) {
if (mRewardedVideoAd != null) {
mRewardedVideoAd.load(unitId);
Log.e(TAG, "보상형 광고 로드 완료");
} else {
Log.e(TAG, "보상형 광고 로드 실패");
}
}
- unitId는 정해진 아이디가 있다! 이것도 내맘대로 id했다가 시간 엄청 보냄..
2. Show
들고온 광고들을 보여 준다. mRewardedVideoAd.show() 는 리스너의 onRewardedVideoAdLoaded에 넣는다. 성공 시 onRewardedVideoAdOpened, onRewarded 실패 시 onRewardedVideoAdFailedToShow 이벤트 콜백 호출.
@Override
public void onRewardedVideoAdLoaded(String unitId) {
// 광고가 로드 성공 시
mRewardedVideoAd.show();
Log.d(TAG, "aos : 보상형 광고 로드 성공 -> show 까지");
}
3. 리스너에 있는 콜백함수를 js에서 사용하고 싶을 때
네이티브에서 정의한 로드 실패 시 호출, 광고 종료 시 호출하는 것을 리액트에서도 알아차리고 뭔가 작업하고 싶은 뭔가가 있을 때는 sendEvent를 사용한다
private void sendEvent(String eventName, @Nullable WritableMap params) {
reactContext
.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class)
.emit(eventName, params);
}
@Override
public void onRewarded(String unitId, RewardItem rewardItem) {
// 사용자가 광고를 끝까지 시청하여 보상을 받을 자격이 생겼을 때
// RewardItem.getType: 보상 타입
// RewardItem.getAmount: 보상 양
WritableMap rewardData = Arguments.createMap();
rewardData.putString("type", rewardItem.getType());
rewardData.putInt("amount", (int) rewardItem.getAmount());
sendEvent("onRewarded"); //이렇게 정의된 이벤트명을 js에서 사용할 수 있다
Log.d(TAG, "보상받기 완료");
}
const onRewardedListener = adiscopeEmitter.addListener('onRewarded', () => {
Alert.alert('보상 완료', '리워드 보상이 완료되었습니다.');
onRewardedListener.remove();
});
//onRewarded가 호출될 때 리워드 보상 alert창이 뜨도록
js에서 네이티브 코드의 실행결과를 알 필요가 없으면? 안해줘도 된다
onRewarded에서 타입과 보상 js로 보내기
WritableMap rewardData = Arguments.createMap();
rewardData.putString("type", rewardItem.getType());
rewardData.putInt("amount", (int) rewardItem.getAmount());
- js로 데이터를 보내기 위해 Map 객체를 만든다 → rewardData
- rewardItem.getType()에서 타입을, rewardItem.getAmount()에서 보상 양(숫자)을 가져와 저장한다
- sendEvent에 같이 넣어준다
sendEvent("onRewarded", rewardData); //이렇게 정의된 이벤트명을 js에서 사용할 수 있다
- js에서는 아래와 같이 타입과 개수를 사용할 수 있다
const onRewardedListener = adiscopeEmitter.addListener('onRewarded', (data) => {
Alert.alert(
'보상 완료',
data.type + '타입, ' + data.amount + '개 보상 지급',
);
onRewardedListener.remove();
});
- 종종 안될때는 gradlew을 삭제시켜주기
cd android
./gradlew clean
cd ..
npm install
자바도 그렇고 안드로이드도 익숙하지 않아서 애먹었지만 리액트 네이티브 모듈이 어떻게 작동하고, sdk 연결해서 사용하는 것의 구조를 파악할 수 있었다. 앞으로 자바도 안드로이드도 점차 공부해나가야지.
'React-Native' 카테고리의 다른 글
| 이미지 항목 스크롤 시 버벅거림 수정(ScrollView FlatList 중첩, react-native-fast-image) (0) | 2025.10.10 |
|---|---|
| [에러해결] 푸시 알람으로 페이지 진입 후 댓글 작성 시 반영안됨 (queryClient.invalidateQueries 사용) (2) | 2025.08.25 |
| npm run ios 에러 해결기 2 (7) | 2025.08.13 |
| [React-Native] 네이버 간편로그인(react-native-naver-login 라이브러리 사용) (0) | 2024.06.06 |
| [React-Native/Xcode] npm run ios 에러 해결의 여정(pod install이 9할) (1) | 2024.06.05 |