ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 안드로이드 NDK 핵심 정리 — C/C++ 네이티브 코드 연동 가이드
    카테고리 없음 2026. 4. 9. 21:23

    Android NDK란?

    Android NDK(Native Development Kit)는 C 및 C++ 코드를 Android 앱에 직접 통합할 수 있게 해주는 공식 도구 모음입니다.
    Java/Kotlin 기반의 Android SDK와 달리, NDK는 네이티브 코드를 통해 하드웨어 자원에 직접 접근하거나 고성능 연산을 처리할 때 활용됩니다.
    특히 게임 엔진, 영상 처리, 오디오 DSP, 머신러닝 추론 등 성능이 중요한 영역에서 필수적으로 사용됩니다.

    NDK를 사용하는 이유

    Android 앱 개발의 대부분은 Java 또는 Kotlin으로 이루어지지만, 특정 상황에서는 NDK가 큰 이점을 제공합니다.
    C/C++로 작성된 기존 라이브러리나 엔진을 재사용할 수 있어 개발 비용을 크게 줄일 수 있습니다.
    또한 JVM의 오버헤드 없이 직접 메모리를 관리하고 CPU 집약적 연산을 처리할 수 있어 전반적인 앱 성능이 크게 향상됩니다.

    주요 사용 사례:

    • 게임 엔진 (Unity, Unreal Engine 등)
    • 음성/영상 코덱 및 실시간 처리
    • 암호화 및 보안 모듈
    • OpenCV 등 컴퓨터 비전 라이브러리 연동
    • 물리 시뮬레이션 및 수치 연산

    NDK 개발 환경 설정

    Android Studio에서 NDK를 사용하려면 먼저 SDK Manager에서 NDK 컴포넌트를 설치해야 합니다.
    설치 후 build.gradle에서 NDK 버전과 ABI 필터를 설정하면 빌드 시 자동으로 네이티브 라이브러리가 컴파일됩니다.
    NDK 빌드 시스템으로는 CMake가 공식 권장 방식이며, CMakeLists.txt 파일에 소스 파일과 라이브러리 구성을 정의합니다.

    android {
        defaultConfig {
            ndk {
                abiFilters "arm64-v8a", "x86_64"
            }
        }
        externalNativeBuild {
            cmake {
                path "src/main/cpp/CMakeLists.txt"
            }
        }
    }

    위 설정에서 abiFilters는 빌드할 대상 CPU 아키텍처를 지정하며, 실제 배포 대상 기기에 맞게 조정하는 것이 좋습니다.
    externalNativeBuild 블록은 CMake 설정 파일 경로를 지정하여 Gradle이 네이티브 빌드를 자동으로 처리하도록 합니다.

    JNI(Java Native Interface) 이해하기

    NDK 개발의 핵심은 JNI(Java Native Interface)입니다.
    JNI는 Java/Kotlin 코드와 C/C++ 코드를 연결하는 브릿지 역할을 하며, 함수 이름 규칙을 통해 양측 코드가 서로를 호출할 수 있게 합니다.
    JNI 함수명은 Java_패키지명_클래스명_메서드명 형태로 작성해야 하며, 이 네이밍 규칙을 정확히 지켜야 런타임에 함수를 올바르게 찾을 수 있습니다.

    Kotlin 쪽 선언:

    class NativeHelper {
        external fun stringFromJNI(): String
    
        companion object {
            init {
                System.loadLibrary("native-lib")
            }
        }
    }

    C++ 쪽 구현:

    #include <jni.h>
    #include <string>
    
    extern "C" JNIEXPORT jstring JNICALL
    Java_com_example_myapp_NativeHelper_stringFromJNI(
            JNIEnv* env,
            jobject /* this */) {
        std::string hello = "Hello from C++!";
        return env->NewStringUTF(hello.c_str());
    }

    extern "C"는 C++ 이름 맹글링(name mangling)을 방지하여 JNI가 함수를 정확히 인식하게 합니다.
    JNIEnv*는 Java 환경 포인터로, Java 객체 생성, 문자열 변환, 예외 처리 등 모든 JNI 작업에 사용됩니다.

    CMakeLists.txt 작성 방법

    CMake는 NDK 프로젝트의 빌드 구성 파일로, 라이브러리 이름, 소스 파일 경로, 링크할 시스템 라이브러리 등을 정의합니다.
    Android Studio가 이 파일을 기반으로 네이티브 코드를 빌드하며, 여러 소스 파일이나 서드파티 라이브러리도 이 파일에서 통합 관리합니다.
    find_library를 통해 Android 시스템 라이브러리(log, android, EGL, GLESv2 등)를 쉽게 연결할 수 있습니다.

    cmake_minimum_required(VERSION 3.22.1)
    
    project("myapp")
    
    add_library(
        native-lib
        SHARED
        native-lib.cpp
    )
    
    find_library(
        log-lib
        log
    )
    
    target_link_libraries(
        native-lib
        ${log-lib}
    )

    add_librarySHARED 옵션은 동적 라이브러리(.so 파일)를 생성하며, Android 앱이 런타임에 로드하는 방식입니다.
    target_link_libraries에서 여러 라이브러리를 나열하면 네이티브 코드에서 해당 API를 모두 활용할 수 있습니다.

    NDK 개발 시 주의사항

    NDK를 사용하면 성능 이점이 있지만, 몇 가지 중요한 주의사항을 반드시 숙지해야 합니다.
    C/C++ 코드는 메모리 관리를 직접 해야 하므로 메모리 누수나 버퍼 오버플로우 같은 버그에 취약합니다.
    또한 ABI 호환성 문제로 인해 다양한 기기에서 철저히 테스트하는 것이 필수입니다.

    • ABI 필터: arm64-v8a, armeabi-v7a, x86, x86_64 등 대상 기기 아키텍처를 명확히 지정해야 합니다.
    • 메모리 관리: new/delete 또는 스마트 포인터(unique_ptr, shared_ptr)를 사용해 메모리 누수를 방지합니다.
    • JNI 레퍼런스: LocalRef, GlobalRef를 적절히 관리하지 않으면 메모리 누수가 발생하므로 사용 후 반드시 해제해야 합니다.
    • 스레드 안전성: JNIEnv는 스레드별로 독립적이므로 다른 스레드와 공유하면 안 됩니다.
    • 예외 처리: C++ 예외가 Java 레이어로 자동 전달되지 않으므로 별도의 에러 핸들링 로직이 필요합니다.

    마무리

    Android NDK는 고성능 앱 개발이나 기존 C/C++ 코드베이스를 재활용할 때 매우 강력한 도구입니다.
    JNI 브릿지와 CMake 빌드 시스템의 기본 구조를 이해하면 복잡한 네이티브 코드 연동도 체계적으로 구현할 수 있습니다.
    처음 NDK를 도입할 때는 작은 기능부터 시작해 점진적으로 확장하는 방식을 권장하며, 공식 Android NDK 가이드를 꾸준히 참고하면 최신 Best Practice를 유지하는 데 도움이 됩니다.

Designed by Tistory.