https://blog.banksalad.com/tech/production-ready-grpc-in-golang/?utm_source=gaerae.com&utm_campaign=개발자스럽다&utm_medium=social

안녕하세요, 뱅크샐러드 엔지니어링 파운데이션의 정겨울입니다.

뱅크샐러드는 마이크로 서비스 환경에서 다양한 언어와 프로토콜을 활용해 서비스를 운영하고 있습니다. 하나의 서비스는 요청을 처리하기 위해 다른 서비스의 API를 호출하는데 대부분의 기존 서비스는 REST API를 통해 JSON으로 통신하고 있습니다. 최근 기존 서비스 안정화 작업 및 신규 서비스 개발 시 go 언어로 개발을 진행하면서 internal 서비스 간 네트워크 통신에 gRPC를 활용하는 비중이 점점 커지고 있습니다. 그 과정에서 뱅크샐러드가 왜 go와 gRPC를 사용했고 gRPC를 잘 쓰기 위해 무엇을 고민했는지 공유하고자 합니다.

gRPC는 HTTP/2 레이어 위에서 Protocol Buffers(이하 protobuf)를 사용해 직렬화된 바이트 스트림으로 통신하므로 JSON 기반의 통신보다 더 가볍고 그만큼 통신 속도가 빠릅니다. 때문에 internal 통신이 빈번한 마이크로 서비스 구조에서 gRPC를 적용했을 때 latency 감소 및 더 많은 트래픽을 처리하는 성능의 이점을 기대해 gRPC를 도입해볼 수 있습니다.

하지만 뱅크샐러드는 protobuf와 gRPC가 각 서비스의 API를 정의하는 source of truth가 될 수 있다는 점을 성능의 이점보다 더 큰 장점으로 여겨 도입했습니다.

클라이언트 개발자와 서버 개발자는 사이에 API를 두고 각자 기능을 구현합니다. 기존에 뱅크샐러드는 API Blueprintswagger를 이용해 API를 문서화하고 명세를 검토하고 합의한 문서를 기준으로 기능을 구현해왔습니다. 하지만 API 문서를 기준으로 막상 개발하다 보면 수정사항이 발생하고, 이를 코드에는 반영하지만 정작 API 문서에는 반영하지 않아 API 명세가 점점 노후화되는 일이 반복되어 왔습니다. 그렇게 어느 순간부터 API 문서는 신뢰를 잃기 시작했으며 API 문서를 본격적으로 최신화하자니 거기에 시간을 쏟을 여유는 없고, 노후화된 문서가 원인이 되어 또 다른 버그가 발생하고, 신규입사자가 코드 파악을 위해 문서를 보며 헤매다가 질문을 하면 누군가가 “아 구두로 합의했는데 API 문서에 반영을 못 했습니다…”로 답이 나오는 일이 발생했습니다.

이에 뱅크샐러드는 gRPC와 protobuf를 사용해 마이크로서비스의 기능을 RPC로 선언했습니다. RPC마다 request, response 모델인 message를 정의하고, 이를 Swift, Java(Kotlin), Go, Python 등등 각 언어에서 import 하여 사용할 수 있도록 코드를 생성해 서버에서는 무조건 generated code를 사용해 protobuf 파일을 source of truth로 삼아 구현하며 기존의 문제를 해결했습니다.

https://s3-us-west-2.amazonaws.com/secure.notion-static.com/a1bffa88-43ec-456f-9818-4d12f3c51635/tada.png

…라고 말하기까지 우리가 풀어야 할 문제는 무척 많았습니다. 당장이라도 protobuf를 이용해 API 명세를 버전 관리도하고 정합성도 보장하고 싶고 generated code를 이용해 gRPC 통신을 하고 싶었지만, go로 서버를 구현하기도 전 protobuf부터 잘 쓰기 위한 문제가 많았습니다. 인터넷에 검색해도 gRPC와 protobuf를 이용한 example, sample, tutorial 수준 이상의 자료는 찾기 힘들었고, 그 때 마다 현재 우리가 진짜 해결하고 싶은 문제가 무엇인지, 우리 조직에 맞는 최선의 선택을 내려야 했습니다.

protobuf와 gRPC를 잘 쓰기와 더불어 gRPC 서버를 golang으로 구현하기까지 많은 고민이 있었습니다.

production에서 go를 활용해 gRPC 서비스를 운영하려면 이런 문제들은 반드시 풀어야만 했습니다. 여러 서비스를 만들며 문제를 풀기 위해 어떤 게 필요할지 논의를 반복하고, 경험을 기반으로 나름대로 노하우가 쌓이고 토대가 마련되기 시작했습니다.

서비스마다 protobuf를 정의하면 해당 기능을 구현하는 클라이언트 개발자와 서버 개발자 간의 명세 논의는 편해집니다. 하지만 기존의 노후화된 API 문서 때문에 발생하는 고통을 생각해봤을 때, protobuf를 하나의 리포지토리로 모으는 편이 source of truth를 만들기에 좀 더 적합하다고 판단했습니다. 아직 성숙하지 않은 protobuf 사용 사례가 각 서비스 리포지토리 마다 파편화되어 녹이 스는 걸 경계함과 동시에 뱅크샐러드 기술조직은 ‘본인의 일이 아니더라도 언제든 참견해 함께 성장하자’라는 문화를 장려하기에 모두가 손쉽게 참여하는 환경을 위해 하나의 리포지토리에서 시작했습니다.