gRPC Include path

오늘 회사서 씨름한 것 중 하나다.

file not found, file does not reside within, protoc is too dumb …

위와 같은 디버깅 메세지가 나오는데 아주 불친절하다. 그래서 뭐 어쩌라고 ?

$ python -m grpc_tools.protoc -I include-path --python_out=pb2.py-out --grpc_python_out=pb2_grpc.py-out proto_file

대강 위와 같이 proto를 컴파일 하는데, 우선 구조는 아래와 같다.

  • project
    • A
      • A1
        • protoA1-path
      • A2
        • protoA2-path
    • B
      • A
        • A1
          • protoA1.proto
        • A2
          • protoA2.proto
    • compile.py

하나의 프로토에 다음과 같은 내용이 있었다. 설명하자면, protoA2.protoA/A1/protoA1.proto를 import 하였다.

project / B / A / A1 / protoA1.proto

syntax = "proto3";

package A.A1;

message test {

}

message empty {

}

project / B / A / A2 / protoA2.proto

syntax = "proto3";

import "A/A1/protoA1.proto";

service X {
    rpc Y (A.A1.test) returns (A.A1.empty) {}
}

위와 같이 protoA2.proto를 작성한 뒤 compile.py 안에서 컴파일 커맨드에 -I 인자를 아래처럼 주어 실행하도록 만들었다.

$ python -m grpc_tools.protoc -I B/ --python_out=A/A2 --grpc_python_out=A/A2 B/A/A2/protoA2.proto

이러니까 웃기게도 B하위 A부터의 구조가 그대로 project / A / A2밑으로

  • project
    • A
      • A2
        • A
          • A2
            • protoA2_pb2.py
            • protoA2_pb2_grpc.py

이렇게 들어간다. 그 얘기는 -I 옵션으로 준 B 디렉터리 하위 구조를 그대로 가져온다는 말이다. 그러니까 -I에 root 디렉터리 옵션을 준다고 생각하면 된다. 물론 이거는 project 바로 밑에 그대로 B의 구조를 만들어 컴파일 하고 싶은 경우다. 어찌됐건 –python_out과 –grpc_python_out에 우리는 _pb2.py**와 **_pb2_grpc.py 의 타겟 디렉토리를 쓰는게아니라 컴파일 대상 .proto파일의 상위 구조를 생각하고 써야된다는 말이다.

즉 방금 내가 테스트한 커맨드라인을 쓰자면 아래와 같다

python -m grpc_tools.protoc -I protobuf/ --python_out=. --grpc_python_out=. protobuf/test_proto/test_B/B.proto

맨 마지막 input파일의 root는 프로젝트 폴더일 것이고 그 바로 밑에 protobuf라는 폴더안에 다음과 같이

  • protobuf/test_proto/test_B/B.proto
  • protobuf/test_proto/test_A/A.proto

를 만들어 두었고 B가 A를 import하고 있을 때

  • project/test_proto/test_B/B_pb2_*.py

를 만들고 싶다면 위 커맨드 대로 컴파일 하면 되는 것이다.

그런데 이건 안된다.

(grpc) $ python -m grpc_tools.protoc -I protobuf/test_proto/ --python_out=test_proto/ --grpc_python_out=test_proto/ protobuf/test_proto/test_B/B.proto
test_proto/test_A/A.proto: File not found.
test_B/B.proto: Import "test_proto/test_A/A.proto" was not found or had errors.
test_B/B.proto:6:12: "test_proto.test_A.test" is not defined.
test_B/B.proto:6:45: "test_proto.test_A.empty" is not defined.

보면 B.proto 에서 우리는 A/A1/A1.proto를 import한다고 해놓고 컴파일러에 import path를 B/A 를 줬다.

A가 겹치는 셈이고 이건 파일을 B / A / A / A1 / A1.proto에서 찾겠다는 말이니 당연히 안된다. 그러니 import를 다음과 같이 바꿔줘야된다. 그래야 import에쓴 A1/protoA1.proto를 정상적으로 찾는다.

syntax = "proto3";

import "A1/protoA1.proto";

service X {
    rpc Y (A.A1.test) returns (A.A1.empty) {}
}

**정리하자면, import 에는 relative path를 쓴다. 그럼 이 relative path에 바로 상위 부터를 root path라하면 이 root path를 protoc의 -I 옵션의 인자로 넣어줘야된다. **

그러니 어느 정도 구조를 맞춰놓고 import문을 다음과 같이쓰고, input으로 project하위 full path를 줘버리고 project폴더를 루트로 하여 –python_out과 –grpc_python_out에 . 을 줘버리는게 아주 마음이 편하다.

syntax = "proto3";

import "A/A1/protoA1.proto";

service X {
    rpc Y (A.A1.test) returns (A.A1.empty) {}
}
python -m grpc_tools.protoc -I protobuf/ --python_out=. --grpc_python_out=. protobuf/test_proto/test_B/B.proto