모바일 개발에서의 Rust: 네이티브 모듈 및 크로스플랫폼 SDK

저자: IT Sectr 게시일: 2026-02-11 읽는 시간: 10 분

Rust — 컴파일 타임에 메모리 안전성을 보장하는 시스템 프로그래밍 언어입니다. Graydon Hoare의 지휘 아래 2010년 Mozilla Research에서 개발했습니다. Rust는 null 포인터, 댕글링 참조, 데이터 경합 등 전체 취약점 클래스를 방지합니다. 모바일 개발에서 Rust는 네이티브 모듈, 크로스플랫폼 SDK 및 WebAssembly에 사용됩니다. Stack Overflow Survey (2025)에 따르면 Rust는 9년 연속으로 «가장 사랑받는 언어» 1위를 차지하고 있습니다.

핵심 요점

  • Rust — 소유권(ownership) 시스템을 통해 가비지 컬렉터 없이 메모리 안전성을 제공하는 시스템 언어
  • Borrow checker — 컴파일 타임에 참조를 검사하여 데이터 경합을 방지하는 분석기
  • 네이티브 모듈은 cargo-ndk와 cargo-lipo를 통해 Android용 .so, iOS용 .a로 컴파일됩니다
  • WebAssembly (Wasm)을 사용하면 브라우저와 모바일 기기에서 네이티브에 가까운 성능으로 Rust 코드를 실행할 수 있습니다
  • FFI (Foreign Function Interface) — C 호환 ABI를 통해 Java, Kotlin, Swift에서 Rust를 호출하는 메커니즘

Rust란?

Rust는 제로 비용 추상화(zero-cost abstractions)를 갖춘 정적 타입 시스템 프로그래밍 언어입니다. Rust는 소유권(ownership), 대여(borrowing), 수명(lifetimes) 시스템 덕분에 가비지 컬렉터 없이 메모리 안전성을 보장합니다. Rust 컴파일러는 가장 엄격한 컴파일러 중 하나로, C++에서 정의되지 않은 동작을 유발하는 코드를 거부합니다.

Rust는 LLVM을 통해 ARM, x86, RISC-V, WebAssembly 등 모든 아키텍처용 네이티브 코드로 컴파일됩니다. 도구 체인에는 rustup(설치 프로그램), rustc(컴파일러), cargo(패키지 관리자 및 빌드 도구)가 포함됩니다. Cargo.toml은 package.json과 유사하며 crates.io의 종속성을 포함합니다. 최신 안정 버전은 Rust 1.81(2025)입니다.

모바일 개발에서 Rust의 주요 적용 분야: 네이티브 라이브러리(Android NDK, iOS), 크로스플랫폼 SDK(Firefox, Dropbox), 성능 중요 부분에서 C++ 대체, 하이브리드 애플리케이션용 WebAssembly. Google은 AOSP(Android Open Source Project)에서 Bluetooth, Wi-Fi, NFC 구성 요소에 Rust를 사용합니다.

모바일 개발을 위한 Rust 설치 및 구성

Android용 크로스 컴파일에는 대상 툴체인이 필요합니다: rustup target add aarch64-linux-android armv7-linux-androideabi. iOS용 — aarch64-apple-ios x86_64-apple-ios. cargo-ndk(Android용)와 cargo-lipo(iOS용)는 빌드 프로세스를 단순화합니다.

sh
# 모바일 개발을 위한 Rust 및 대상 플랫폼 설치
$ curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
$ rustup target add aarch64-linux-android
$ rustup target add armv7-linux-androideabi
$ rustup target add aarch64-apple-ios
$ rustup target add x86_64-apple-ios

# Android 빌드를 위한 cargo-ndk 설치
$ cargo install cargo-ndk

# Android용 빌드
$ cargo ndk -t arm64-v8a -o ./jniLibs build --release

소유권 시스템: ownership, borrowing, lifetimes

Ownership(소유권)은 Rust의 기본 개념입니다. 각 값에는 정확히 한 명의 소유자가 있습니다. 할당되거나 함수에 전달되면 소유권이 이동(move)됩니다. 이동 후 이전 소유자는 값을 사용할 수 없습니다. 이는 이중 메모리 해제와 댕글링 포인터를 제거합니다.

Borrowing(대여)을 사용하면 소유권을 이전하지 않고 값에 대한 참조를 임시로 사용할 수 있습니다. &T 참조(불변) — 읽기 수 제한 없음. &mut T 참조(가변) — 정확히 한 명의 작성자, 읽기 불가. 컴파일러는 컴파일 타임에 이러한 규칙을 확인합니다.

