protoc-gen-doc으로 시작하는 gRPC 문서화
[kt cloud 플랫폼Innovation팀 강솔 님]
protoc-gen-doc으로 시작하는 gRPC 문서화
이번 포스팅에서는 gRPC 기반 API의 효과적인 문서화 방안에 대해 다뤄보겠습니다. 기존에 REST API는 주로 Swagger를 활용해 문서화했는데, gRPC에서도 유사한 수준의 명확하고 일관된 문서를 제공하기 위한 방법을 고민하게 되었습니다.
gRPC 문서화를 설계하면서 특히 중점적으로 고려한 요소는 1) 편의성과 2) .proto 파일과의 호환성이었습니다. 내부 공유용으로 활용될 문서이기에, 큰 리소스를 투입하지 않고도 최신의 .proto 파일 내용을 반영할 수 있는 방안을 찾고자 했습니다.
이와 같은 요구 사항을 충족하기 위해 protoc-gen-doc을 사용하여 .proto 파일 기반의 자동화된 문서화를 구현했습니다.
1.protoc-gen-doc란?
1.1. protoc-gen-doc 주요 특징
protoc-gen-doc은 go 언어 기반의 .proto 파일 문서화 플러그인입니다. protoc컴파일러와 함께 이 플러그인을 설정하여 .proto 파일에서 정의한 gRPC 메세지 및 서비스를 HTML, Markdown, Json 형식 문서로 변환 가능합니다.
Swagger와의 비교
Swagger처럼 UI를 통한 인터렉티브 테스트를 제공할 수는 없지만, .proto 파일로 정의한 메세지 및 서비스들을 플러그인을 통해서 편리하게 문서화할 수 있기 때문에 내부 문서 공유용으로 빠르게 문서를 최신 상태로 유지하기 용이합니다. 이제 설치부터 문서 생성 과정까지 단계별로 살펴보겠습니다.
1.2. protoc-gen-doc 설치하기
protoc-gen-doc의 경우, Go 언어로 작성된 protoc 컴파일러 기반의 문서화 플러그인입니다. 따라서 Go 환경 설치와 protoc 컴파일러 설치가 선행되어야만 합니다.
1. Go 설치
a. Windows
1) Go 다운로드 및 설치 - Go Download
1. Go 공식 다운로드 페이지에서 Windows용 설치 파일(.msi)을 다운로드합니다.
2. 설치 파일을 실행하고, 안내에 따라 설치합니다.
2) 환경 변수 설정 확인
1. 설치가 완료되면, 환경 변수에 Go 설치 경로(C:\Go\bin)가 자동으로 추가됩니다.
2. CMD 또는 PowerShell에서 아래 명령어를 통해 Go가 정상적으로 설치되었는지 확인합니다.
go version |
b. macOS
1) Go 설치
1. Go 공식 다운로드 페이지에서 macOS용 설치 파일(.pkg)을 다운로드합니다.
2. 설치 파일을 실행하고, 안내에 따라 설치합니다.
2) 환경 변수 설정
- macOS에서는 기본적으로 /usr/local/go/bin에 설치되며, 환경 변수 설정이 자동으로 추가됩니다.
- 설치 확인:
go version |
2 . protoc 설치
a. Windows
1) Protocol Buffers 다운로드
- Protocol Buffers Releases 페이지에서 최신 버전의 Windows용 zip 파일(protoc-<version>-win64.zip)을 다운로드합니다.
2) 압축 해제 및 설치
- 압축을 해제하여 protoc.exe를 원하는 디렉터리에 복사합니다. 예를 들어, C:\Program Files\Protoc에 설치할 수 있습니다.
3) 환경 변수 설정
1. C:\Program Files\Protoc를 시스템 환경 변수 PATH에 추가합니다.
2. 시스템 속성 → 고급 시스템 설정 → 환경 변수에서 PATH에 추가합니다.
4) 설치 확인
protoc --versio |
b. macOS
1) Homebrew를 사용한 설치 - Homebrew 패키지 사용
brew install protobuf |
protoc --version |
3. protoc-gen-doc 설치
a. Windows
1) Go 패키지 관리 명령을 사용하여 protoc-gen-doc 설치
go install github.com/pseudomuto/protoc-gen-doc/cmd/protoc-gen-doc@latest |
2) 환경 변수 설정
1. C:\Users\<사용자>\go\bin을 환경 변수에 추가합니다.
3) 설치 확인
protoc-gen-doc --versionWindows |
b. macOS
1) Go 패키지 관리 명령을 사용하여 protoc-gen-doc 설치
go install github.com/pseudomuto/protoc-gen-doc/cmd/protoc-gen-doc@latest |
2) 환경 변수 설정
1. 기본적으로 $GOPATH/bin 위치에 설치되며 해당 위치를 시스템 PATH에 추가
export PATH=$PATH:$(go env GOPATH)/bin |
3) 설치 확인
protoc-gen-doc --version
|
2.protoc-gen-doc 을 통해 API 문서 만들기
protoc-gen-doc을 사용하는 방법은 1) 로컬 Command를 활용하는 방법(사전 설치 필요)과 2) 원격 플러그인을 사용하는 방법 두 가지가 있습니다.
2.1. 로컬 Command를 통한 문서 생성
1. proto 파일 작성 및 주석 추가하기
protoc-gen-doc의 경우 주석을 description으로 변환합니다.
a. .proto 파일 예시
// GetBalance 메서드 호출 Request message BalanceRequest { string account_number = 1; // 계좌번호 } // GetBalance 메서드 응답 Response message BalanceResponse { uint64 balance = 1; // 잔고 } // 서비스 정의 - 계좌 확인 서비스 service BankService { rpc GetBalance(BalanceRequest) returns (BalanceResponse); } |
b. 문서화 결과
2. 문서 생성 명령어 실행하기
a. 문서 생성 관련 옵션
1) --doc_out: 생성된 문서를 저장할 경로
2) --doc_opt: 출력 형식과 파일 이름 지정 (예: HTML로 index.html 파일 생성)
3) --proto_path, -I: proto 파일 의존성 관련 경로 지정, 기본값은 현재 디렉터리
b. 예시
// proto에서 정의한 서비스와 메세지를 markdown 형식으로 문서 생성 protoc \ --proto_path=src/main/proto \ --doc_out=./out \ --doc_opt=markdown,api-deafault-doc.md \ src/main/proto/*.proto |
c. 문서 생성 옵션(--doc-opt)
1) 사용 방법:
--doc_opt=<FORMAT>|<TEMPLATE_FILENAME>,<OUT_FILENAME>[,default|source_relative] |
2) option_format: 생성할 파일 포맷
1. 기본 포맷: html, markdown, json, docbook 사용
2. 템플릿 사용: 템플릿 위치 지정
→ Gradle의 경우 동작하는 디렉토리를 root project로 세팅
3) output_file_name: 생성할 파일명
4) additional_options:
1. 대상 proto 파일 디렉토리 구조
아래와 같은 디렉토리 구조를 가졌을 때 옵션에 따라 생성 결과가 달라집니다.
## 요청 proto 파일 디렉토리 구조 protos/ ├── service/ │ └── student_service.proto └── messages/ └── student_message.proto |
2. default 옵션: 출력 파일이 단일 디렉토리에 저장됨
protoc --doc_out=out protos/**/*.proto ## 생성 결과 out/ ├── student_service.html ├── student_message.html |
3. source_relative: proto 파일의 디렉토리 구조를 유지한 채 doc 파일 생성
protoc --doc_out=source_relative:out protos/**/*.proto ## 생성 결과 out/ ├── service/ │ └── student_service.html └── messages/ └── student_message.html |
d. 사용 예시(참고: gen-doc 플러그인 관련 option 설정 - protoc-gen-doc/plugin.go at master · pseudomuto/protoc-gen-doc)
// #1. docs.md라는 이름의 md 파일로 생성 doc { option "markdown,docs.md" } // #2. api.json 파일을 생성할 때 source_relative 옵션 적용 doc { option "json,api.json,source_relative" } // #3. template/grpc-asciidoc.tmpl이라는 템플릿을 활용하여 adoc 문서 생성 doc { option "template/grpc-asciidoc.tmpl",/api-doc.adoc" } |
2.2. 원격 플러그인을 통한 문서 생성
원격 플러그인을 사용하여 문서를 생성할 경우, 앞선 방식과 다르게 Go 환경 구성 및 protoc-gen-doc 플러그인 설치 등의 번거로운 작업 없이 편리하게 사용할 수 있습니다.
1. protoc 설정
a. 플러그인 추가하기
1) gRPC 플러그인 추가
doc { artifact = "io.grpc:protoc-gen-grpc-java:1.58.0" } |
doc { artifact = "io.github.pseudomuto:protoc-gen-doc:1.5.1" } |
b. protoc Task 설정하기
1) generateProtoTasks 설정
1. .proto 파일에 대해 코드 생성 및 문서화 작업 설정
protobuf { protoc { artifact = "com.google.protobuf:protoc:3.21.12" } plugins { doc { artifact = "io.github.pseudomuto:protoc-gen-doc:1.5.1" } } generateProtoTasks { all().each { task -> task.builtins { java {} } task.plugins { doc { option "html,index.html" // 기본 html 변환 설정 } } } } } |
2. 플러그인 사용 시 유의점
build 시 단 한번만 task가 실행되기 때문에 사용의 제약이 발생합니다. 따라서 패키지별로 문서를 생성하거나, 여러 개의 proto 파일로 하나의 문서 생성하기 등의 작업은 어렵습니다.
2.3. Template 사용하기
protoc-gen-doc의 경우 Go 언어의 템플릿 파일(.tmpl)을 활용하여 API 문서를 동적으로 생성할 수 있습니다.
1. Template 파일 구성 요소
# Package // 패키지 이름이 표시되며 .proto 파일의 패키지명을 문서 상단에 보여줍니다. # Menu // 문서의 목차 역할 # Services // 서비스와 메서드의 구조를 보여줍니다. 각 서비스와 메서드에 대해 다음 정보를 포함합니다. // 메서드 이름 // 요청 및 응답 타입 // gRPC 호출 방식: GRPC localhost:8080/{서비스명}/{메서드명} # Messages //메시지 타입을 표 형식으로 상세하게 설명 # Enums # Sclar Value Type |
2. Template 파일 Sample
a. protoc-gen-doc에서는 .md 파일 및 .adoc파일에 대한 기본 템플릿을 제공합니다.
b. 템플릿 관련 자료: Template Sample
3. Template을 활용한 문서화
a. Template을 활용 문서 옵션
--doc_opt 옵션을 통해 대상 템플릿 경로를 지정하여 원하는 포맷으로 문서를 생성할 수 있습니다.
protoc \ --proto_path=src/main/proto \ --doc_out=./out \ --doc_opt=template/grpc-md.tmpl,api-template-doc.md \ src/main/proto/*.proto |
b. 사용 결과
2.4. protoc-gen-doc 사용 시 주의사항
마지막으로 protoc-gen-doc을 사용할 때 주의해야 할 점은 외부에서 정의된 메시지 의존성 문제입니다. .proto 파일이 다른 파일에서 정의한 메시지를 import하고 있다면, 종종 의존성 오류가 발생할 수 있습니다. 이를 방지하기 위해 --proto_path 옵션을 통해 import 경로를 명확히 지정해 주어야 합니다.
특히 외부 라이브러리나 Google의 공용 proto 파일을 사용할 때 문제가 발생할 수 있습니다. 예를 들어, Google에서 제공하는 google.protobuf.timestamp과 같은 메시지를 사용하려고 할 때, protoc-gen-doc은 외부에서 정의된 이 메시지를 인식하지 못해 오류가 발생할 수 있습니다. 이 경우 google.protobuf.Timestamp 타입이 정의되지 않았다는 오류가 표시됩니다.
1. 문제 상황
a. 관련 코드: google.timestamp 사용
syntax = "proto3"; package cohttp://m.example.bank; option java_package = "cohttp://m.example.bank"; option java_outer_classname = "BankServiceProto"; //import "common/timestamp.proto"; import "google/protobuf/timestamp.proto"; // GetBalance 메서드 호출 Request message BalanceRequest { string account_number = 1; // 계좌번호 } // GetBalance 메서드 응답 Response message BalanceResponse { uint64 balance = 1; // 잔고 google.protobuf.Timestamp updated_by = 2; // 잔고 업데이트 시간 } // 서비스 정의 - 계좌 확인 서비스 service BankService { rpc GetBalance(BalanceRequest) returns (BalanceResponse); } |
2. 오류 메세지 - google.protobuf.Timestamp가 정의되지 않아 오류 발생
2. 해결 방안
위에서 발생한 원격 의존성을 해결 하기 위해 두 가지 방안이 존재합니다.
a. 내부에 외부 라이브러리 재정의
1) 원격 메세지를 프로젝트 내부에서 재정의하여 의존성 문제를 해결합니다.
syntax = "proto3"; package cohttp://m.example.common; option java_package = "cohttp://m.example.common"; option java_outer_classname = "EmptyProto"; option java_multiple_files = true; message Timestamp { int64 seconds = 1; int32 nanos = 2; } |
2) 의존성 경로 변경 - 내부 경로로 변경
import "common/timestamp.proto"; // GetBalance 메서드 응답 Response message BalanceResponse { uint64 balance = 1; // 잔고 common.Timestamp updated_by = 2; // 잔고 업데이트 시간 } |
b. 원격 플러그인 사용하기
1) 원격 플러그인 사용 예시
## build.gradle protobuf { protoc { artifact = "com.google.protobuf:protoc:${protobufVersion}" } plugins { grpc { artifact = "io.grpc:protoc-gen-grpc-java:${grpcVersion}" } doc { artifact = "io.github.pseudomuto:protoc-gen-doc:${protocGenDocVersion}" } } generateProtoTasks { all().each { task -> task.builtins { java {} } task.plugins { grpc {} doc { option "markdown, api-doc.md" } } } } } |
참고/출처