[Go] Nodejs에서 Go 코드 연동해보기
프로그램은 되도록 한가지의 언어로 작성하는게 좋고 편하긴 하지만, 경우에 따라서는 각 언어의 기능과 에코시스템을 최대한 활용하는 편이 효율이 훨씬 좋을 수가 있다.
근데 그렇다고 또 무작정 MSA 형태로 서비스를 올려서 쓰는건 좋은 선택이라고 말하긴 어렵다.
막 그렇게 복잡한 케이스가 아니라면 FFI를 이용해서 연동하는 것도 하나의 방법일 수 있다.
일단, Go는 C FFI를 지원하기 때문에 C언어 형태로 코드를 내보내는 것이 가능하다.
그리고 Nodejs는 C++ 코드를 애드온하는 기능을 제공한다.
이 두 기능을 사용해서 붙이면 사용이 가능할 것이다.
Go 프로젝트 구성
관련 포스트
https://blog.naver.com/sssang97/222959089158
우선 디렉터리 파고, mod를 초기화해준다.

그다음에 메인 패키지에 소스코드를 때려박는다.
package main
import "C"
import (
"fmt"
"strconv"
)
//export Add
func Add(a int, b int) int { return a + b }
//export Sub
func Sub(a int, b int) int { return a - b }
//export Mul
func Mul(a int, b int) int { return a * b }
//export Div
func Div(a int, b int) int { return a / b }
//export PrintToConsole
func PrintToConsole(msg string) {
fmt.Println(msg)
}
func main() {
result := Sub(Add(1, 2), 10)
PrintToConsole(strconv.Itoa(result))
}
소스코드가 통짜인 것은 이유가 있다.
원래는 나도 저 함수 구현들을 서브패키지로 빼려고 했는데, 그러면 그걸 export할 수가 없다.
cgo는 main 패키지에서만 export를 할 줄 알고, go에는 import를 다시 export를 하는 기능이 없기 때문이다.
환장할 노릇이다.
아무튼 하나씩 짚어보자.
일단 main 패키지여야 하고, C 모듈을 포함시켜야 한다.
그래야먄 헤더파일을 생성해준다.
그리고 내보낼 함수들을 정의해준다.
당연히 대문자로 시작해야 하고, 주석으로 export될 함수임을 명시해줘야 한다.
그래서 일단
실행은 된다.
C로 내보내기
도는건 확인했으니 C 포맷으로 export를 해보자.
다음 처럼 옵션을 줘서 컴파일을 하면 된다.
go build -o libgoffi.so -buildmode=c-shared go/main.go // Linux

그럼 외부에서 갖다쓸 수 있는 공유 라이브러리(.so)와 헤더파일을 던져줄 것이다.
이걸 주워가서 쓰면 된다.
다만 문제는... dll로 내보내는 기능이 없다는 것이다.
그래서 windows에서는 활용하기 어렵다.
NodeJS에서 갖다쓰기
원래 Node.JS에서 공식적으로 제공하는 FFI는 애드온이다.
https://blog.naver.com/sssang97/221994411876
근데 이걸 쓰려면 또 so를 가져와서 애드온 형태로 감싸주는 정신나간 짓들을 해야 하기 때문에, 여기서는 좀더 편리한 방법을 택하겠다.
다행스럽게도 ffi를 스크립트만으로 간단하게 로드하는 모듈이 있다.
깔아주자.
npm i ffi-napi ref-struct-napi
그리고 이런식으로 호출해서 사용하면 된다.
const ffi = require("ffi-napi");
const Struct = require("ref-struct-napi");
const GoString = Struct({
p: "string",
n: "longlong",
});
const goffi = ffi.Library("./libgoffi.so", {
Add: ["longlong", ["longlong", "longlong"]],
Sub: ["longlong", ["longlong", "longlong"]],
Mul: ["longlong", ["longlong", "longlong"]],
Div: ["longlong", ["longlong", "longlong"]],
PrintToConsole: ["void", [GoString]],
});
console.log("Add(12, 99) = ", goffi.Add(12, 99));
console.log("Sub(1, 555) = ", goffi.Sub(1, 555));
str = new GoString();
str["p"] = "Hello Node!";
str["n"] = 11;
goffi.PrintToConsole(str);
문자열 타입을 GO에 맞춰서 정의해주고

so 파일을 로드하면서 가져올 항목들을 다시금 정의한다.
배열의 첫번째 항목이 리턴타입이고, 두번째 배열이 파라미터 타입이다.
그러면 이제 그대로 사용할 수가 있다.
실행하면 된다.
다행스럽게도 잘 돈다.
참조
https://stackoverflow.com/questions/30951751/how-to-make-nodejs-to-talk-with-golang
https://medium.com/learning-the-go-programming-language/calling-go-functions-from-other-languages-4c7d8bcc69bf