서론
JSON, XML 를 사용하여 통신을 하던 구글의 엔지니어들은 다음과 같은 생각을 갖게 됩니다.
"더 빨리 통신하고 싶어!"
그래서 구글은 프로토콜 버퍼라는 해결책을 내놓습니다.
그게 뭔데?
프로토콜 버퍼의 사전적 정의는 다음과 같습니다.
프로토콜 버퍼(Protocol Buffers)는 구조화된 데이터를 직렬화하는 방식이다. 유선이나 데이터 저장을 목적으로 서로 통신할 프로그램을 개발할 때 유용하다. - 한국어 위키피디아
제가 공부하며 내린 한줄 요약은 다음과 같습니다.
통신 과정에서 사용하는 언어에서 자유로운 모델 정의, 통신 속도를 빠르게 하기 위한 인코딩, 그 외에 편리한 도구 제공
프로토콜 버퍼는 크게 다음과 같은 기능을 제공합니다.
- 모델 스키마 정의
- 프로토콜 버퍼 스키마를 여러 언어의 인터페이스로 컴파일
- 인코딩 기능을 통한 데이터 크기 절약
이제 왜 이렇게 생각했는지 차례대로 설명하겠습니다.
모델 정의
프로토콜 버퍼를 사용하면 문법에 맞게 스키마를 작성하여 모델을 정의 할 수 있습니다.
사용자의 모델이 다음과 같다고 해보겠습니다.
- username
- password
- age
- isAdmin
이 경우 프로토콜 버퍼 스키마는 다음과 같이 작성 할 수 있겠습니다. 파일명은 user.proto 로 하였습니다.
syntax = "proto3"; // proto2 or proto3 for setting version
package info; // package name
message User {
string username = 1; // <type> <name> = <id>
string password = 2;
string email = 3;
uint32 age = 4;
bool isAdmin = 5;
}
이렇게 작성 된 모델은 protoc 라는 도구를 통해 여러 언어로 번역(컴파일) 될 수 있습니다.
protoc는 이곳 에서 설치 할 수 있습니다.
protoc를 활용한 컴파일 - go
protoc -I . user.proto --go_out .
다음과 같이 컴파일 된 모습을 볼 수 있습니다.
user.pb.go 에서는 우리가 아까 정의한 스키마에 대한 인터페이스를 제공합니다.
그 외의 편리한 도구 1 - 인터페이스
밑의 코드는 유저 모델 안에 값을 넣고, marshal(encode) 하여 파일로 저장 한 뒤, 파일에 접근하여 unmarshal(decode) 하는 예제 입니다.
package main
import (
"io/ioutil"
"log"
pb "protocol-buffer-practice/info" // import info as pb
"google.golang.org/protobuf/proto"
)
const filename = "user"
func main() {
writeMessage()
readMessage()
}
// marshal(encode) mmessage User with the given parameters, and write it as file
func writeMessage() {
user := &pb.User{Username: "yeongyu", Password: "qwerty", Email: "public.kim.yeon.gyu@gmail.com", Age: 17, IsAdmin: false}
out, err := proto.Marshal(user) // marshal
if err != nil {
log.Fatalf("Failed to marshal User: %v", err)
}
if err := ioutil.WriteFile(filename, out, 0644); err != nil { // write file, and if error occurs
log.Fatalf("Failed to write User: %v", err)
}
}
// decode the content of the given filename
func readMessage() {
in, err := ioutil.ReadFile(filename)
if err != nil {
log.Fatalf("Failed to read Message: %v", err)
}
user := &pb.User{}
if err := proto.Unmarshal(in, user); err != nil { // unmarshal, and if error occurs
log.Fatalln("Failed to unmarshal User: ", err)
}
log.Printf("Username: %s", user.GetUsername()) // the example of getter
log.Printf("Password: %s", user.GetPassword())
log.Printf("Email: %s", user.GetEmail())
log.Printf("Age: %d", user.GetAge())
log.Printf("IsAdmin: %t", user.GetIsAdmin())
}
이렇게 코드를 작성 후 실행하면 다음과 같이 나오게 됩니다.
그리고 다음과 같이 user 바이너리가 생긴 모습을 볼 수 있습니다. 바이너리라 그런지 vscode에서는 깨지는 모습도 보입니다.
그 외에 편리한 도구 2 - gRPC와의 활용
사실 gRPC에 대해 다루기 위해 본 글을 작성하였습니다.
스포일러를 하자면, REST API에서 생각하던 그런 서비스를 쉽게 작성할 수 있게 됩니다.
"훌륭한 백엔드 엔지니어가 되기 까지" 다음 편에서 해당 내용을 다루도록 하겠습니다.
작성한 소스코드
https://github.com/code-yeongyu/go-protocol-buffer-practice
참고한 링크
'Computer Science' 카테고리의 다른 글
다크모드 적용시: StatusBar (상단바) 아이콘 색상 바꿀때 유의 할 점 (0) | 2021.01.29 |
---|---|
gRPC(general-purpose Remote Procedure Calls)란? (1) | 2020.12.22 |
SW 마에스트로 11기 후기 (3) | 2020.11.29 |
선린인터넷고등학교 정보보호과 동아리 지원시스템 개발기 (1) | 2020.06.07 |
SW 마에스트로 11기 지원기 (0) | 2020.05.16 |