What is RPC(Remote Procedure call)?
RPC๋ ์ํคํผ๋์์ ์ค๋ช
์ ๋น๋ฆฌ๋ฉด ๋ค์๊ณผ ๊ฐ์ด ์ค๋ช
ํ ์ ์๋ค.
๋ณ๋์ ์๊ฒฉ ์ ์ด๋ฅผ ์ํ ์ฝ๋ฉ ์์ด ๋ค๋ฅธ ์ฃผ์๊ณต๊ฐ์์
ํจ์๋ ํ๋ก์์ ๋ฅผ ์คํํ ์ ์๊ฒ ํด์ฃผ๋ ํ๋ก์ธ์ค๊ฐ ํต์ ๊ธฐ์ ์ด๋ค.
๋ค์ ๋งํด, ์๊ฒฉ ํ๋ก์์ ํธ์ถ์ ์ด์ฉํ๋ฉด
ํ๋ก๊ทธ๋๋จธ๋ ํจ์๊ฐ ์คํ ํ๋ก๊ทธ๋จ์ ๋ก์ปฌ ์์น์ ์๋ ์๊ฒฉ ์์น์ ์๋ ๋์ผํ ์ฝ๋๋ฅผ ์ด์ฉํ ์ ์๋ค.
Plain Text
๋ณต์ฌ
์๋ client๊ฐ server์ GetUser๋ผ๋ ์๋ฒ์ ๋ก์ง์ ํธ์ถํ๋ rpc ๋จ๊ณ๋ฅผ ๋ํ๋ธ ๋ค์ด์ด๊ทธ๋จ์ด๋ค.
What is gRPC?
gRPC๋ ์์์ ์ค๋ช
ํ RPC ์์ g๊ฐ ๋ถ์๋ค. ์ฌ๋ฌ๋ถ์ด ์๊ฐํ๋ ๊ทธ๊ฒ ๋ง๋ค. ๋ฐ๋ก Google์ด๋ค.
google Remote Procedure call ์ด ๋ฐ๋ก gRPC์ด๋ค.
๊ตฌ๊ธ์์ ๊ฐ๋ฐํ ์ด๋์์๋ ๋์ํ๋ ๊ณ ์ฑ๋ฅ RPC ํ๋ ์์ํฌ ์คํ์์ค์ด๋ค.
gRPC์์ ํด๋ผ์ด์ธํธ ์ ํ๋ฆฌ์ผ์ด์
์ ๋ค๋ฅธ ์์คํ
์ ์๋ฒ ์ ํ๋ฆฌ์ผ์ด์
์ ์๋ ๋ฉ์๋๋ฅผ local ๊ฐ์ฒด์ธ ๊ฒ ์ฒ๋ผ
์ง์ ํธ์ถํ ์ ์์ผ๋ฏ๋ก ๋ถ์ฐ ์ ํ๋ฆฌ์ผ์ด์
๊ณผ ์๋น์ค๋ฅผ ์ฝ๊ฒ ๋ง๋ค ์ ์๋ค.
๋ง์ RPC ์์คํ
์์์ ๋ง์ฐฌ๊ฐ์ง๋ก gRPC๋ ์๋น์ค๋ฅผ ์ ์ํ๊ณ ,
๋งค๊ฐ ๋ณ์์ ๋ฐํ ์ ํ์ ์ฌ์ฉํ์ฌ ์๊ฒฉ์ผ๋ก ํธ์ถํ ์ ์๋ ๋ฉ์๋๋ฅผ ์ง์ ํ๋ ์์ด๋์ด๋ฅผ ๊ธฐ๋ฐ์ผ๋ก ํ๋ค.
์๋ฒ๋ ํด๋น ์ธํฐํ์ด์ค๋ฅผ ๊ตฌํํ๊ณ gRPC ์๋ฒ๋ฅผ ์คํํ์ฌ ํด๋ผ์ด์ธํธ ํธ์ถ์ ์ฒ๋ฆฌํ๋ค.
ํด๋ผ์ด์ธํธ๋ ์๋ฒ์ ๋์ผํ ๋ฐฉ๋ฒ์ ์ ๊ณตํ๋ stub์ ๊ฐ์ง๋ค.
Supported languages
ํ์ฌ gRPC ๋ ์๋์ ๊ฐ์ ์ธ์ด๋ค์ ์ง์ํ๊ณ ์๋ค.
REST vs gRPC
REST | gRPC | |
protocol | HTTP / 1.1 | HTTP / 2 |
payload | JSON | ProtoBuf |
API contract | Strict, required | Loose, optional |
security | TLS/SSL | TLS/SSL |
streaming | one way | Bidirectional |
Browser support | All | gRPC-web |
gRPC๊ฐ HTTP/2 ์ ํ์ ํ ProtoBuf๋ฅผ ์ฌ์ฉํ๋ ๋ฑ ์ฌ๋ฌ ์ด์ ๋ก ์ธํด REST ๋๋น ์ข์ performance๋ฅผ ๊ฐ์ง๊ณ ์๋ค.
What is ProtoBuf(Protocol Buffer)?
โข
google์์ ๊ฐ๋ฐํ ๊ตฌ์กฐํ๋ ๋ฐ์ดํฐ๋ฅผ Serialization ํ๋ ๊ธฐ๋ฒ์ด๋ค.
โข
๋ฐ์ดํฐ ์ ํ, ๋ฐ์ดํฐ ํ์
๋ฑ์ 1byte๋ก ์๋ณํ๊ณ , ์ฃผ์ด์ง length ๋งํผ๋ง ์ฝ์ด์ ์ฉ๋์ด ์๋ค.
Proto File
.proto ํ์ฅ์๋ฅผ ๊ฐ๋ ProtoBuf์ ๊ธฐ๋ณธ ์ ๋ณด๋ฅผ ๋ช
์ธํ๋ ํ์ผ์ด๋ค.
1. Message
syntax = "proto3"; // proto buf version
message MessageName {
string name = 1;
int32 id = 2;
optional bool do_you_wanna_build_snowman = 3;
repeated int32 snowman_made_date = 4;
}
Protobuf
๋ณต์ฌ
โข
message ์ด๋ฆ์ CamelCase, field ์ด๋ฆ์ snake ํํ๋ก ์ฌ์ฉํ๋๊ฒ์ ๊ถ์ฅ.
โข
field ์ด๋ฆ์ ์ซ์๋ก ์์ํ ์ ์๋ค. (ย 1_nameย โ name_1)
โข
field ๋ ๊ณ ์ ํ ๋ฒํธ๋ฅผ ๊ฐ์ง๊ฒ ๋๋๋ฐ, 1 ~ 536,870,911 ๊น์ง ์ฌ์ฉ ๊ฐ๋ฅํ๋ค(19000 ~ 19999๋ reversed๋ ๊ฐ์ผ๋ก ์ฌ์ฉ๋ถ๊ฐ)
โข
required,ย optional,ย repeatedย ์ต์
์ ์ฌ์ฉํ ์ ์๋ค.
โฆ
requiredย - ๋ฐ๋์ ํ๋์ ํ๋๋ฅผ ๊ฐ์ ธ์ผํ๋ค. (proto3 ๋ถํฐ ์ฌ์ฉ x)
โฆ
optionalย - ์๊ฑฐ๋ ํ๋๋ง ๊ฐ์ ธ์ผ ํ๋ ํ๋.
โฆ
repeatedย - ๋ฐ๋ณต์ ์ผ๋ก ์ฌ๋ฌ๋ฒ ์ฌ์ฉ๋ ์ ์๋ค. ์์๋ ๋ณด์กด๋๋ค.
โฆ
โฆ
oneof - ์๋ proto์ ๊ฐ์ด ์ ์ํ๋ฉด. ์๋ go ์ฝ๋ ์์์ฒ๋ผ generate ๋๋ค. (๋น์ฐํ์ง๋ง ์ธ์ด๋ง๋ค ์์ฑ๋๋ ์ฝ๋๋ ๋ค๋ฅด๋ค.)
package account;
message Profile {
ย oneof avatar {
ย ย string image_url = 1;
ย ย bytes image_data = 2;
ย }
}
Protobuf
๋ณต์ฌ
type Profile struct {
// Types that are valid to be assigned to Avatar:
// *Profile_ImageUrl
// *Profile_ImageData
Avatar isProfile_Avatar `protobuf_oneof:"avatar"`
}
type Profile_ImageUrl struct {
ImageUrl string
}
type Profile_ImageData struct {
ImageData []byte
}
Go
๋ณต์ฌ
2. Package
package seoul.company;
message TestBank {
// ...
}
message Resume {
seoul.company.Testbank testbank_1 = 1; // possible
Testbank testbank_2 = 2 // possible
}
Protobuf
๋ณต์ฌ
โข
pakage๋ message ํ์
์ด๋ฆ์ ์ค์ฒฉ์์ด ๊ตฌ๋ถํ ๋ ์ฌ์ฉํ๋ค.
3. Service
service HelloWorld {
rpc Hello (HelloRequest) returns (HelloResponse) {};
rpc HelloClientStream (stream HelloClientStreamRequest) returns (HelloClientStreamResponse) {};
rpc HelloServerStream (HelloServerStreamRequest) returns (stream HelloServerStreamResponse) {};
rpc HelloBiStream (stream HelloBiStreamRequest) returns (stream HelloBiStreamResponse) {};
}
Protobuf
๋ณต์ฌ
โข
RPC๋ฅผ ํตํด Server๊ฐ Client์๊ฒ ์ ๊ณตํ ํจ์์ ํํ๋ฅผ ์ ์ํ๋ค.
โข
CamelCase๋ฅผ ๊ถ์ฅํ๋ค.
โข
๊ธฐ๋ณธ์ ์ผ๋ก ๋จ์ผ ์์ฒญ/์๋ต์ผ๋ก ๋์ํ์ง๋ง,ย streamย ์ต์
์ ์ฌ์ฉํด์ RPC๋ฅผ ๊ตฌํํ ์ ์๋ค.
What is BSR?
BSR(Buf Schema Registry) ์ Protobuf ํ์ผ๋ค์ ๋ฒ์ ๋ ๋ชจ๋๋ก ์ ์ฅํ๊ณ ๊ด๋ฆฌํด์ฃผ๋ ์ ์ฅ์์ด๋ค.
ํด๋น ์ ์ฅ์๋ฅผ ์ด์ฉํ๋ฉด ๊ฐ์ธ์ด๋ ๊ธฐ๊ด์ด API๋ค์ ๋ง์ฐฐ์์ด ์ฌ์ฉํ๊ณ ๋ฐฐํฌํ ์ ์๋ค.
BSR์ ํ์๊ฐ๋ฅํ UI์ ์์กด์ฑ ๊ด๋ฆฌ, API ๊ฒ์ฆ๊ณผ ๋ฒ์ ๋, ๋ฌธ์ ์์ฑ ๊ทธ๋ฆฌ๊ณ ์๊ฒฉ ์ฝ๋ ์ ๋ํ
์ดํฐ๋ฅผ ํตํด ํ์ฅ ๊ฐ๋ฅํ ํ๋ฌ๊ทธ์ธ ์์คํ
์ ์ ๊ณตํ๋ค.
22/09/26 ์ผ ๊ธฐ์ค ๋ฒ ํ ๋ฒ์ ์ผ๋ก ๋ฌด๋ฃ์ด๋ ๋ฒ ํ๋ฒ์ ์ดํ์ team์ผ๋ก ์ฌ์ฉํ๋ ค๋ฉด ์ ๋ฃ(10๋ฌ๋ฌ)๋ก ์ ํ ๋ ์์ ์ด๋ค.
gRPC, protoBuf ๋ฅผ ์ฌ์ฉํ๊ธฐ ์ํด ํด๋น ์๋น์ค๋ฅผ ๋ฐ๋์ ์ด์ฉํ ํ์๋ ์๋ค.
Install
$ brew install bufbuild/buf/buf
Bash
๋ณต์ฌ
Login
$ buf registry login
UserName: <USERNAME>
Token: <TOKEN>
Shell
๋ณต์ฌ
์ ๋ช
๋ น์ด๋ฅผ ์
๋ ฅํ๊ณ , ์ ์ ๋ค์, ํ ํฐ์ ์
๋ ฅํ๋ฉด ๋ก๊ทธ์ธ ๋๋ค.
์ ์์ ์ผ๋ก ๋ก๊ทธ์ธ์ด ์๋ฃ๋๋ฉด ~/.netrc ํ์ผ์ ์๋์ ๊ฐ์ด ์ ์ฅ๋๋ค.
machine buf.build
login <USERNAME>
password <TOKEN>
machine go.buf.build
login <USERNAME>
password <TOKEN>
Shell
๋ณต์ฌ
๋ก๊ทธ์ธ์ด ์๋ฃ๋๋ฉด BSR์ ์ ๊ทผ ๊ถํ ์ค์ ์ด ๋๋ฌ๋ค.
์ด์ repository๋ฅผ ์์ฑํ๊ณ module์ push ํ ์ ์๋ค.
How to use
์์ํ๊ธฐ ์์์ ๊ธฐ๋ณธ์ ์ธ ์ฉ์ด๋ค์ ํ์ธํด๋ณด์.
โข
Modules
Modules ์ Buf์ BSR์ ํต์ฌ์ด๋ค. module ์ ๊ตฌ์ฑ๋๊ณ , ๋น๋๋๊ณ , ๋ฒ์ ์ด ๋ช
์๋ ๋
ผ๋ฆฌ๋จ์์ Protobuf์ ์งํฉ์ด๋ค. module์ buf.yaml ์ ์ด๊ธฐํํ ๋ ์์ฑํ๋ค.
โข
Repositories
module๋ repository ์ ์ ์ฅ๋๋ค. ๋ ํฌ์งํ ๋ฆฌ๋ ๋ชจ๋์ ๋ชจ๋ ๋ฒ์ ์ ์ ์ฅํ๋ค. ๊ฐ ๋ฒ์ ์ ์ปค๋ฐ์ด๋ ์ ํ์ ์ผ๋ก ํ๊ทธ์ ์ํด์ ์๋ณ๋๋ค.
Git ์ ์ฅ์์ ๋๋ต ๋น์ทํ์ง๋ง BSR ์ ์ฅ์๋ ์๊ฒฉ ์์น์ผ ๋ฟ์ด๋ฉฐ ์ ์ฅ์ "ํด๋ก "์ด๋ผ๋ ๊ฐ๋
์ ์๋ค. ์ฆ, ๋ฆฌํฌ์งํ ๋ฆฌ๋ ์ฌ๋ฌ ์์น์ ์กด์ฌํ์ง ์๋๋ค.
โข
Module names
module์ ์ด๋ฆ๊ณผ 3๊ฐ์ ๋ค๋ฅธ ์ปดํฌ๋ํธ๋ฅผ ๊ฐ๋๋ค.
โข
โข
Owner: ๊ฐ์ธ์ด๋ ๊ธฐ๊ด๊ณผ ๊ฐ์ repo์ ์ฃผ์ธ์ด๋ค.
โข
Repository: repo์ ์ด๋ฆ์ด๋ค.
์์,
Name | Remote | Owner | Repository |
buf.build/testbank/tools | buf.build | testbank | tools |
buf.build/korea/weather | buf.build | korea | weather |
solvetheproblem.io/solve/solve | solvetheproblem.io | solve | solve |
Create Repository
command line ์์ ๋ ํฌ๋ฅผ ์์ฑํ๋ ๋ฐฉ๋ฒ๊ณผ buf.build ์ฝ์์์ Create repository ๋ฅผ ํด๋ฆญํด์ ์์ฑํ๋ ๋ฐฉ๋ฒ์ด ์๋ค.
โข
command line
buf beta registry repository create buf.build/$BUF_USER/$REPO_NAME --visibility public
Shell
๋ณต์ฌ
โข
buf.build console (home โ ์ฐ์ธก ์๋จ name ํด๋ฆญ โ Repositories โ Create repository)
Configure
$ buf mod init
Bash
๋ณต์ฌ
Set name
buf.yaml ํ์ผ์์ name์ ๋งค์นญํด ์ฃผ๋ฉด ๋๋ค.
version: v1
name: <Remote>/<Owner>/<Repository>
lint:
use:
- DEFAULT
breaking:
use:
- FILE
YAML
๋ณต์ฌ
Push the module
buf.yaml ์ด ์๋ ํด๋์์ ์๋ ์ปค๋งจ๋๋ฅผ ์ํํ๋ฉด ๋๋ค.
$ buf push
Shell
๋ณต์ฌ
ํด๋น ์ปค๋งจ๋๋ฅผ ์ํํ๋ฉด Buf Registry์ code๊ฐ push ๋๋ค.
Write Configuration
โข
buf.yaml
buf.yaml ํ์ผ์ ๋ชจ๋์ ์ ์ํ๊ณ Protobuf ํ์ผ์ ๋ฃจํธ์ ์์นํ๋ค.
buf.yaml ์ ๊ตฌ์ฑ์ buf์๊ฒ .proto ํ์ผ์ ์ด๋์ ์๊ณ lint, breaking ๋ฑ ์ต์
์ ์ค์ ํ ์ ์๋ค.
buf.lock
buf mod update ์ปค๋ฉ๋๋ฅผ ํตํด ์์ฑํ ์ ์๋ค. ์๋์ผ๋ก ์์ฑ๋๋ ๋ถ๋ถ์ด๋ ์์ ํ์ง ๋ง์.
buf.gen.yaml
buf.gen.yaml ํ์ผ์ buf generate ์ปค๋งจ๋๊ฐ protoc ํ๋ฌ๊ทธ์ธ๋ค์ ์ด๋ค์์ผ๋ก ์คํํ๋์ง์ ๋ํ ํต์ ์ด๋ค.
version: v1
plugins:
- name: cpp
out: gen/proto/cpp
- name: java
out: gen/proto/java
- name: go
out: gen/proto/go
opt: paths=source_relative
- name: go-grpc
out: gen/proto/go
opt:
- paths=source_relative
- require_unimplemented_servers=false
YAML
๋ณต์ฌ
buf.work.yaml
buf.work.yaml์ workspace๋ฅผ ์ ์ํ ๋ ์ฌ์ฉํ๋ค. ํ๋ ์ด์์ ๋ชจ๋์ด ๊ณตํต๋ ๋ฆฌํ
ํ ๋ฆฌ์ ์กด์ฌํ ๋ local ๋ชจ๋์ด ๋ค๋ฅธ local๋ชจ๋์ import ํ ์ ์๊ฒ ํด์ค๋ค.
.
โโโ buf.work.yaml
โโโ testbank-rpc
โ โโโ testbank
โ โ โโโ contents
โ โ โโโ v1
โ โ โโโ user.proto
โ โโโ buf.yaml
โโโ solve-rpc
โโโ solve
โ โโโ solve
โ โโโ v1
โ โโโ problem.proto
โโโ buf.yaml
Bash
๋ณต์ฌ
์๋ฅผ ๋ค์ด ์์ ๊ฐ์ ๋๋ ํ ๋ฆฌ ๊ตฌ์กฐ๋ฅผ ๊ฐ๊ณ ์์ ๋,
# buf.work.yaml
version: v1
directories:
- testbank-rpc
- solve-rpc
Bash
๋ณต์ฌ
์์ ๊ฐ์ด buf.work.yaml์ ์์ฑํ๋ฉด testbank-rpc์์ solve-rpc, ํน์ ๊ทธ ๋ฐ๋, ๋ฅผ import ํด์ ์ฌ์ฉํ ์ ์๋ค.
breaking changes
version: v1
lint:
use:
- DEFAULT
breaking:
use:
- FILE
YAML
๋ณต์ฌ
โข
Option : FILE, PACKAGE, WIRE, WIRE JSON
default ๊ฐ์ FILE ์ด๋ค. API์ ์ฌ์ฉ์ ๊ฐ์ ์ต๋ํ์ ํธํ์ฑ์ ๋ณด์ฅํ ๊ฒ์ ๊ถ์ฅํ๋ค.
์ผ๋ฐ์ ์ผ๋ก lint๊ตฌ์ฑ์ ์ง์ ํ ๋ ์ฒ๋ผ ํน์ ๋ณ๊ฒฝ ๊ท์น์ ํฌํจ/์ ์ธํ๊ธฐ๋ณด๋ค๋ ์ด๋ฌํ ์ต์
์ค ํ๋๋ง ์ ํํ๋ ๊ฒ์ด ์ข๋ค.
// pet/v1/pet.proto
message Pet {
- PetType pet_type = 1;
+ string pet_tyep = 1;
string pet_id = 2;
string name = 3;
}
Protobuf
๋ณต์ฌ
๋ง์ฝ ์์ ์์์ฒ๋ผ Pet.pet_type field๋ฅผ PetType ์์ string ์ผ๋ก ๋ณ๊ฒฝํ๊ณ ๋์,
buf breaking ๋ช
๋ น์ด๋ฅผ ์ํํด ๋ณด๋ฉด ๋ณ๊ฒฝ๋ ๋ถ๋ถ์ ์๋ฌ๊ฐ ๋ฐ์ํ๋ค.
$ buf breaking --against "../../.git#branch=main,subdir=start/petapis"
pet/v1/pet.proto:20:3:Field "1" on message "Pet" changed type from "enum" to "string".
Shell
๋ณต์ฌ
lint
buf lint ๋ฅผ ์ฌ์ฉํ๋ฉด API ์ ์์ ๊ฐ์ฅ ์ข์ ์ฌ๋ก๋ฅผ ๊ฒฐ์ ํ๊ฒ ํด์ ์ผ๊ด์ฑ์ ๊ฐ์ ํ๊ณ ์ง์ผ์ค๋ค.
$ buf lint --error-format=json
{"path":"google/type/datetime.proto","start_line":17,"start_column":1,"end_line":17,"end_column":21,"type":"PACKAGE_VERSION_SUFFIX","message":"Package name \"google.type\" should be suffixed with a correctly formed version, such as \"google.type.v1\"."}
{"path":"pet/v1/pet.proto","start_line":44,"start_column":10,"end_line":44,"end_column":15,"type":"FIELD_LOWER_SNAKE_CASE","message":"Field name \"petID\" should be lower_snake_case, such as \"pet_id\"."}
{"path":"pet/v1/pet.proto","start_line":49,"start_column":9,"end_line":49,"end_column":17,"type":"SERVICE_SUFFIX","message":"Service name \"PetStore\" should be suffixed with \"Service\"."}
Shell
๋ณต์ฌ
buf.yaml ํ์ผ์์ ignore ์ต์
์ผ๋ก ํน์ ํ์ผ์ lint์์ ๋ฌด์ํ ์ ์๋ค.
version: v1
lint:
use:
- DEFAULT
ignore:
- google/type/datetime.proto
breaking:
use:
- FILE
YAML
๋ณต์ฌ
Using module
์๋์ ๊ฐ์ด ๊ฐ ์ธ์ด์์ ์ํ๋ ํ
ํ๋ฆฟ์ผ๋ก push๋ ๋ชจ๋์ ๋ชจ๋์ ๋ฐ์์ ์ฌ์ฉํ ์ ์๋ค.
โข
go
$ go get go.buf.build/${template_owner}/${template_name}/${owner}/${repository}
Bash
๋ณต์ฌ
โข
js
$ npm config set @buf:registry https://npm.buf.build
$ npm install @buf/${template_owner}_${template_name}_${owner}_${repository}
Bash
๋ณต์ฌ
ํน์, buf.gen.yaml ํ์ผ์ ์์ฑํ๋ค๋ฉด buf generate ๋ฅผ ํตํด์ ๋ก์ปฌ์์ ์์ฑํ ์ ์๋ค.
$ buf generate
Bash
๋ณต์ฌ
์ ์ปค๋งจ๋๋ฅผ ์คํํ๋ฉด buf.gen.yaml ์ out์ผ๋ก ์ง์ ํ ํด๋์ ์์ฑ๋๋ค.
End
ํ์ฌ ํ
์คํธ๋ฑ
ํฌ๋ ํต์ ์์ gRPC๋ฅผ ์ฌ์ฉํ๊ณ ํด๋น ProtoBuf๋ฅผ BSR์ ์ฌ์ฉํด์ ๊ด๋ฆฌํ๊ณ ์๋ค.
gRPC๋ฅผ ์ฌ์ฉํ๊ธฐ ์ํด ์์ฑํ๋ ProtoBuf์ ๋ฐ๋ฅธ ํฐ ์ด์ ์ด ์๋ค.
1. ProtoBuf๋ฅผ ์ด์ฉํ์ฌ ์ฌ์ฉํ๋ ์ธ์ด์ ๋ง๋ ์ฝ๋๋ฅผ generateํด์ ์ฌ์ฉํ ์ ์๋ค.
2. ProtoBuf ์์ฒด๋ก API๋ช
์ธ์ ๊ธฐ๋ฅ์ ํ๊ธฐ ๋๋ฌธ์ ๋ฐ๋ก ์์ฑํ ํ์๊ฐ ์๋ค.
์ฒ์ ๊ธฐ๋ฅ์ ์ค๊ณํ ๋ ProtoBuf ์์ฑ์ ์ ๊ฒฝ์ ๋ง์ด ์ฐ๋ฉด ์ดํ์ ๊ฐ๋ฐ์ด ํธํด์ง๋ค.
BSR์ ์ฌ์ฉ์ ์์์ ์ธ๊ธํ ๊ฒ ์ฒ๋ผ ํ์๊ฐ ์๋์ง๋ง, ํด๋น ์ ์ฅ์๋ฅผ ์ฌ์ฉํ๋ฉด commit๊ณผ tag ๋ฑ์ผ๋ก ProtoBuf๋ฅผ ๊ด๋ฆฌํ๊ธฐ ์์ํ๊ณ module์ ์ฝ๊ฒ ๋ด๋ ค๋ฐ์์ ์ฌ์ฉํ ์ ์๋ค.
์ด์งํ Software Engineer
์ธ์ ๋ชจ๋ ๊ฒ์ ๊ด์ฌ์ด ๋ง๊ณ , ์ธ์์ ํฅํ ๊ด์ฌ์ผ๋ก ์๋น์ค๋ฅผ ๋ง๋ค๊ณ ์์ต๋๋ค.
Reference
โข