현재 재직하고 있는 회사에서 내가 맡은 업무는 신분증 OCR, 사본 판별 솔루션 개발을 하고 있다. 지금은 물론 자주 들어서 JNI에 대해서는 대략적인 느낌으로 아는 것은 있지만, 처음 듣고서 기능의 개발을 해야 할 때는 진짜 막막했다... ㅜㅜ 지금도 개념은 모르는데 그냥 어찌저찌 구선생(?) 님을 통해서 열심히 해결하고 있지만 이번 기회에 정리해 보고자 한다.
먼저 현재 재직하고 있는 회사의 주요 고객층은 금융업에 속해있는 회사들이 많다. 이 회사들의 가장 불편한 공통점은 엄청 레거시한 언어와 버전으로 프로젝트들을 관리한다... AI가 유행하는 지금 Python을 사용하는 고객사를 아직 보지를 못했다... Java도 20버전대가 나온 지가 언젠데 나는 아직 회사에서 8버전 위로 올라가 본 적이 없다... ㅠㅠ(개인 프로젝트만 최신 버전으로 사용해서 하고 있다!! 이렇게해도 나중에 이직은 가능하겠지...???)
그래서 대부분의 고객사에 납품할 수 있는 형식이 매우 제한되어 있는데 현 회사는 AI 모델을 onnx라는 것으로 변환하고 그걸 Java와 연결해주는 so 라이브러리 라는 것을 만들어 Java에서도 AI 모델을 사용할 수 있도록 해준다. 그때 그 연결을 도와주는 것이 JNI라는 것이다. 이제 한번 JNI를 자세하게 알아보도록 해보자. 대략 그림으로 표현하면 아래와 같다.(절대 정답은 아닙니다!!)

JNI(Java Native Interface)는 Java 코드와 다른 프로개르밍 언어(주로 C/C++)로 작성된 네이티브 코드 간의 상호 작용을 가능하게 하는 인터페이스이다. Java가 JVM 위에서 실행되면서 네이티브 시스템 기능에 직접 접근하기 어려울 때, JNI를 통해 네이티브 코드를 호출하여 성능 최적화나 하드웨어 접근을 구현할 수 있다.
"출처 위키백과"
| Java | C++/C | C++/C 배열(참조 자료형) |
|---|---|---|
| boolean | jboolean | jbooleanArray |
| byte | jbyte | jbytArray |
| char | jchar | jcharArray |
| short | jshort | jshortArray |
| int | jint | jintArray |
| long | jlong | jlongArray |
| float | jfloat | jfloatArray |
| double | jdouble | jdoubleArray |
| Java | C++/C |
|---|---|
| Object | jobject |
| Class | jclass |
| String | string |
| Array | jarray |
| C++/C | Description |
|---|---|
| jthrowable | 자바 예외를 나타내는 타입 |
| jmethodID, jfieldID | 메서드나 필드의 식별자를 나타내는 타입 |
| signature | Java Type |
|---|---|
| B | byte |
| C | char |
| D | double |
| F | float |
| I | int |
| J | long |
| S | short |
| V | void |
| Z | boolean |
| L+ {클래스의 패키지 + 클래스 명} | 해당 클래스 ex. String -> Ljava/lang/String; |
| [+{클래스 시그니처} | 배열 ex. int[] -> [I |
public class JniTest {
native void printHello();
native void printString(String str);
static {
System.loadLibrary("jniTest");
}
}
/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class JniTest */
#ifndef _Included_JniTest
#define _Included_JniTest
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class: JniTest
* Method: printHello
* Signature: ()V
*/
JNIEXPORT void JNICALL Java_JniTest_printHello
(JNIEnv *, jobject);
/*
* Class: JniTest
* Method: printString
* Signature: (Ljava/lang/String;)V
*/
JNIEXPORT void JNICALL Java_JniTest_printString
(JNIEnv *, jobject, jstring);
#ifdef __cplusplus
}
#endif
#endif
#include "JniTest.h"
JNIEXPORT void JNICALL Java_JniTest_printHello(JNIEnv *env, jobject obj)
{
printf("Hello World\n");
return;
}
//C Version
#include <stdio.h>
JNIEXPORT void JNICALL Java_JniTest_printString(JNIEnv *env, jobject obj, jstring string)
{
const char *str = (*env)->GetStringUTFChars(env, string, 0);
printf("%s!\n", str);
return;
}
//C++ Version
#include <iostream>
JNIEXPORT void JNICALL Java_JniTest_printString(JNIEnv* env, jobject obj, jstring string) {
const char* str = env->GetStringUTFChars(string, 0);
std::cout << str << "!" << std::endl;
env->ReleaseStringUTFChars(string, str);
}





public class JniMain {
public static void main(String[] args) {
JniTest jniTest = new JniTest();
jniTest.printHello();
jniTest.printString("jni test success;");
}
}


물론 내가 배움의 속도가 느린 것도 있겠지만.... 이건 너무 레거시한거 아니냐고!!! JniTest.h 파일 빌드하는데 경로 상의 문제인지 모르고 이것저것 수정하다 보니까 2일을 허비해서.... 이번에 글을 업로드하는 시간이 너무 오래 걸렸다... 그래도 이번 기회에 JNI랑 조금 친해(?) 졌으니 다음 기회에는 진짜 내가 알고 싶었던 JNI를 왜 사용해야 하는지? 그리고 꼭 JNI만을 사용해야 하는지를 알아보자!!
핑계 아닌 핑계로 최근에 회사가 너무 바쁘기도 했고.... 여행도 가야 해서 공부할 시간이 없어 글을 작성할 시간이 없었는데 다녀온 뒤로는 이런저런 핑계 없이 힘내서 글을 열시미 써보자!!!