1. generate
protoc --go_out=plugins=grpc:{b} {a}
a: proto 파일의 경로
b: a경로를 기준으로한, generate된 코드가 나올 경로
2. language
잠시 보다보면 별거 없다.
syntax = "proto3";
package protocol;
//interface (golang기준)
service UserService {
rpc ListUser(ListUserRequestType) returns (ListUserResponseType) {}
rpc RegisterUser(RegisterUserRequestType) returns (RegisterUserResponseType) {}
}
//struct
message User {
//field
string id = 1;
string email = 2;
}
//Request
message ListUserRequestType {
}
//Response
//어려울 것 없다. 그냥 서버사이드의 request, response 객체 느낌 생각하면 된다.
message ListUserResponseType {
//slice(array) 아래 #1 참조
repeated User users =1;
}
message RegisterUserRequestType {
string email = 1;
}
message RegisterUserResponseType {
}
/*
#1
If a field is repeated, the field may be repeated any number of times (including zero).
The order of the repeated values will be preserved in the protocol buffer.
Think of repeated fields as dynamically sized arrays.
*/
이걸 generate하면
// Code generated by protoc-gen-go. DO NOT EDIT.
// source: app/interface/rpc/v1.0/protocol/user_service.proto
package protocol
import proto "github.com/golang/protobuf/proto"
import fmt "fmt"
import math "math"
import (
context "golang.org/x/net/context"
grpc "google.golang.org/grpc"
)
// Reference imports to suppress errors if they are not otherwise used.
var _ = proto.Marshal
var _ = fmt.Errorf
var _ = math.Inf
// This is a compile-time assertion to ensure that this generated file
// is compatible with the proto package it is being compiled against.
// A compilation error at this line likely means your copy of the
// proto package needs to be updated.
const _ = proto.ProtoPackageIsVersion2 // please upgrade the proto package
type User struct {
Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"`
Email string `protobuf:"bytes,2,opt,name=email,proto3" json:"email,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *User) Reset() { *m = User{} }
func (m *User) String() string { return proto.CompactTextString(m) }
func (*User) ProtoMessage() {}
func (*User) Descriptor() ([]byte, []int) {
return fileDescriptor_user_service_8e150b7eb2f667ef, []int{0}
}
func (m *User) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_User.Unmarshal(m, b)
}
func (m *User) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_User.Marshal(b, m, deterministic)
}
func (dst *User) XXX_Merge(src proto.Message) {
xxx_messageInfo_User.Merge(dst, src)
}
func (m *User) XXX_Size() int {
return xxx_messageInfo_User.Size(m)
}
func (m *User) XXX_DiscardUnknown() {
xxx_messageInfo_User.DiscardUnknown(m)
}
var xxx_messageInfo_User proto.InternalMessageInfo
func (m *User) GetId() string {
if m != nil {
return m.Id
}
return ""
}
func (m *User) GetEmail() string {
if m != nil {
return m.Email
}
return ""
}
type ListUserRequestType struct {
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *ListUserRequestType) Reset() { *m = ListUserRequestType{} }
func (m *ListUserRequestType) String() string { return proto.CompactTextString(m) }
func (*ListUserRequestType) ProtoMessage() {}
func (*ListUserRequestType) Descriptor() ([]byte, []int) {
return fileDescriptor_user_service_8e150b7eb2f667ef, []int{1}
}
func (m *ListUserRequestType) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_ListUserRequestType.Unmarshal(m, b)
}
func (m *ListUserRequestType) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_ListUserRequestType.Marshal(b, m, deterministic)
}
func (dst *ListUserRequestType) XXX_Merge(src proto.Message) {
xxx_messageInfo_ListUserRequestType.Merge(dst, src)
}
func (m *ListUserRequestType) XXX_Size() int {
return xxx_messageInfo_ListUserRequestType.Size(m)
}
func (m *ListUserRequestType) XXX_DiscardUnknown() {
xxx_messageInfo_ListUserRequestType.DiscardUnknown(m)
}
var xxx_messageInfo_ListUserRequestType proto.InternalMessageInfo
type ListUserResponseType struct {
Users []*User `protobuf:"bytes,1,rep,name=users,proto3" json:"users,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *ListUserResponseType) Reset() { *m = ListUserResponseType{} }
func (m *ListUserResponseType) String() string { return proto.CompactTextString(m) }
func (*ListUserResponseType) ProtoMessage() {}
func (*ListUserResponseType) Descriptor() ([]byte, []int) {
return fileDescriptor_user_service_8e150b7eb2f667ef, []int{2}
}
func (m *ListUserResponseType) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_ListUserResponseType.Unmarshal(m, b)
}
func (m *ListUserResponseType) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_ListUserResponseType.Marshal(b, m, deterministic)
}
func (dst *ListUserResponseType) XXX_Merge(src proto.Message) {
xxx_messageInfo_ListUserResponseType.Merge(dst, src)
}
func (m *ListUserResponseType) XXX_Size() int {
return xxx_messageInfo_ListUserResponseType.Size(m)
}
func (m *ListUserResponseType) XXX_DiscardUnknown() {
xxx_messageInfo_ListUserResponseType.DiscardUnknown(m)
}
var xxx_messageInfo_ListUserResponseType proto.InternalMessageInfo
func (m *ListUserResponseType) GetUsers() []*User {
if m != nil {
return m.Users
}
return nil
}
type RegisterUserRequestType struct {
Email string `protobuf:"bytes,1,opt,name=email,proto3" json:"email,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *RegisterUserRequestType) Reset() { *m = RegisterUserRequestType{} }
func (m *RegisterUserRequestType) String() string { return proto.CompactTextString(m) }
func (*RegisterUserRequestType) ProtoMessage() {}
func (*RegisterUserRequestType) Descriptor() ([]byte, []int) {
return fileDescriptor_user_service_8e150b7eb2f667ef, []int{3}
}
func (m *RegisterUserRequestType) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_RegisterUserRequestType.Unmarshal(m, b)
}
func (m *RegisterUserRequestType) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_RegisterUserRequestType.Marshal(b, m, deterministic)
}
func (dst *RegisterUserRequestType) XXX_Merge(src proto.Message) {
xxx_messageInfo_RegisterUserRequestType.Merge(dst, src)
}
func (m *RegisterUserRequestType) XXX_Size() int {
return xxx_messageInfo_RegisterUserRequestType.Size(m)
}
func (m *RegisterUserRequestType) XXX_DiscardUnknown() {
xxx_messageInfo_RegisterUserRequestType.DiscardUnknown(m)
}
var xxx_messageInfo_RegisterUserRequestType proto.InternalMessageInfo
func (m *RegisterUserRequestType) GetEmail() string {
if m != nil {
return m.Email
}
return ""
}
type RegisterUserResponseType struct {
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *RegisterUserResponseType) Reset() { *m = RegisterUserResponseType{} }
func (m *RegisterUserResponseType) String() string { return proto.CompactTextString(m) }
func (*RegisterUserResponseType) ProtoMessage() {}
func (*RegisterUserResponseType) Descriptor() ([]byte, []int) {
return fileDescriptor_user_service_8e150b7eb2f667ef, []int{4}
}
func (m *RegisterUserResponseType) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_RegisterUserResponseType.Unmarshal(m, b)
}
func (m *RegisterUserResponseType) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_RegisterUserResponseType.Marshal(b, m, deterministic)
}
func (dst *RegisterUserResponseType) XXX_Merge(src proto.Message) {
xxx_messageInfo_RegisterUserResponseType.Merge(dst, src)
}
func (m *RegisterUserResponseType) XXX_Size() int {
return xxx_messageInfo_RegisterUserResponseType.Size(m)
}
func (m *RegisterUserResponseType) XXX_DiscardUnknown() {
xxx_messageInfo_RegisterUserResponseType.DiscardUnknown(m)
}
var xxx_messageInfo_RegisterUserResponseType proto.InternalMessageInfo
func init() {
proto.RegisterType((*User)(nil), "protocol.User")
proto.RegisterType((*ListUserRequestType)(nil), "protocol.ListUserRequestType")
proto.RegisterType((*ListUserResponseType)(nil), "protocol.ListUserResponseType")
proto.RegisterType((*RegisterUserRequestType)(nil), "protocol.RegisterUserRequestType")
proto.RegisterType((*RegisterUserResponseType)(nil), "protocol.RegisterUserResponseType")
}
// Reference imports to suppress errors if they are not otherwise used.
var _ context.Context
var _ grpc.ClientConn
// This is a compile-time assertion to ensure that this generated file
// is compatible with the grpc package it is being compiled against.
const _ = grpc.SupportPackageIsVersion4
// UserServiceClient is the client API for UserService service.
//
// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://godoc.org/google.golang.org/grpc#ClientConn.NewStream.
type UserServiceClient interface {
ListUser(ctx context.Context, in *ListUserRequestType, opts ...grpc.CallOption) (*ListUserResponseType, error)
RegisterUser(ctx context.Context, in *RegisterUserRequestType, opts ...grpc.CallOption) (*RegisterUserResponseType, error)
}
type userServiceClient struct {
cc *grpc.ClientConn
}
func NewUserServiceClient(cc *grpc.ClientConn) UserServiceClient {
return &userServiceClient{cc}
}
func (c *userServiceClient) ListUser(ctx context.Context, in *ListUserRequestType, opts ...grpc.CallOption) (*ListUserResponseType, error) {
out := new(ListUserResponseType)
err := c.cc.Invoke(ctx, "/protocol.UserService/ListUser", in, out, opts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *userServiceClient) RegisterUser(ctx context.Context, in *RegisterUserRequestType, opts ...grpc.CallOption) (*RegisterUserResponseType, error) {
out := new(RegisterUserResponseType)
err := c.cc.Invoke(ctx, "/protocol.UserService/RegisterUser", in, out, opts...)
if err != nil {
return nil, err
}
return out, nil
}
// UserServiceServer is the server API for UserService service.
type UserServiceServer interface {
ListUser(context.Context, *ListUserRequestType) (*ListUserResponseType, error)
RegisterUser(context.Context, *RegisterUserRequestType) (*RegisterUserResponseType, error)
}
func RegisterUserServiceServer(s *grpc.Server, srv UserServiceServer) {
s.RegisterService(&_UserService_serviceDesc, srv)
}
func _UserService_ListUser_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(ListUserRequestType)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(UserServiceServer).ListUser(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: "/protocol.UserService/ListUser",
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(UserServiceServer).ListUser(ctx, req.(*ListUserRequestType))
}
return interceptor(ctx, in, info, handler)
}
func _UserService_RegisterUser_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(RegisterUserRequestType)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(UserServiceServer).RegisterUser(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: "/protocol.UserService/RegisterUser",
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(UserServiceServer).RegisterUser(ctx, req.(*RegisterUserRequestType))
}
return interceptor(ctx, in, info, handler)
}
var _UserService_serviceDesc = grpc.ServiceDesc{
ServiceName: "protocol.UserService",
HandlerType: (*UserServiceServer)(nil),
Methods: []grpc.MethodDesc{
{
MethodName: "ListUser",
Handler: _UserService_ListUser_Handler,
},
{
MethodName: "RegisterUser",
Handler: _UserService_RegisterUser_Handler,
},
},
Streams: []grpc.StreamDesc{},
Metadata: "app/interface/rpc/v1.0/protocol/user_service.proto",
}
func init() {
proto.RegisterFile("app/interface/rpc/v1.0/protocol/user_service.proto", fileDescriptor_user_service_8e150b7eb2f667ef)
}
var fileDescriptor_user_service_8e150b7eb2f667ef = []byte{
// 249 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x74, 0x4f, 0xcd, 0x4a, 0xc3, 0x40,
0x10, 0x76, 0xa3, 0x95, 0x3a, 0x95, 0x1e, 0xc6, 0x8a, 0x21, 0xa0, 0xd4, 0xc5, 0x43, 0x0f, 0x92,
0xd5, 0x78, 0xf5, 0x0d, 0xf4, 0x14, 0x15, 0x8f, 0x12, 0xd3, 0x51, 0x16, 0x6a, 0xb3, 0xee, 0x6c,
0x0b, 0x3e, 0x97, 0x2f, 0x28, 0xbb, 0x21, 0x6c, 0x2c, 0xed, 0x71, 0xbe, 0xf9, 0x7e, 0xa1, 0xa8,
0x8c, 0x51, 0x7a, 0xe9, 0xc8, 0x7e, 0x54, 0x35, 0x29, 0x6b, 0x6a, 0xb5, 0xbe, 0xcd, 0x6f, 0x94,
0xb1, 0x8d, 0x6b, 0xea, 0x66, 0xa1, 0x56, 0x4c, 0xf6, 0x8d, 0xc9, 0xae, 0x75, 0x4d, 0x79, 0x40,
0x71, 0xd8, 0x3d, 0xe5, 0x35, 0x1c, 0xbc, 0x30, 0x59, 0x1c, 0x43, 0xa2, 0xe7, 0xa9, 0x98, 0x8a,
0xd9, 0x51, 0x99, 0xe8, 0x39, 0x4e, 0x60, 0x40, 0x5f, 0x95, 0x5e, 0xa4, 0x49, 0x80, 0xda, 0x43,
0x9e, 0xc2, 0xc9, 0xa3, 0x66, 0xe7, 0x15, 0x25, 0x7d, 0xaf, 0x88, 0xdd, 0xf3, 0x8f, 0x21, 0x79,
0x0f, 0x93, 0x08, 0xb3, 0x69, 0x96, 0x4c, 0x1e, 0xc7, 0x2b, 0x18, 0xf8, 0x70, 0x4e, 0xc5, 0x74,
0x7f, 0x36, 0x2a, 0xc6, 0x79, 0x17, 0x9b, 0x07, 0x6a, 0xfb, 0x94, 0x0a, 0xce, 0x4a, 0xfa, 0xd4,
0xec, 0xc8, 0x6e, 0x18, 0xc7, 0x16, 0xa2, 0xdf, 0x22, 0x83, 0xf4, 0xbf, 0x20, 0x46, 0x16, 0xbf,
0x02, 0x46, 0x1e, 0x7c, 0x6a, 0xf7, 0xe2, 0x03, 0x0c, 0xbb, 0x6a, 0x78, 0x1e, 0xf3, 0xb7, 0xac,
0xc8, 0x2e, 0xb6, 0xbd, 0xa3, 0xb5, 0xdc, 0xc3, 0x57, 0x38, 0xee, 0x07, 0xe3, 0x65, 0x54, 0xec,
0x58, 0x90, 0xc9, 0x5d, 0x94, 0xbe, 0xf1, 0xfb, 0x61, 0x20, 0xdd, 0xfd, 0x05, 0x00, 0x00, 0xff,
0xff, 0x6d, 0x19, 0x74, 0x30, 0xcc, 0x01, 0x00, 0x00,
}
the code comes out like this. 잘 보면 interface로 UserServiceServer, UserServiceClient가 존재한다.
아래는 위 소스를 우리가 사용해야하는 부분 위주로 간츄려 본 것이다.
//struct가 정의 된다.
type User struct
func (m *User) GetId() string
func (m *User) GetEmail() string
//request에는 담아 보낼게 없었으므로
type ListUserRequestType struct
//response는 User 배열을 나른다.
type ListUserResponseType struct
func (m *ListUserResponseType) GetUsers() []*User
//email을 나른다.
type RegisterUserRequestType struct
func (m *RegisterUserRequestType) GetEmail() string
type RegisterUserResponseType struct
//client나 server는 protobuff의 service부분을 이용해 각각의 interface를 만들어 가진다.
//서로 다른 점이라 한다면, client는 grpc.ClientConn을 필드 오브젝트로 가진 struct가 존재하며,
//server는 Handler를 가지는데 위 전체 소스에서 볼 수있듯이 concret object를 받아 usecase를 구현해 사용한다.
type UserServiceClient interface
type userServiceClient struct
func NewUserServiceClient(cc *grpc.ClientConn) UserServiceClient
func (c *userServiceClient) ListUser(ctx context.Context, in *ListUserRequestType, opts ...grpc.CallOption) (*ListUserResponseType, error)
func (c *userServiceClient) RegisterUser(ctx context.Context, in *RegisterUserRequestType, opts ...grpc.CallOption) (*RegisterUserResponseType, error)
type UserServiceServer interface
func RegisterUserServiceServer(s *grpc.Server, srv UserServiceServer)
func _UserService_ListUser_Handler(srv interface, ctx context.Context, dec func(interface) error, interceptor grpc.UnaryServerInterceptor) (interface, error)
func _UserService_RegisterUser_Handler(srv interface, ctx context.Context, dec func(interface) error, interceptor grpc.UnaryServerInterceptor) (interface, error)
3. 후기
protocol buffer는 기본적으로 proto파일을 정의하고 그에 맞게 소스를 generate하여 사용한다.
협업시에 이용한다면 proto파일을 정의문서로도 사용하기 좋을것 같고, usecase구현 부분만 잘 관리하면 의존성이라던가 하는 여러 귀찮은 요소들이 깔끔하게 정리 될 수있을 것 같다.
신기술 탐방삼아 구현해본 grpc이므로 벤치마크등의 성능테스트는 생략한다.
비교해놓은 사람들 많으니 google에 검색해보자!
시간나면 rest vs grpc vs graphql을 제대로 한번 비교 해 보고싶다