기본적으로 go
에서 외부 라이브러리를 동적으로 (.so
.dll
) 로드할 때 cgo
패키지를 사용합니다.
go
로 작성된 외부 라이브러리를 동적으로 로드 해주는 기본 패키지가 존재합니다.
Plugin
패키지는 go
로 작성된 라이브러리를 동적으로 임포트하는 패키지 입니다.
플러그인 만들기
플러그인을 작성할 파일을 생성합니다. 플러그인은 go
의 기본 타입(string, int...) 와 함수를 전달할 수 있습니다.
$ touch plugin.go
플러그인을 작성합니다.
package main
// 사용하지 않지만, 없으면 컴파일이 진행되지 않음
func main() {}
// 값
var SomeVal = "hello"
// 함수
func SomeFunc() { println("hello") }
플러그인에서 노출할 값과 함수를 정의합니다. main
패키지에서 작성하며, main
함수는 사용하지 않으나, 미 작성 시 컴파일이 되지 않습니다.
플러그인을 컴파일 합니다.
# Linux
$ go build -buildmode=plugin -o plugin.so plugin.go
# Windows
$ go build -buildmode=plugin -o plugin.dll plugin.go
플러그인 로드하기
플러그인을 사용할 파일을 생성합니다.
$ touch loader.go
package main
import "plugin"
func main() {
// 플러그인 열기
plug, err := plugin.Open("plugin.so") //plugin.dll
if err != nil {
println(err.Error())
return
}
// SomeVal 찾기
symSomeVal, err := plug.Lookup("SomeVal")
if err != nil {
println(err.Error())
return
}
// 형 변환
// 포인터 형태로 값이 전달됨
if SomeVal, ok := symSomeVal.(*string); !ok {
println("SomeVal casting err")
return
} else {
println(*SomeVal)
}
// SomeFunc 찾기
symSomeFunc, err := plug.Lookup("SomeFunc")
if err != nil {
println(err.Error())
return
}
// 형 변환
if SomeFunc, ok := symSomeFunc.(func()); !ok {
println("SomeFunc casting err")
return
} else {
SomeFunc()
}
}
컴파일
$ go build loader.go
실행
$ ./loader
hello
hello
구조체 전달
플러그인의 값은 main
패키지에서 정의됩니다.
go
의 main
패키지는 외부에서 패키지를 읽거나 볼 수 없습니다.
정확히는, plugin
의 main
패키지 안에 있는 구조체는 전달이 되나, loader
에서 plugin
의 main
패키지에 접근 할 수 없기에, 캐스팅에서 오류가 발생합니다.
panic: interface conversion: plugin.Symbol is *main.SomeStruct, not *loader.SomeStruct [recovered]
panic: interface conversion: plugin.Symbol is *main.SomeStruct, not *loader.SomeStruct
loader
에서 같은 형태의 구조체를 정의해도 위 오류가 나타납니다.
기본적으로 main
패키지는 타 패키지가 간섭할 수 없습니다.
구조체 뿐만 아니라 type
키워드로 정의한 커스텀 타입은 사용할 수 없습니다.
이러한 문제를 해결하려면 구조체를 외부 패키지에 정의하여 사용하면 됩니다.
구조체 정의 패키지
package some_struct
type SomeStruct struct {
Name: string
}
func (s *SomeStruct) Hi() {
println("Hi " + s.Name)
}
플러그인
package main
import "some_struct"
var V = some_struct.SomeStruct{Name: "apple"}
func main() {}
로더
package main
import "plugin"
import "some_struct"
func main() {
p, err := plugin.Open("plugin/path/plugin.so")
if err != nil {
return nil, err
}
s, err := p.Lookup("V")
if err != nil {
println(err.Error())
return
}
if apple, ok := s.(*some_struct.SomeStruct); !ok {
println("casting error")
return
}
println(apple.Name)
apple.Hi()
}
예시
플러그인 예시
리플랙션을 사용하여 플러그인을 쉽게 만들기 위한 라이브러리