
grpc를 보기 전에 IDL이 뭔지 확인해본다 ...
- 인터페이스 정의 언어(Interface Description Language, IDL) 는 특정 프로그래밍 언어와 별도로 지정된 객체의 인터페이스에 사용되는 일반 언어입니다.
( https://developer.mozilla.org/ko/docs/Glossary/IDL)
_- 소프트웨어 컴포넌트의 인터페이스를 묘사하기 위한 명세 언어이다.
(https://ko.wikipedia.org/wiki/%EC%9D%B8%ED%84%B0%ED%8E%98%EC%9D%B4%EC%8A%A4_%EC%A0%95%EC%9D%98_%EC%96%B8%EC%96%B4)
IDL은 어느 한 언어에 국한되지 않은 언어중립적인 방법으로 인터페이스를 표현함으로써, 같은 언어를 사용하지 않는 소프트웨어 컴포넌트 사이의 통신을 가능하게 한다
그니까... 한 쪽에서는 golang을 쓰고, 다른 쪽에서는 javascript를 써도 서로 언어와 상관없이 서로 인터페이스를 가능하게 한다.
인터페이스를 하기 위한 정의 언어이기 때문에 서로 통신하는 정보를 어떻게 저장할 지에 대한 규칙도 정할 수 있다.
구조화된 데이터를 IDL로 직렬화해서 보내고, 통신 환경에 이종간 플랫폼 환경에서 원활한 통신이 잘 되도록 한다.
IDL 중 Protocol Buffers는 보통 *RPC를 이용한 소프트웨어에서 사용한다.
이 때, RPC 연경의 양 쪽에 있는 컴퓨터는 다른 운영 체제와 프로그래밍 언어를 사용할 수 있다.
IDL은 다른 두 개의 시스템을 연결하는 다리 역할을 한다.
( RPC는 다음 게시물로 정리 예정. )
xml, json 그리고 Protocol Buffers 등이 있다.
대표적으로 이 3개를 비교해본다.

| XML | JSON | Protocol Buffers (=proto) | |
|---|---|---|---|
| 사람이 읽을 수 있는가? | O | O | X |
| 브라우저에서 사용 가능? | O | O | X |
| javaScript 지원 | O | O | X |
| 스키마 여부 | 옵션 (DTD,XSD으로 스키마 지원함) | 옵션 | 필수 |
| 데이터 보안 | 도청 및 디코딩 가능 | 도청 및 디코딩 가능 | 스키마 없이 해석 어려움 |
| 처리 속도 | 느림 | 보통 | 빠름 |
| 데이터 크기 | 큼 | 중간 | 작음 |
| 비용 | 대규모 데이터에서는 비용이 많이 듦 | 대규모 데이터에서는 비용이 많이 듦 | 더 저렴함 |
| RPC 인터페이스 지원 | 아니오 (SOAP) | 아니오 (restful) | 예 |
| 유효성 검사 | 수동 파싱 및 유효성 검사 필요 | 수동 파싱 및 유효성 검사 필요 | 키워드로 유효성 검사 가능 (스키마 기반) |
eXtensible Markup Language의 약어. W3C에서 여러 특수 목적의 마크업 언어를 만드는 용도에서 권장되는 다목적 마크업 언어이다. 마크업 언어는 태그 등을 이용하여 데이터의 구조를 기술하는 언어의 한 가지이다. 가장 친숙하고 흔하게 접할 수 있는 마크업 언어로 HTML이 있다.
주로 데이터 표현 및 전송을 위한 마크업 언어다.
1. 사람이 읽을 수 있는가 ? - O
2. 장점 - 문서 중심의 데이터, 확장성 좋음
3. 스키마 여부 - 옵션이지만, 스키마를 사용해서 유효성 검사 가능함
4. 데이터 크기 - 가장 큼...
5.어디서 보통 사용하는지?
JavaScript Object Notation (JSON) 은 데이터 교환 형식의 일종입니다. 엄격한 부분 집합은 아니지만, JSON은 JavaScript 구문과 매우 유사합니다. 많은 프로그래밍 언어가 JSON을 지원하지만, 웹 사이트와 브라우저 확장기능을 포함한 JavaScript 기반 앱에 특히 유용합니다.
1. 사람이 읽을 수 있는가 ? - O
2. 데이터 크기 및 효율성 - XML보다 좋다..
3. 스키마 여부 - 옵션 => 유연하게 데이터 표현이 가능함.
문자열, 숫자, 객체, 배열 등 다양한 데이터 타입을 사용해서 데이터 표현하기 좋다.
스키마 지원을 하지 않기 떄문에 유효성 검사를 XML처럼 엄격하게 할 수는 없다... ( JSON Schema가 있긴 함)
날짜, 바이너리 등 데이터 타입을 지원하지 않는 것이 있다.
4. 데이터 보안
5. RESTful API 시 사용
나는 RPC에 대해 공부할 예정이기 때문에 RPC에서 사용하는 protocol buffer에 대해서 더 자세히 알려보려고 한다.
Protocol Buffers는 Google에서 개발한 Protocol Buffers는 데이터를 직렬화(Serialization)하는 방법으로, RPC 및 네트워크 통신에서 사용된다.
1. 사람이 읽을 수 있는가? - X
2. 데이터 보안 -스키마 기반이다
3. 처리 속도 및 비용
4. 언어, 플랫폼으로부터 독립적
5. RPC 지원
따라서,
IDL은 인터페이스 정의 언어...로
정말 쉽게 말하자면 프로그램들이 다른 언어로 개발됐어도 서로 대화(인터페이스)할 수 있게 도와주는 공통으로 사용하는 언어라고 보면 됨...
이 때 종류는 대표적으로 3가지가 있는데 XML, JSON, Protocol Buffers가 있음..
사람이 알아볼 수 있어야하는데 복잡한 데이터 구조일 때.. 문서 중심 데이터..로 표현하고 싶다면 XML을 사용하고...
사람이 알아볼 수 있어야 하는데 RESTful 통신해야 한다면 JSON..
RPC 통신이나 데이터 전송할 때 효율성이 중요하다면 proto...
사용하면 된다...

끝
https://developer.mozilla.org/ko/docs/Glossary/JSON
https://hungryjayy.github.io/posts/IDL/
https://velog.io/@gth1123/IDL-XML-JSON-gRPC
https://developer.mozilla.org/ko/docs/Web/XML
(+)
그럼 진짜 얼마나 차이나는지 궁금해서 golang server로 api 서버를 띄우고, locust로 체크해보았다.

구조는 다음과 같다.
performance_idl
- proto
|- message.proto : proto 파일
|- message.pd.go : proto를 go에서 쓸 수 있도록 변환
|- message_pd2.py : proto를 py에서 쓸 수 있도록 변환
- go.mod
- locustfile.py : locust 성능 테스트 파일
- server.go : go server
Go 언어에서 Protocol Buffers (프로토콜 버퍼)**를 사용하기 위한 공식 라이브러리
syntax = "proto3";
package myapp;
option go_package = ".;proto";
option optimize_for = SPEED;
message TestMessage {
int32 id = 1;
string name = 2;
string message = 3;
map<string, string> data = 4;
}
.proto 파일 => .go 파일
protoc --go_out=. --go_opt=paths=source_relative proto/message.proto
.proto 파일 => .py 파일 (locust 에서 사용 예정)
protoc --python_out=. proto/message.proto
package main
import (
"encoding/json"
"fmt"
"google.golang.org/protobuf/proto"
"io"
"log"
"net/http"
pd "performance-idl/proto"
)
func handleJSON(w http.ResponseWriter, r *http.Request) {
var data map[string]interface{}
if err := json.NewDecoder(r.Body).Decode(&data); err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
w.Header().Set("Content-Type", "application/json")
if err := json.NewEncoder(w).Encode(data); err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
}
}
func handleProtobuf(w http.ResponseWriter, r *http.Request) {
body, err := io.ReadAll(r.Body)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
var message pd.TestMessage
if err := proto.Unmarshal(body, &message); err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
res := &pd.TestMessage{
Id: message.Id,
Message: message.Message,
Name: message.Name,
Data: message.Data,
}
data, err := proto.Marshal(res)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
w.Header().Set("Content-Type", "application/x-protobuf")
w.Write(data)
}
func main() {
http.HandleFunc("/api/json", handleJSON)
http.HandleFunc("/api/protobuf", handleProtobuf)
fmt.Println("server on.. 🥷")
log.Fatal(http.ListenAndServe(":8080", nil))
}
from locust import HttpUser, task, between, constant
import json
from proto import message_pb2
class LoadTestUserProtobuf(HttpUser):
wait_time = between(1, 3)
@task(1)
def protobuf_test(self):
message = message_pb2.TestMessage(id=1, name="fear", message="fear is the mind killer",
data={"key1": "value1", "key2": "value2", "key3": "value4"})
self.client.post("/api/protobuf", data=message.SerializeToString(),
headers={"Content-Type": "application/x-protobuf"})
@task(1)
def json_test(self):
payload = {"id": 1, "name": "fear", "message": "fear is the mind killer",
"data": {"key1": "value1", "key2": "value2", "key3": "value4"}}
self.client.post("/api/json", data=json.dumps(payload), headers={"Content-Type": "application/json"})
상세 내용은 아래에서 확인해보면 된다.
https://github.com/fritmk/idl-performance

https://medium.com/@akresling/go-benchmark-json-v-protobuf-4ec3c62ec8d4

locust 지표 설명은 여서 확인해보면 된다.
https://velog.io/@tallguy188/%EC%84%B1%EB%8A%A5%EC%B5%9C%EC%A0%81%ED%99%94-%EC%9D%B4%EB%AF%B8%EC%A7%80-%EC%B5%9C%EC%A0%81%ED%99%94-%EB%B0%8F-%EB%A1%9C%EB%93%9C%ED%85%8C%EC%8A%A4%ED%8A%B8Locust