기능 작성을 완료한 zzz 서버를 시작하려 했는데, 다음 에러가 발생했다.
panic: proto: file "entpb/entpb.proto" is already registered
previously from:"/server/services/xxx/pkg/ent/proto/entpb"
currently from: "/server/services/yyy/pkg/ent/proto/entpb"
See https://protobuf.dev/reference/go/faq#namespace-conflict
zzz 서버는 xxx 서버와 yyy 서버의 protobuf
가 필요한 상황이다.
이전까지 겪지 않다가 처음으로 겪어본 문제였다. 왜냐하면 이전까지는 한 서버는 다른 서버 하나의 protobuf
만 필요했었지만, 이번에 처음으로 2개 이상의 protobuf
가 필요하게 됬기 때문이다.
위 자료 중, 2번 자료가 중요하다. 특히 다음 구절...
There is no correlation between the Go import path and the package specifier in the .proto file.
The latter is only relevant to the protobuf namespace,
while the former is only relevant to the Go namespace.
Also, there is no correlation between the Go import path and the .proto import path.
프로토콜 버퍼 컴파일러인 protoc 입장에서는 golang import path
는 알 바 아니고, .proto
파일의 import path
가 중요하다.
즉, 다음 두 경로는 golang import path
는 다르지만, .proto
파일의 import path
는 똑같은 entpb
이기 때문에 충돌이 발생하는 것이다.
"/server/services/xxx/pkg/ent/proto/entpb"
"/server/services/yyy/pkg/ent/proto/entpb"
https://pkg.go.dev/entgo.io/contrib/entproto@v0.6.0#section-readme
golang ORM
으로 entgo
를 사용하고 있다. 그리고 스키마 생성할 때 자동으로 protobuf를 생성할 수 있도록 entproto
를 같이 사용 중이다.
해결 방법은 다음과 같다.
func (MessageWithPackageName) Annotations() []schema.Annotation {
return []schema.Annotation{entproto.Message(
entproto.PackageName("something"),
)}
}
위처럼 작성하게 되면 .proto
파일이 PackageName
에 기입된 디렉토리 내에 위치하게 된다. 뿐만 아니라 .proto
도 entproto.proto
가 아니게 된다.
위 코드를 예로 들면, entpb/something
패키지 내에 something.proto
가 생긴다.
go generate
라는 명령어를 수행하게 되면, generate.go
가 자동으로 생성된다. 또한 .proto
파일 역시 아직 1.의 packageName
에 종속된다.
.proto
를 원하는 디렉토리로 위치시키고, generate.go
를 생성하지 않게 하려면 다음과 같이 entc.go
를 수정하면 된다.
//go:build ignore
package main
import (
"entgo.io/contrib/entproto"
"log"
"entgo.io/contrib/entgql"
"entgo.io/ent/entc"
"entgo.io/ent/entc/gen"
)
func main() {
ex, err := entgql.NewExtension(
)
entProtoExtension, err := entproto.NewExtension(
entproto.SkipGenFile(),
entproto.WithProtoDir(
"../../something/",
),
)
opts := []entc.Option{
entc.Extensions(
ex,
entProtoExtension,
),
}
if err := entc.Generate("./schema", &gen.Config{}, opts...); err != nil {
log.Fatalf("running ent codegen: %v", err)
}
}
entproto.SkipGenFile()
를 활용하면, .proto
를 생성하는generate.go
가 자동 생성되는 걸 막을 수 있다. 단, 이 기능을 활용하려면 최신 버전의 entproto
가 필요하다.
entproto.WithProtoDir()
를 활용하면, .proto
가 생성되는 위치를 프로그래머 마음대로 할 수 있게 된다.
다음은 makefile에 작성된 명령어 집합이다.
./pkg/ent/schema
에서 스키마를 읽어온 후, PROTOBUF_DIR
에 존재하는 ~~.proto
를 이용하여 컴파일을 실행한다. 그리고 OUT_DIR
에 grpc용 코드, grpc 서비스용 코드, .go 코드를 생성해준다.
PROTOBUF_DIR=../../protobuf
OUT_DIR=pkg/ent/proto/entpb
proto:
@mkdir -p $(OUT_DIR)
@find $(PROTOBUF_DIR)/xxx -name '*.proto' | xargs -I {} protoc -I=$(PROTOBUF_DIR) --go_out=$(OUT_DIR) --go_opt=paths=source_relative \
--go-grpc_out=$(OUT_DIR) --go-grpc_opt=paths=source_relative {} \
--entgrpc_out=$(OUT_DIR) --entgrpc_opt=paths=source_relative,schema_path=./pkg/ent/schema