마이그레이션 가이드
react-native-device-info에서 react-native-nitro-device-info로 전환하는 방법입니다.
개요
react-native-nitro-device-info는 react-native-device-info(RNDI) 15.x와 같은 함수 이름, 시그니처, 기본 DeviceInfo 객체, 훅을 제공하는 호환 계층이 있습니다. react-native-device-info 대신 react-native-nitro-device-info/compat에서 import하면 호출 코드는 유지할 수 있습니다.
어디까지 바꿀지에 따라 호환 API와 네이티브 API 중 하나를 선택하세요.
호환 API부터 사용하세요. 필요하면 나중에 개별 호출을 네이티브 API로 옮길 수 있습니다. 두 API를 함께 사용할 수 있습니다.
호환 API로 마이그레이션하기(권장)
1. 설치
# Install the new library + its peer dependency
npm install react-native-nitro-device-info react-native-nitro-modules
# (or: yarn add react-native-nitro-device-info react-native-nitro-modules)
# iOS
cd ios && pod install && cd ..
2. codemod 실행
라이브러리에 포함된 codemod는 모든 react-native-device-info import를 react-native-nitro-device-info/compat으로 바꿉니다. import 경로만 수정하며 호출 코드는 바꾸지 않습니다.
npx react-native-nitro-device-info migrate
# or target a specific directory:
npx react-native-nitro-device-info migrate src
ES import(default, named, namespace), re-export, CommonJS require()를 바꿉니다.
// Before
import DeviceInfo from 'react-native-device-info';
import { getModel, useBatteryLevel } from 'react-native-device-info';
// After (rewritten automatically — call sites unchanged)
import DeviceInfo from 'react-native-nitro-device-info/compat';
import { getModel, useBatteryLevel } from 'react-native-nitro-device-info/compat';
수동으로 작업한다면 프로젝트 전체에서 import 문자열 'react-native-device-info'를 'react-native-nitro-device-info/compat'으로 바꿔도 같은 결과를 얻습니다.
3. 이전 의존성 제거
npm uninstall react-native-device-info
4. 문서의 주의 사항 확인
호환 계층은 RNDI 15.x API를 대상으로 합니다. 이 라이브러리에 네이티브 대응 기능이 없는 일부 API는 fallback 값을 반환합니다. 아래 호환 계층 주의 사항을 확인하세요. 앱에서 이 API를 쓰지 않으면 전환이 완료됩니다.
호출 코드를 바꾸거나 await를 추가·제거할 필요가 없습니다.
호환 계층 주의 사항
호환 계층은 다음 예외를 포함해 RNDI 전체 API의 시그니처에 맞춥니다.
다른 API는 이 라이브러리의 네이티브 구현에 위임합니다. 플랫폼별 fallback 값을 반환하거나 RNDI와 다르게 동작할 수 있습니다. 일부 결과 형태는 자동 변환합니다. 예를 들어 getAvailableLocationProviders()는 RNDI의 { gps: true, network: true } 맵을 반환합니다. getFreeDiskStorage(storageType?)는 iOS 저장 공간 타입 인자를 받지만 무시합니다. 비동기 오디오 기기 훅은 RNDI의 { loading, result } 형태를 반환합니다.
호환 계층 대신 속성을 직접 읽고 동기 getter를 사용하려면 패키지 루트에서 DeviceInfoModule을 import하고 아래 표에 따라 호출 코드를 바꾸세요.
주요 차이
구조
- react-native-device-info: 설치한 릴리스에 따라 API와 구현이 다릅니다.
- react-native-nitro-device-info: Nitro HybridObject(JSI)를 사용합니다.
속성과 메서드: 대부분의 RNDI 메서드는 직접 읽는 속성으로 바뀝니다.
// react-native-device-info // native API
const deviceId = DeviceInfo.getDeviceId(); const deviceId = DeviceInfoModule.deviceId;
const brand = DeviceInfo.getBrand(); const brand = DeviceInfoModule.brand;
기본 동기 호출: RNDI에서 비동기였던 값이 동기로 바뀝니다.
const uniqueId = DeviceInfoModule.uniqueId; // sync property
const totalMemory = DeviceInfoModule.totalMemory; // sync property
const batteryLevel = DeviceInfoModule.getBatteryLevel(); // sync method
const isTablet = DeviceInfoModule.isTablet; // sync property
Promise 메서드는 여전히 await가 필요합니다.
const ipAddress = await DeviceInfoModule.getIpAddress();
const carrier = await DeviceInfoModule.getCarrier();
const installTime = await DeviceInfoModule.getFirstInstallTime();
빠른 대응표
시스템 리소스
네트워크와 연결
네이티브 API로 전환하는 순서
네이티브 API로 전환하는 절차입니다. 호출 코드를 유지하려면 호환 API 마이그레이션을 사용하세요.
1. 새 라이브러리 설치
# Remove old library
npm uninstall react-native-device-info
# Install new library
npm install react-native-nitro-device-info react-native-nitro-modules
# iOS
cd ios && pod install && cd ..
2. import 수정
코드 전체에서 import를 찾아 바꾸세요.
// Before
import DeviceInfo from 'react-native-device-info';
// After
import { DeviceInfoModule } from 'react-native-nitro-device-info';
3. 모듈 이름 수정
모든 DeviceInfo 참조를 DeviceInfoModule로 바꾸세요.
// Before
const brand = DeviceInfo.getBrand();
// After
const brand = DeviceInfoModule.brand; // Also changed to property
4. 메서드 호출을 속성 접근으로 변경
메서드가 속성으로 바뀌었다면 호출 대신 속성을 읽도록 수정하세요.
// Before
const deviceId = DeviceInfo.getDeviceId();
const brand = DeviceInfo.getBrand();
const model = DeviceInfo.getModel();
const systemName = DeviceInfo.getSystemName();
const systemVersion = DeviceInfo.getSystemVersion();
const readableVersion = DeviceInfo.getReadableVersion();
// After
const deviceId = DeviceInfoModule.deviceId;
const brand = DeviceInfoModule.brand;
const model = DeviceInfoModule.model;
const systemName = DeviceInfoModule.systemName;
const systemVersion = DeviceInfoModule.systemVersion;
const readableVersion = DeviceInfoModule.readableVersion;
5. 불필요한 await 제거
동기로 바뀐 메서드의 await를 제거하세요.
// Before
const uniqueId = await DeviceInfo.getUniqueId();
const totalMemory = await DeviceInfo.getTotalMemory();
const batteryLevel = await DeviceInfo.getBatteryLevel();
// After (no await needed, now sync properties/methods)
const uniqueId = DeviceInfoModule.uniqueId;
const totalMemory = DeviceInfoModule.totalMemory;
const batteryLevel = DeviceInfoModule.getBatteryLevel();
6. 변경 테스트
앱을 실행하고 확인하세요.
- 모든 기기 정보 호출의 정상 동작
- 비동기·동기 변경으로 인한 런타임 오류 여부
- TypeScript 타입의 정확성
전체 예제
변경 전(react-native-device-info)
import React, { useEffect, useState } from 'react';
import DeviceInfo from 'react-native-device-info';
function DeviceInfoScreen() {
const [deviceId, setDeviceId] = useState('');
const [brand, setBrand] = useState('');
const [uniqueId, setUniqueId] = useState('');
const [totalMemory, setTotalMemory] = useState(0);
const [batteryLevel, setBatteryLevel] = useState(0);
const [ipAddress, setIpAddress] = useState('');
useEffect(() => {
async function loadDeviceInfo() {
// Everything was async or method-based
const id = DeviceInfo.getDeviceId();
const b = DeviceInfo.getBrand();
const uid = await DeviceInfo.getUniqueId();
const mem = await DeviceInfo.getTotalMemory();
const bat = await DeviceInfo.getBatteryLevel();
const ip = await DeviceInfo.getIpAddress();
setDeviceId(id);
setBrand(b);
setUniqueId(uid);
setTotalMemory(mem);
setBatteryLevel(bat);
setIpAddress(ip);
}
loadDeviceInfo();
}, []);
return (
<View>
<Text>Device: {brand} {deviceId}</Text>
<Text>Unique ID: {uniqueId}</Text>
<Text>Memory: {totalMemory}</Text>
<Text>Battery: {batteryLevel < 0 ? 'Unavailable' : `${(batteryLevel * 100).toFixed(0)}%`}</Text>
<Text>IP: {ipAddress}</Text>
</View>
);
}
변경 후(react-native-nitro-device-info)
import React, { useEffect, useState } from 'react';
import { DeviceInfoModule } from 'react-native-nitro-device-info';
function DeviceInfoScreen() {
const [ipAddress, setIpAddress] = useState('');
// Sync properties/methods - instant access, no state needed
const deviceId = DeviceInfoModule.deviceId;
const brand = DeviceInfoModule.brand;
const uniqueId = DeviceInfoModule.uniqueId;
const totalMemory = DeviceInfoModule.totalMemory;
const batteryLevel = DeviceInfoModule.getBatteryLevel();
useEffect(() => {
// Only async operations need useEffect
DeviceInfoModule.getIpAddress().then(setIpAddress);
}, []);
return (
<View>
<Text>Device: {brand} {deviceId}</Text>
<Text>Unique ID: {uniqueId}</Text>
<Text>Memory: {totalMemory}</Text>
<Text>Battery: {batteryLevel < 0 ? 'Unavailable' : `${(batteryLevel * 100).toFixed(0)}%`}</Text>
<Text>IP: {ipAddress}</Text>
</View>
);
}
마이그레이션의 이점
// react-native-device-info: model and brand already return synchronously.
const brand = DeviceInfo.getBrand();
const model = DeviceInfo.getModel();
const memory = await DeviceInfo.getTotalMemory();
// Native API: direct properties; measure latency in your app.
const brand = DeviceInfoModule.brand;
const model = DeviceInfoModule.model;
const memory = DeviceInfoModule.totalMemory;
2. 간단한 코드
단순 getter에는 async/await가 필요하지 않습니다.
// Old - required async function
async function getDeviceInfo() {
const brand = DeviceInfo.getBrand();
const model = DeviceInfo.getModel();
return `${brand} ${model}`;
}
// New - direct access
function getDeviceInfo() {
return `${DeviceInfoModule.brand} ${DeviceInfoModule.model}`;
}
3. TypeScript 지원
전체 타입 정의와 IntelliSense를 제공합니다.
import type { PowerState, BatteryState } from 'react-native-nitro-device-info';
const powerState: PowerState = DeviceInfoModule.getPowerState();
// TypeScript knows: powerState.batteryLevel, powerState.batteryState, powerState.lowPowerMode
4. New Architecture 기반 구조
장기 지원을 위해 React Native의 New Architecture(Fabric + JSI)를 사용합니다.
React 훅 마이그레이션
호환 API: 모든 RNDI 훅을 같은 시그니처로 re-export합니다. 비동기 훅의 { loading, result } 형태도 포함합니다. codemod가 import를 수정하며 사용법은 같습니다.
// Before
import { useBatteryLevel, useDeviceName } from 'react-native-device-info';
// After (rewritten to the compat subpath — usage identical)
import { useBatteryLevel, useDeviceName } from 'react-native-nitro-device-info/compat';
function BatteryWidget() {
const batteryLevel = useBatteryLevel(); // number | null
const { result: name } = useDeviceName(); // AsyncHookResult<string>
return <Text>{name}: {batteryLevel}</Text>;
}
네이티브 API: 패키지 루트에서는 AsyncHookResult 래퍼 없이 값 자체를 반환하는 훅 7개를 직접 export합니다. 호출 코드를 네이티브 API로 옮길 때 사용하세요.
나머지 RNDI 훅(useFirstInstallTime, useDeviceName, useHasSystemFeature, useIsEmulator, useManufacturer)은 호환 API에서만 제공하며 RNDI의 AsyncHookResult<T> 형태를 반환합니다.
전체 훅 설명은 React 훅 가이드를 참고하세요.
네이티브 API로 전환할 때 달라지는 동작
다음 차이는 네이티브 API(DeviceInfoModule)를 사용할 때 적용됩니다. 호환 계층은 함수 시그니처와 훅 결과 형태를 맞추지만 모든 플랫폼 동작을 재현하지는 않습니다. 호환 계층 주의 사항을 확인하세요.
- 기본 동기 호출: 대부분의 메서드는 더 이상 Promise를 반환하지 않습니다.
- 속성 접근: 일부 getter는 속성입니다.
- 모듈 이름:
DeviceInfo 대신 DeviceInfoModule을 사용합니다.
- 이벤트 리스너: 원시 배터리·네트워크 상태 리스너를 export하지 않습니다. 상태 관찰에는 제공하는 React 훅(
useBatteryLevel, usePowerState 등)을 사용하세요. 호환 계층은 RNDI 훅을 같은 형태로 re-export합니다.
문제 해결
마이그레이션 후 TypeScript 오류
타입 오류가 발생하면 다음을 확인하세요.
# Clear TypeScript cache
rm -rf node_modules/.cache
yarn install
# Restart TypeScript server in your IDE
런타임 오류
"Cannot read property" 오류가 발생하면 확인하세요.
- peer dependencies
react-native-nitro-modules 설치 여부
- iOS의
pod install 실행 여부
- 정리 후 빌드(
yarn android / yarn ios)
메서드를 찾을 수 없는 경우
- API 레퍼런스에서 정확한 메서드 이름을 확인하세요.
- 일부 메서드는 이름이 바뀌거나 통합됐을 수 있습니다.
- 필요한 메서드가 없으면 GitHub 이슈를 등록하세요.
expo-device에서 마이그레이션
expo-device의 여러 API에 대응하는 API를 react-native-nitro-device-info에서 제공합니다.
주요 차이
API 대응표
마이그레이션 예제
변경 전(expo-device):
import * as Device from 'expo-device';
async function getDeviceInfo() {
const deviceType = await Device.getDeviceTypeAsync();
const isRooted = await Device.isRootedExperimentalAsync();
const canSideload = await Device.isSideLoadingEnabledAsync();
return {
brand: Device.brand,
model: Device.modelName,
yearClass: Device.deviceYearClass,
totalMemory: Device.totalMemory,
deviceType,
isRooted,
canSideload,
};
}
변경 후(react-native-nitro-device-info):
import { DeviceInfoModule } from 'react-native-nitro-device-info';
function getDeviceInfo() {
// All synchronous - no async/await needed!
return {
brand: DeviceInfoModule.brand,
model: DeviceInfoModule.model,
yearClass: DeviceInfoModule.deviceYearClass,
totalMemory: DeviceInfoModule.totalMemory,
deviceType: DeviceInfoModule.deviceType,
isRooted: DeviceInfoModule.isDeviceCompromised(),
canSideload: DeviceInfoModule.isSideLoadingEnabled(),
};
}
제공하지 않는 API
다음 expo-device API에는 직접 대응하는 API가 없습니다.
가동 시간 동작
iOS의 getUptime()은 앱 이벤트 시간 측정에만 지원합니다. 경과 시간 측정, 타이머, 또는 startupTime으로 앱 이벤트 uptime을 절대 타임스탬프로 변환하는 용도입니다. 원시 가동 시간과 부팅 타임스탬프는 일반 기기 정보 데이터에 포함하지 마세요. 선언한 목적과 전송 제한은 iOS privacy info manifest를 참고하세요.
- expo-device: 깊은 절전을 제외한 가동 시간을 밀리초로 반환
- react-native-nitro-device-info: 깊은 절전을 제외한 가동 시간을 밀리초로 반환
- iOS:
systemUptime 사용
- Android:
uptimeMillis() 사용
- 두 플랫폼 모두 expo-device와 같은 활성 시간 반환
year class
두 라이브러리는 RAM과 CPU 사양으로 year class를 계산하는 같은 Facebook 알고리즘을 사용합니다. year class는 하드웨어가 고급 사양으로 평가됐을 것으로 추정하는 연도입니다.
루팅·탈옥 탐지
- expo-device:
isRootedExperimentalAsync() — 실험적 기본 검사
- react-native-nitro-device-info:
isDeviceCompromised() — 종합 검사
- Android: su 바이너리, 루팅 앱, 시스템 속성, test-keys 검사
- iOS: Cydia, 탈옥 파일, 샌드박스 탈출, 의심 경로 검사
react-native-carrier-info에서 마이그레이션
통신사 정보에 react-native-carrier-info를 사용하고 있다면 react-native-nitro-device-info로 통합할 수 있습니다.
API 대응표
마이그레이션 예제
변경 전(react-native-carrier-info):
import CarrierInfo from 'react-native-carrier-info';
async function getCarrierDetails() {
const carrierName = await CarrierInfo.carrierName();
const allowsVOIP = await CarrierInfo.allowsVOIP();
const isoCountryCode = await CarrierInfo.isoCountryCode();
const mcc = await CarrierInfo.mobileCountryCode();
const mnc = await CarrierInfo.mobileNetworkCode();
const operator = await CarrierInfo.mobileNetworkOperator();
return { carrierName, allowsVOIP, isoCountryCode, mcc, mnc, operator };
}
변경 후(react-native-nitro-device-info):
import { DeviceInfoModule } from 'react-native-nitro-device-info';
function getCarrierDetails() {
// All synchronous - no async/await needed!
return {
carrierName: DeviceInfoModule.getCarrierSync(),
allowsVOIP: DeviceInfoModule.carrierAllowsVOIP,
isoCountryCode: DeviceInfoModule.carrierIsoCountryCode,
mcc: DeviceInfoModule.mobileCountryCode,
mnc: DeviceInfoModule.mobileNetworkCode,
operator: DeviceInfoModule.mobileNetworkOperator,
};
}
주요 이점
- async/await 불필요: 모든 통신사 속성은 동기입니다.
- 단일 의존성: 별도 carrier-info 패키지가 필요하지 않습니다.
- JSI 직접 접근: Nitro Modules로 네이티브 코드에 접근합니다.
- 통합 API: 다른 80개 이상의 기기 속성과 통신사 정보를 함께 제공합니다.
- iOS:
CTTelephonyNetworkInfo와 CTCarrier API 사용
- Android:
TelephonyManager API 사용
- carrierAllowsVOIP: iOS에서는 실제 값, Android에서는 대응 API가 없어 항상
true
- 빈 문자열: SIM 카드가 없으면 모든 속성이
"" 반환
도움이 필요한 경우