Protocol Buffers, 또는 protobuf는 구조화된 데이터를 직렬화하고, 다른 언어로 쉽게 전송하고, 역직렬화할 수 있도록 Google에서 만든 언어 중립적, 효율적인 데이터 직렬화 포맷입니다.
먼저 protobuf를 컴파일 하기위해 protoc를 설치해야 한다.
install protoc
brew install protoc
// java, kotlin
protoc --proto_path=src --java_out=src/main/java --kotlin_out=src/main/kotlin src/main/resources/sample.proto
// javascript
protoc --js_out=import_style=commonjs,binary:. src/main/resources/sample.proto
syntax = "proto3";
package sample;
option java_multiple_files = false;
option java_package = "me.clevekim.protobuf";
option java_outer_classname = "Sample";
message Course {
int32 id = 1;
string course_name = 2;
}
dependencies {
// for protobuf
implementation("com.google.protobuf:protobuf-java:3.22.2")
implementation("com.google.protobuf:protobuf-java-util:3.22.2")
implementation("com.google.protobuf:protobuf-kotlin:3.22.2")
}
Protobuf message를 다루기 위해 HttpMessageConverter에 Google에서 제공하는 ProtobufHttpMessageConverter를 설정해야 한다.
@Bean
fun addProtobufHttpMessageConveter(): ProtobufHttpMessageConverter {
return ProtobufHttpMessageConverter()
}
@RestController
class CourseController(@Autowired val courseRepo: CourseRepository) {
@GetMapping(value = ["/courses/{id}"], produces = [MediaType.APPLICATION_PROTOBUF_VALUE])
@ResponseBody
fun customer(@PathVariable id: Int): CDSDTraining.Course? {
val course = courseRepo.getCourse(id)
return course
}
@PostMapping(value = ["/courses"], consumes = [MediaType.APPLICATION_PROTOBUF_VALUE], produces = [MediaType.APPLICATION_PROTOBUF_VALUE])
@ResponseBody
fun coursePost(@RequestBody course: Course?): CDSDTraining.Course? {
return course
}
}
// google-protobuf
npm install google-protobuf
// axios
npm install axios
npx create-react-app webapp
function searchApi() {
const url = "/courses/1";
axios.get(url, {
headers:{
'Accept': 'application/x-protobuf',
'MediaType': 'application/x-protobuf',
},
// protobuf message를 제대로 받을려면 responseType을 지정해야함.
// `responseType` indicates the type of data that the server will respond with
// options are: 'arraybuffer', 'document', 'json', 'text', 'stream'
// https://axios-http.com/docs/req_config
responseType: "arraybuffer",
})
.then(function(response) {
console.log("성공");
let bytes = new Uint8Array(response.data);
let sample = Course.deserializeBinary(bytes)
setCourse(sample)
})
.catch(function(error) {
console.log("실패", error);
course = null
})
}
function sendApi() {
const url = '/courses'
let course = new Course()
course.setId(2)
course.setCourseName("Test")
let courseBytes = course.serializeBinary()
axios.post(
url,
courseBytes,
{
headers: {
'Accept': 'application/x-protobuf',
'MediaType': 'application/x-protobuf',
'Content-Type': 'application/x-protobuf',
},
// protobuf message를 제대로 받을려면 responseType을 지정해야함.
// `responseType` indicates the type of data that the server will respond with
// options are: 'arraybuffer', 'document', 'json', 'text', 'stream'
// https://axios-http.com/docs/req_config
responseType: "arraybuffer",
}
)
.then(function(response) {
console.log("성공");
let bytes = new Uint8Array(response.data);
let sample = Course.deserializeBinary(bytes)
setCourse(sample)
})
.catch(function(error) {
console.log("실패", error);
})
}
import React, { useState } from "react";
import axios from "axios"
import {Course} from "./protobuf/sample_pb";
function AxiosApi() {
// photos, setPhotos 비구조화 할당
let [course, setCourse] = useState(null);
// 통신 메서드
function searchApi() {
const url = "/courses/1";
axios.get(url, {
headers:{
'Accept': 'application/x-protobuf',
'MediaType': 'application/x-protobuf',
// 'Content-Type': 'application/x-protobuf',
},
responseType: "arraybuffer",
})
.then(function(response) {
let bytes = new Uint8Array(response.data);
let sample = Course.deserializeBinary(bytes)
setCourse(sample)
})
.catch(function(error) {
console.log("실패", error);
course = null
})
}
function sendApi() {
const url = '/courses'
let course = new Course()
course.setId(2)
course.setCourseName("Test")
let courseBytes = course.serializeBinary()
axios.post(
url,
courseBytes,
{
headers: {
'Accept': 'application/x-protobuf',
'MediaType': 'application/x-protobuf',
'Content-Type': 'application/x-protobuf',
},
responseType: "arraybuffer",
}
)
.then(function(response) {
let bytes = new Uint8Array(response.data);
let sample = Course.deserializeBinary(bytes)
setCourse(sample)
})
.catch(function(error) {
console.log("실패", error);
})
}
// 조회 데이터 존재할 경우
if(course !== null) {
return (
<div>
<div>id:{course.id} / {course.getId()}</div>
<div>courseName:{course.courseName} / {course.getCourseName()}</div>
<div>
<button onClick={searchApi}> 다시 불러오기 </button>
</div>
<div>
<button onClick={sendApi}> Submit </button>
</div>
</div>
);
} else { // 조회 데이터 존재하지 않을 경우
return (
<div>
<button onClick={searchApi}> 불러오기 </button>
</div>
)
}
}
export default AxiosApi;
sample project : https://github.com/clevekim00/spring-protobuf