안녕하세요 여러분! 👋 크로스 플랫폼 React Native 앱에서 건강, 피트니스, 유틸리티 기능을 만들어 본 적이 있다면 그 고통을 아실 겁니다. 하나의 기능 세트를 작성하는 것이 아니라 완전히 다른 두 개의 무거운 네이티브 생태계와 씨름하게 됩니다.
1. 시작
iOS에는 성숙한 Apple HealthKit API가 있습니다. Android에는 기존 Google Fit SDK를 대체한 Google의 통합 API 프레임워크 Health Connect가 있습니다. 새 앱 WaterPaw의 수분 섭취 추적 아키텍처를 만들 때 완전히 모듈화된 해법을 원했습니다.
UI 계층을 어지럽히는 플랫폼 조건 검사(Platform.OS === 'ios')는 원하지 않았습니다. 가용성, 권한, 데이터 가져오기를 한곳에서 처리하는 하나의 추상 커스텀 React 훅을 원했습니다. 다음은 두 플랫폼을 통합하기 위해 사용한 깔끔한 설계 패턴이며, 네이티브 구성은 Expo Config Plugins로 자동 처리합니다.
먼저 프런트엔드가 어떤 네이티브 플랫폼에서 실행되는지 신경 쓰지 않도록 구체적인 청사진(인터페이스)을 정의했습니다.
export type HydrationData = {
value: number; // in milliliters
date: string;
};
export interface HealthBridge {
isAvailable(): Promise<boolean>;
requestPermissions(): Promise<boolean>;
getHydrationData(date: Date): Promise<HydrationData[]>;
}2. 구체적인 브리지
그다음 정확한 구현 세부 사항을 격리된 네이티브 래퍼로 분리했습니다.
- iOS 래퍼:
react-native-health를 기반으로 초기화 상태를 확인하고 표준 HealthKit 권한 매트릭스를 전달합니다. - Android 래퍼:
react-native-health-connect를 기반으로 필수 2단계 실행 흐름(초기화 확인 후 별도의 권한 인텐트 계약)을 사용합니다.
3. 훅: 동적 플랫폼 내보내기
플랫폼에 따라 브리지를 동적으로 내보내면 컴포넌트가 하나의 커스텀 훅만 호출할 수 있다는 점에서 마법이 일어납니다.
import { Platform } from 'react-native';
import { iOSHealthBridge } from './iOSHealthBridge';
import { AndroidHealthBridge } from './AndroidHealthBridge';
const HealthAPI = Platform.OS === 'ios' ? iOSHealthBridge : AndroidHealthBridge;
export function useHydrationSync() {
const syncData = async () => {
const available = await HealthAPI.isAvailable();
if (!available) return;
const hasPermission = await HealthAPI.requestPermissions();
if (hasPermission) {
const records = await HealthAPI.getHydrationData(new Date());
// Commit directly to a local-first store (e.g. AsyncStorage or Zustand)
}
};
return { syncData };
}4. Expo의 큰 함정 💡
두 네이티브 패키지 모두 크게 커스터마이징된 네이티브 코드(Objective-C와 Kotlin)를 사용하므로 표준 Expo Go 안에서는 컴파일되지 않습니다. Xcode나 Android Studio를 직접 만지지 않고 깔끔하게 우회하려면 app.json에 config plugins를 선언해야 합니다. Health Connect는 최소 SDK 26을 명시적으로 요구합니다.
{
"expo": {
"plugins": [
["react-native-health", { "NSHealthShareUsageDescription": "Sync your hydration records." }],
["expo-health-connect"],
[
"expo-build-properties",
{ "android": { "minSdkVersion": 26 } }
]
]
}
}이렇게 업데이트한 뒤 npx expo prebuild --clean를 실행하면 네이티브 생성이 자동으로 처리됩니다.
왜 이렇게까지 하나요?
미니멀 유틸리티를 만들 때는 성능과 마찰 없는 경험이 전부입니다. 이 아키텍처를 사용하면 UI는 60 FPS로 부드럽게 유지되고 업데이트는 즉각적이며, 로컬 우선이므로 앱이 사용자의 데이터 프라이버시를 온전히 존중합니다. 서드파티 서버도, 필수 회원가입도 없습니다.
이 구조적 로직으로 방금 출시한 WaterPaw의 스마트 알림과 마스코트 성장 시스템을 구현했습니다.