rust
fn process_data(data: &mut Vec<i32>) {
    for item in data.iter_mut() {
        *item *= 2;
    }
}

fn main() {
    let mut numbers = vec![1, 2, 3, 4, 5];
    process_data(&mut numbers);
    println!("Processed: {:?}", numbers);

    let first = &numbers[0];  // 불변 대여
    println!("First: {}", first);
}

process_data는 Vec에 대한 가변 참조를 받아 각 요소를 두 배로 만듭니다. iter_mut()는 가변 참조가 있는 반복자를 반환합니다. *item은 할당을 위해 참조를 역참조합니다. process_data 이후에도 numbers는 계속 접근 가능합니다 — 소유권이 이동하지 않고 대여만 되었습니다.

Lifetimes: 참조의 수명

Lifetimes('a) — 참조의 수명을 연결하는 어노테이션입니다. 컴파일러는 이를 사용하여 참조가 가리키는 데이터보다 오래 살지 않는지 확인합니다. 대부분의 경우 수명은 자동으로 추론됩니다 — 참조가 있는 함수 시그니처에서만 지정하면 됩니다.

rust
fn longest<'a>(x: &'a str, y: &'a str) -> &'a str {
    if x.len() > y.len() { x } else { y }
}

fn main() {
    let string1 = String::from("rust");
    let result;
    {
        let string2 = String::from("mobile");
        result = longest(string1.as_str(), string2.as_str());
        println!("Longest: {}", result);
    } // string2는 이 중괄호까지 살고, result는 내부에서 유효
    // println!("{}", result); // 오류: string2가 죽었습니다
}

Android 및 iOS 네이티브 모듈을 위한 Rust

Rust는 Android(.so, NDK 통해) 및 iOS(.a 정적 라이브러리)용 네이티브 라이브러리로 컴파일됩니다. Google은 AOSP(Android Open Source Project)에서 Rust를 공식 지원하며, Bluetooth(ava), DNS-over-HTTP3, CriticalNotification 구성 요소에 Rust가 사용됩니다. Google(2025)에 따르면 Android의 Rust는 대상 구성 요소에서 메모리 취약점을 70% 제거했습니다.

Android에서는 선택한 ABI에 맞는 올바른 툴체인으로 Cargo를 래핑하는 cargo-ndk가 사용됩니다. Android 빌드에는 aarch64-linux-android(64비트 ARM, 최신 기기), armv7-linux-androideabi(32비트 ARM), x86_64-linux-android(에뮬레이터)가 포함됩니다.

rust
// Cargo.toml — Android/iOS용 라이브러리 구성
[package]
name = "mobile_crypto"
version = "0.1.0"

[lib]
crate-type = ["staticlib", "cdylib"]

[dependencies]
serde = { version = "1", features = ["derive"] }
libc = "0.2"

crate-type: staticlib — iOS(.a)용, cdylib — Android(.so)용. serde — Kotlin/Swift와의 데이터 교환을 위한 직렬화. libc — FFI용 C 호환 타입.

예: Rust에서 이미지 처리

rust
use std::ffi::{CStr, CString};
use std::os::raw::c_char;

/// 이미지 처리를 위한 C 호환 함수
#[no_mangle]
pub unsafe extern "C" fn process_image(
    input: *const u8,
    len: usize,
    output: *mut u8,
    out_len: *mut usize,
) {
    let input_slice = std::slice::from_raw_parts(input, len);
    let result: Vec<u8> = input_slice.iter()
        .map(|b| b ^ 0xFF)
        .collect();
    let result_len = result.len();
    std::ptr::copy_nonoverlapping(result.as_ptr(), output, result_len);
    *out_len = result_len;
}

#[no_mangle]은 C 호환 ABI를 위한 네임 맹글링을 비활성화합니다. extern "C"는 C 호출 규칙을 지정합니다. unsafe — Rust 외부에서의 호출이 안전성을 보장하지 않음을 나타내는 마커입니다. std::ptr::copy_nonoverlapping은 결과 바이트를 출력 버퍼에 복사합니다.

Rust의 크로스플랫폼 SDK

Rust는 단일 코드베이스에서 Android와 iOS에서 작동하는 크로스플랫폼 SDK의 이상적인 후보입니다. Mozilla(Firefox), Dropbox(파일 동기화), Meta(Libra 블록체인)와 같은 회사는 모바일 애플리케이션의 중요한 구성 요소에 Rust를 선택했습니다.

C++ 대비 Rust 라이브러리의 장점: 컴파일러가 크로스플랫폼 코드에서 메모리 누수와 데이터 경합이 없음을 보장합니다. Rust로 한 번 작성하고 테스트한 코드는 모든 플랫폼에서 동일하게 작동합니다. Cargo는 CMake나 vcpkg 없이 종속성을 기본적으로 관리합니다.

라이브러리/Crate목적
tonicRust gRPC 클라이언트/서버
reqwestasync/await 지원 HTTP 클라이언트
ring암호화(AES, RSA, ECDSA)
sqlx비동기 SQLite, PostgreSQL
wasm-bindgenRust와 JavaScript/WASM 통합
ndk-glueRust의 Android NativeActivity
uniffiKotlin/Swift용 FFI 바인딩 생성

모바일 앱을 위한 Rust와 WebAssembly

Rust는 WebAssembly(Wasm)로 컴파일됩니다 — 브라우저와 모바일 기기에서 실행 가능한 바이너리 형식입니다. Wasm은 웹 환경에서 네이티브에 가까운 성능을 제공합니다. 모바일 개발에서 Wasm은 하이브리드 애플리케이션(WebView + Wasm)에서 복잡한 계산(3D 렌더링, 이미지 인식, 암호화)에 사용됩니다.

wasm-pack은 Rust를 .wasm으로 컴파일하고 JS 래퍼를 생성합니다. wasm-bindgen은 Rust 함수를 JavaScript 호출에 연결합니다. 모바일 기기에서 Wasm 러너는 WebView 내에서 또는 Wasmer/Wasmtime SDK를 통해 실행할 수 있습니다.

rust
use wasm_bindgen::prelude::*;

/// Wasm에서 피보나치 수 계산(JS에서 호출)
#[wasm_bindgen]
pub fn fibonacci(n: u32) -> u64 {
    match n {
        0 => 0,
        1 => 1,
        _ => {
            let mut a = 0;
            let mut b = 1;
            for _ in 2..=n {
                let temp = a + b;
                a = b;
                b = temp;
            }
            b
        }
    }
}

#[wasm_bindgen]은 fibonacci 함수의 JavaScript 래퍼를 생성합니다. 브라우저에서 호출은 wasm.fibonacci(10)처럼 보입니다. u32/u64 타입은 자동으로 Number/BigInt로 변환됩니다. 이러한 함수의 Wasm 모듈 크기는 약 5KB에 불과하여 최소한의 오버헤드입니다.

FFI: Kotlin과 Swift에서 Rust 호출

FFI (Foreign Function Interface) — 다른 언어에서 Rust 코드를 호출하는 메커니즘입니다. extern "C"와 #[no_mangle]이 있는 Rust 함수는 C 호환 ABI로 내보내집니다. Kotlin에서는 JNI를 통해, Swift에서는 @_cdecl("name") 또는 CBridge.h를 통해 호출됩니다.

UniFFI — FFI 바인딩을 생성하는 Mozilla 도구입니다. .udl 파일에 API를 설명하면 UniFFI가 자동으로 Kotlin과 Swift 래퍼를 생성합니다. 이는 Firefox Android와 Firefox iOS의 Rust 구성 요소에 대한 표준 접근 방식입니다.

rust
// UniFFI 인터페이스가 있는 Rust 라이브러리
uniffi::setup_scaffolding!();

#[derive(uniffi::Enum)]
pub enum EncryptionError {
    InvalidKey,
    EncryptionFailed,
}

#[derive(uniffi::Record)]
pub struct EncryptedData {
    pub ciphertext: Vec<u8>,
    pub nonce: Vec<u8>,
}

#[uniffi::export]
pub fn encrypt(key: &[u8], plaintext: &[u8]) -> Result<EncryptedData, EncryptionError> {
    // AES-256-GCM 구현
    todo!()
}

UniFFI는 ByteArray를 받아 EncryptedData를 반환하는 encrypt 메서드가 있는 Kotlin 클래스를 생성합니다. Swift에서는 ciphertext(Data)와 nonce(Data) 필드가 있는 EncryptedData 구조체가 생성됩니다. 오류는 Kotlin/Swift 예외로 변환됩니다.

Rust vs C++: 모바일 개발 비교

RustC++는 모바일 개발에서 시스템 프로그래밍을 위한 두 가지 주요 언어입니다. 둘 다 ARM용 네이티브 코드로 컴파일되며 C ABI를 통해 FFI를 지원합니다. 주요 차이점은 메모리 안전성 모델입니다: Rust는 컴파일 타임에 검사하고, C++는 개발자 규율에 의존합니다.

기준RustC++
메모리 안전성보장됨(ownership)개발자 책임
가비지 컬렉터없음(RAII + ownership)없음(수동 관리)
메모리 누수컴파일러가 방지가능(new에 delete 없음)
데이터 경합방지됨(Send + Sync)가능
Android NDKcargo-ndk 통해CMake / NDK Build 통해
iOS 지원cargo-lipo + .aXcode + .a
빌드 시스템Cargo(내장)CMake / Ninja
빌드 시간2~5분(클린)5~15분(클린)
생태계crates.io(20만+ 크레이트)vcpkg / Conan(10만+ 라이브러리)

Rust는 C++보다 안전하고 생산적입니다 — 컴파일러가 C++에서는 런타임에 나타나는 오류를 잡아냅니다. C++는 더 풍부한 모바일 라이브러리 생태계를 갖추고 있습니다. 새 프로젝트에는 Rust가 선호되며, 기존 C++ 코드는 점진적으로 포팅됩니다.

자주 묻는 질문

모바일 개발에서 Rust는 C++와 어떻게 다른가요?

주요 차이점은 컴파일 타임 메모리 안전성입니다. Rust에는 소유권 시스템과 borrow checker 덕분에 nullptr, 댕글링 포인터, 데이터 경합이 없습니다. C++는 개발자 규율에 의존하고, Rust는 컴파일러 검사에 의존합니다.

Android 개발에 Rust를 사용할 수 있나요?

네, Android NDK를 통해 사용할 수 있습니다. Google은 AOSP(Android Open Source Project)에서 Rust를 공식 지원합니다. Rust는 cargo-ndk를 통해 .so 라이브러리로 컴파일됩니다. JNI를 통한 FFI는 C++와 동일하게 작동합니다.

cargo란 무엇이며 Rust 개발에 어떻게 도움이 되나요?

Cargo는 Rust의 패키지 관리자이자 빌드 시스템입니다. crates.io에서 종속성을 다운로드하고, 프로젝트를 컴파일하며, 테스트를 실행합니다. 모바일 개발에서 cargo-ndk는 Android ABI용 크로스 컴파일을 위해 Cargo를 확장합니다.

Rust는 iOS에도 적합한가요, 아니면 Android 전용인가요?

Rust는 두 플랫폼을 모두 지원합니다. iOS의 경우 cargo-lipo를 사용하여 유니버설 바이너리(arm64 + x86_64 시뮬레이터)를 만듭니다. Rust 코드는 정적 .a 라이브러리로 컴파일되어 Xcode를 통해 링크됩니다.

2026년에 모바일 개발을 위해 Rust를 배울 가치가 있나요?

네, Rust는 크로스플랫폼 SDK와 네이티브 모듈의 표준이 되어가고 있습니다. Google, Meta, Mozilla가 Rust에 투자하고 있습니다. 성능 중요 구성 요소의 경우 Rust는 C++보다 안전하고 생산적입니다.

요약

  • Rust — ownership, borrowing, lifetimes으로 GC 없이 메모리 안전성을 제공하는 시스템 언어
  • Borrow checker가 컴파일 타임에 데이터 경합과 댕글링 포인터를 방지
  • 네이티브 모듈은 cargo-ndk(Android .so)와 cargo-lipo(iOS .a)로 빌드
  • UniFFI가 Kotlin과 Swift용 FFI 바인딩 생성 — Mozilla 표준 모바일 SDK 방식
  • WebAssembly로 모바일 WebView에서 Rust를 네이티브에 가까운 성능으로 실행
  • Google이 AOSP에서 Rust 사용 — 대상 구성 요소의 메모리 취약점 70% 감소
  • Rust vs C++: Rust는 더 안전, C++는 더 풍부한 생태계; 새 네이티브 모듈에는 Rust 선호

턴키 방식의 모바일 애플리케이션을 개발해 드립니다

IT Sectr는 2017년부터 스타트업과 기업을 위한 iOS 및 Android 애플리케이션을 만듭니다. 저희가 상담해 드리고 최적의 솔루션을 제안하겠습니다.

프로젝트 논의

더 읽어보기