-
네이티브앱 통합 관점에서 RN vs FlutterReact Native 2024. 11. 19. 12:07
요약)
가장 중요한 차이는
RN은 JavaScript 런타임과 네이티브 의존성을 따로 관리 하는 반면, Flutter는 자체 내장 엔진에서 모든 의존성을 자체 관리React Native Flutter 의존성관리 JavaScript 런타임과 네이티브 의존성을 Podfile로 따로 관리 - 모듈 생성시 생성된 .ios/Flutter/podhelper.rb를 통해 CocoaPods 의존성을 자동으로 관리
- Flutter 모듈의 모든 네이티브 의존성을 캡슐화통합방식
기존 iOS 프로젝트의 Podfile과 RN의 Podfile을 병합해야 함
Why? )
React Native는 JavaScript 코드를 실행하기 위해
React, React-Core, React-RCTBridge 등 다양한 네이티브 모듈에 의존
CocoaPods으로 개별 설치해야 함
(ios폴더 내 Podfile이 RN의 네이티브 의존성을 관리)
=> 기존 iOS 프로젝트와 통합하려면, 기존 iOS프로젝트의 Podfile과 React Native에서 사용하는 Podfile을 통합해주어야함.Podfile에 Flutter 모듈 경로만 추가하면 됨
Why? )
Flutter는 자체 내장 엔진을 가지고 있음
=> iOS프로젝트의 Podfile에 Flutter 모듈 경로만 추가하면 됨통합용
이성RN 프로젝트의 Podfile에서 사용하는 라이브러리와 기존 프로젝트의 동일한 라이브러리가 충돌 Flutter모듈의 독립적인 의존성 관리로 의존성 충돌 가능성이 낮음 빌드성능 Bridge의 오버헤드로 인해 상대적으로 느림 Flutter 엔진 내장으로 네이티브와 거의 동일한 성능 제공 통신방식 CocoaPod을 통해 설치된 React-RCTBridge를 통해 JavaScript와 네이티브간 통신 MethodChannel을 통해 Dart와 네이티브 간 직접적으로 통신 환경셋팅만 잘 이루어진다면.. RN이 더 나을지도 모르겠다
( GPT의 반대 관점 !!!! 혼란스럽다 )
iOS와 Android의 네이티브 기능을 많이 활용해야 하는 경우 React Native를 선호할 수 있는 이유는 다음과 같습니다:
1. 네이티브 코드와의 손쉬운 통합
React Native는 기본적으로 네이티브 모듈(Swift, Objective-C, Java, Kotlin)과 매우 쉽게 연동할 수 있는 구조를 제공합니다.
• iOS와 Android 네이티브 모듈 확장: 네이티브에서 직접 구현한 기능을 React Native에서 호출할 수 있어, 플랫폼에 특화된 기능을 개발하고 통합하기에 적합합니다.
• 예: iOS의 Core Location, Face ID, Android의 CameraX 등의 플랫폼별 API를 React Native의 네이티브 모듈로 래핑하여 사용할 수 있습니다.
(ReactNative의 iOS 폴더 내에서 네이티브 모듈을 구현한 후, ReactNative에서 바로 호출하는 구조. 네이티브 프로젝트에서 코드관리X 반면 Flutter는 네이티브 프로젝트에서 플러터ViewController를 생성한 뒤, MethodChannel로 받아주어야함
>> 예시코드는 밑에 있음)
2. 기존 네이티브 프로젝트와의 병합 가능성
React Native는 기존의 네이티브 앱 프로젝트에 일부 화면으로 통합하는 데 적합합니다.
• 이미 iOS 또는 Android로 개발된 앱이 있을 때, 특정 화면이나 기능을 React Native로 구현해 추가할 수 있습니다.
• iOS ViewController나 Android Activity/Fragment를 React Native 화면과 결합하는 것이 비교적 간단합니다.
3. 커스텀 네이티브 코드 개발이 용이
React Native는 네이티브 API에서 제공하지 않는 고유 기능을 개발해야 할 때도 유연합니다.
• Custom Native Module: iOS와 Android에서 별도로 네이티브 기능을 개발한 후, JavaScript에서 호출할 수 있도록 연결할 수 있습니다.
• 예: iOS에서 Metal API로 고성능 그래픽을 구현하거나, Android에서 BLE(Bluetooth Low Energy) 기능을 네이티브로 구현한 후 React Native와 연동 가능.
4. 네이티브 코드와 JavaScript 간 통신 구조 (Bridge)
React Native는 Bridge 아키텍처를 사용하여 JavaScript와 네이티브 간 데이터를 주고받을 수 있습니다.
• 복잡한 네이티브 로직은 네이티브 코드로 구현하고, React Native는 UI 및 간단한 로직을 처리하는 방식으로 역할 분리가 가능합니다.
• 이 방식은 성능 병목 현상을 줄이고 네이티브 기능을 쉽게 호출할 수 있도록 합니다.
5. 풍부한 네이티브 플러그인 생태계
React Native는 커뮤니티에서 개발된 수많은 네이티브 플러그인을 제공합니다.
• 네이티브 기능(카메라, 지도, 파일 저장소 등)을 구현할 때, 기존 플러그인을 활용하면 추가 구현 없이 바로 사용할 수 있습니다.
• 예: react-native-camera, react-native-maps, react-native-fs 등이 있으며, 필요한 경우 직접 커스터마이징도 가능합니다.
6. 코드 재사용성 극대화
React Native는 iOS와 Android에 공통 코드베이스를 사용하면서도, 플랫폼별 네이티브 코드와 연동이 가능하기 때문에, 네이티브 기능을 활용하면서도 중복 작업을 최소화할 수 있습니다.
7. React Native의 한계점
• Bridge 기반 구조로 인해, 네이티브와 JavaScript 간 데이터 통신 속도가 병목 현상을 유발할 수 있습니다. 이는 매우 고성능을 요구하는 앱에서는 문제가 될 수 있습니다.
• 복잡한 네이티브 기능을 많이 사용하는 경우, 네이티브 코드 작성량이 많아져 크로스플랫폼의 이점이 줄어들 수 있습니다.
Flutter와 비교
Flutter도 네이티브 기능 연동이 가능하지만, React Native보다 초기 설정이 복잡하고, 플랫폼별 코드를 작성할 때 조금 더 많은 작업이 필요합니다.
• Flutter는 네이티브 코드와 연동하기 위해 MethodChannel을 사용하지만, 네이티브 개발 경험이 없는 개발자에게는 추가 학습 부담이 될 수 있습니다.
• 반면 React Native는 기존 네이티브 코드 경험을 살려 더 직관적으로 접근할 수 있습니다.
- Flutter는 MethodChannel을 사용하여 Flutter와 네이티브 간의 직접적인 통신을 설정- React Native는 네이티브 모듈을 통해 Bridge 방식으로 통신 >> 초기에 작업해야 할 설정의 복잡성과 사용 편의성에서 차이가 발생
1. Flutter: MethodChannel 사용
Flutter는 네이티브와의 통신을 위해 MethodChannel이라는 구조를 사용
• Flutter와 네이티브 간 통신을 위한 MethodChannel 설정.
• 플랫폼별로 명시적으로 MethodHandler를 작성하여 각 네이티브 기능을 처리.
• 결과값을 다시 Flutter로 반환.
[Flutter 코드]import 'package:flutter/services.dart'; class NativeUtils { static const platform = MethodChannel('com.example/native'); // 네이티브 메서드 호출 static Future<String> getBatteryLevel() async { try { final String batteryLevel = await platform.invokeMethod('getBatteryLevel'); return 'Battery level: $batteryLevel%'; } catch (e) { return 'Failed to get battery level: $e'; } } }
[ iOS (Swift): MethodChannel 구현]import UIKit import Flutter @UIApplicationMain @objc class AppDelegate: FlutterAppDelegate { override func application( _ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? ) -> Bool { let controller: FlutterViewController = window?.rootViewController as! FlutterViewController let methodChannel = FlutterMethodChannel(name: "com.example/native", binaryMessenger: controller.binaryMessenger) methodChannel.setMethodCallHandler { (call: FlutterMethodCall, result: @escaping FlutterResult) in if call.method == "getBatteryLevel" { self.getBatteryLevel(result: result) } else { result(FlutterMethodNotImplemented) } } return super.application(application, didFinishLaunchingWithOptions: launchOptions) } private func getBatteryLevel(result: FlutterResult) { let device = UIDevice.current device.isBatteryMonitoringEnabled = true if device.batteryState == .unknown { result(FlutterError(code: "UNAVAILABLE", message: "Battery info unavailable", details: nil)) } else { result(Int(device.batteryLevel * 100)) } } }
• MethodChannel 작성: Flutter와 네이티브 간 직접적인 메시지 전달 방식을 설정해야 하므로 네이티브 처리에 대한 이해가 필요
• 플랫폼별 코드 작성: iOS와 Android 각각에 대해 별도의 코드 작성이 필수
2. React Native: Native Modules 사용- React Native는 네이티브와 통신하기 위해 Native Module이라는 방식을 사용
- Bridge 아키텍처를 통해 JavaScript와 네이티브 코드 간의 통신을 추상화하여 더 직관적으로 접근
React Native 네이티브 연동 단계
1. RN프로젝트 내에서 ios/android폴더 내에 네이티브 모듈 정의
2. JavaScript에서 NativeModules를 통해 네이티브 호출.
3. 네이티브 코드에서 처리 후 결과 반환.
React Native 에서 네이티브 모듈 호출import { NativeModules } from 'react-native'; const { NativeUtils } = NativeModules; export const getBatteryLevel = async () => { try { const batteryLevel = await NativeUtils.getBatteryLevel(); console.log(`Battery level: ${batteryLevel}%`); return batteryLevel; } catch (error) { console.error('Failed to get battery level:', error); return null; } };
ReactNative의 iOS 폴더 내에서 네이티브 모듈 정의@objc(NativeUtils) class NativeUtils: NSObject { @objc func getBatteryLevel(_ resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) { let device = UIDevice.current device.isBatteryMonitoringEnabled = true if device.batteryState == .unknown { reject("UNAVAILABLE", "Battery info unavailable", nil) } else { resolve(Int(device.batteryLevel * 100)) } } @objc static func requiresMainQueueSetup() -> Bool { return false } }
iOS네이티브 프로젝트에서 모듈 등록#import <React/RCTBridgeModule.h> @interface RCT_EXTERN_MODULE(NativeUtils, NSObject) RCT_EXTERN_METHOD(getBatteryLevel:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject) @end
React Native 연동의 학습 부담
• React Native는 네이티브 모듈을 생성하면 해당 기능이 JavaScript로 자동 연결되므로 설정이 간소화됩니다.
• 플랫폼별 코드 작성이 필요하지만, 기존 네이티브 개발자라면 친숙한 환경에서 작업할 수 있습니다.
• JavaScript로 네이티브 모듈을 쉽게 호출할 수 있으므로 사용이 직관적입니다.
비교: Flutter vs React Native- 설정 복잡성 측면: Fluutter는 MethodChannel 생성, 각 네이티브 프로젝트 코드에서 MethodChannel을 받아서 처리해야함
(플랫폼별 코드를 각각 작성)
반면 RN은 RN폴더 내에서 네이티브 모듈 생성 후 바로 사용 가능 (호출이 직관적 ??)
>> iOS/Android의 네이티브 기능 연동이 빈번하다면 React Native가 더 적합할 가능성이 높다 ???
(참고)Flutter의 자체 엔진이 주는 장점
Flutter는 React Native와 달리 자체 엔진을 포함하고 있어 여러 면에서 차별화됩니다.
4.1. 자체 엔진의 특징
- Flutter는 C++ 기반의 렌더링 엔진을 사용하여 모든 UI 및 로직을 실행합니다.
- Dart VM은 Flutter 엔진 내부에서 동작하며, UI를 직접 그리는 방식(Skia)을 사용합니다.
- 네이티브 플랫폼의 컴포넌트를 활용하지 않고, 완전히 독립적인 렌더링 방식을 사용합니다.
4.2. 장점
- 플랫폼 독립성:
- Flutter 엔진은 iOS와 Android에서 동일하게 동작하므로, UI 일관성이 유지됩니다.
- 네이티브 의존성이 최소화되며, 플랫폼 간 동작 차이를 걱정할 필요가 없습니다.
- 고성능:
- React Native는 JSCore와 네이티브 간 통신(Bridge)에 오버헤드가 발생하지만, Flutter는 자체 엔진 내에서 모든 UI 렌더링과 로직 처리를 수행하므로 네이티브와 거의 동일한 성능을 제공합니다.
- 통합 간소화:
- Flutter는 .ios/Flutter를 통해 모든 의존성을 캡슐화하여 기존 네이티브 프로젝트와의 충돌 가능성을 최소화합니다.
- Podfile에 Flutter 모듈 경로를 추가하는 것만으로도 통합이 완료됩니다.
'React Native' 카테고리의 다른 글
iOS네이티브 프로젝트에 Flutter 병합하기 - 1 (0) 2024.11.28 iOS 네이티브 프로젝트에 통합 - RN, Flutter 환경셋팅 비교 (0) 2024.11.20 왜 네이티브 개발에는 상태관리 개념이 없는가? (0) 2024.11.18 [iOS 네이티브 앱에 RN화면 접목하기] (0) 2024.11.11 Props에 대해 (0) 2024.11.11