gRPC
gRPC
gRPC๋ ๊ณ ์ฑ๋ฅ ์คํ ์์ค ๋ฒ์ฉ RPC ํ๋ ์ ์ํฌ์ ๋๋ค.
Installation
์์ํ๊ธฐ ์ ์ ํ์ํ ํจํค์ง๋ฅผ ์ค์นํด์ผํฉ๋๋ค.
$ npm i --save grpc @grpc/proto-loader
Transporter
gRPC ์ ์ก๊ธฐ๋ก ์ ํํ๋ ค๋ฉด createMicroservice()
๋ฉ์๋์ ์ ๋ฌ๋ ์ต์
๊ฐ์ฒด๋ฅผ ์์ ํด์ผ ํฉ๋๋ค.
@@filename(main)
const app = await NestFactory.createMicroservice(ApplicationModule, {
transport: Transport.GRPC,
options: {
package: 'hero',
protoPath: join(__dirname, 'hero/hero.proto'),
},
});
info ํํธ
join()
ํจ์๋path
ํจํค์ง์์ ๊ฐ์ ธ์ค๊ณTransport
์ด๊ฑฐ๋@nestjs/microservices
์์ ์จ ๊ฒ์ ๋๋ค.
Options
์ ์ก๊ธฐ ๋์์ ๊ฒฐ์ ํ๋ ์ฌ์ฉ ๊ฐ๋ฅํ ์ต์ ์ด ๋ง์ด ์์ต๋๋ค.
url
Connection url
protoLoader
NPM ํจํค์ง ์ด๋ฆ (๋ค๋ฅธ ํ๋กํ ๋ก๋๋ฅผ ์ฌ์ฉํ๋ ค๋ ๊ฒฝ์ฐ)
protoPath
.proto
ํ์ผ์ ์ ๋ (๋๋ ๋ฃจํธ ๋๋ ํ ๋ฆฌ์ ๋ํ) ๊ฒฝ๋ก
loader
@grpc/proto-loader
์ต์
. ์ฌ๊ธฐ.์ ์ ์ค๋ช
๋์ด ์์ต๋๋ค
package
Protobuf package name
credentials
Server credentials (read more)
Overview
์ผ๋ฐ์ ์ผ๋ก package
์์ฑ์ protobuf ํจํค์ง ์ด๋ฆ์ ์ค์ ํ๊ณ protoPath
๋.proto
์ ์ ํ์ผ์ ๊ฒฝ๋ก์
๋๋ค. hero.proto
ํ์ผ์ ํ๋กํ ์ฝ ๋ฒํผ ์ธ์ด๋ฅผ ์ฌ์ฉํ์ฌ ๊ตฌ์ฑ๋ฉ๋๋ค.
syntax = "proto3";
package hero;
service HeroService {
rpc FindOne (HeroById) returns (Hero) {}
}
message HeroById {
int32 id = 1;
}
message Hero {
int32 id = 1;
string name = 2;
}
์์ ์์์, ์ฐ๋ฆฌ๋ HeroById
๋ฅผ ์
๋ ฅ์ผ๋ก ์์ํ๊ณ Hero
๋ฉ์์ง๋ฅผ ๋ฆฌํดํ๋ FindOne()
gRPC ํธ๋ค๋ฌ๋ฅผ ๋
ธ์ถํ๋ HeroService
๋ฅผ ์ ์ํ์ต๋๋ค. ์ด ํ๋กํ ํ์
์ ์๋ฅผ ์ถฉ์กฑ์ํค๋ ํธ๋ค๋ฌ๋ฅผ ์ ์ํ๋ ค๋ฉด @GrpcMethod()
๋ฐ์ฝ๋ ์ดํฐ๋ฅผ ์ฌ์ฉํด์ผํฉ๋๋ค. ์ด์ ์ ์๋ ค์ง @MessagePattern()
์ ๋ ์ด์ ์ ์ฉํ์ง ์์ต๋๋ค.
@@filename(hero.controller)
@GrpcMethod('HeroService', 'FindOne')
findOne(data: HeroById, metadata: any): Hero {
const items = [
{ id: 1, name: 'John' },
{ id: 2, name: 'Doe' },
];
return items.find(({ id }) => id === data.id);
}
info ํํธ
@GrpcMethod()
๋ฐ์ฝ๋ ์ดํฐ๋@nestjs/microservices
ํจํค์ง์์ ๊ฐ์ ธ์ต๋๋ค.
HeroService
๋ ์๋น์ค ์ด๋ฆ์ธ ๋ฐ๋ฉด, FindOne
์ FindOne()
gRPC ํธ๋ค๋ฌ๋ฅผ ๊ฐ๋ฆฌํต๋๋ค. ํด๋นํ๋ findOne()
๋ฉ์๋๋ ๋๊ฐ์ง ์ธ์, ์ฆ ํธ์ถ์๋ก๋ถํฐ ์ ๋ฌ๋ data
์ gRPC ์์ฒญ์ ๋ฉํ ๋ฐ์ดํฐ๋ฅผ ์ ์ฅํ๋ metadata
๋ฅผ ์ทจํฉ๋๋ค.
๋ํ, FindOne
์ ์ค์ ๋ก ์ฌ๊ธฐ์ ์ค๋ณต๋ฉ๋๋ค. @GrpcMethod()
์ ๋ ๋ฒ์งธ ์ธ์๋ฅผ ์ ๋ฌํ์ง ์์ผ๋ฉด Nest๋ ์๋์ผ๋ก ์ฒซ ๋ฒ์งธ ๋ฌธ์ (์ :findOne
->FindOne
)์ ํจ๊ป ๋ฉ์๋ ์ด๋ฆ์ ์ฌ์ฉํฉ๋๋ค.
@@filename(hero.controller)
@Controller()
export class HeroService {
@GrpcMethod()
findOne(data: HeroById, metadata: any): Hero {
const items = [
{ id: 1, name: 'John' },
{ id: 2, name: 'Doe' },
];
return items.find(({ id }) => id === data.id);
}
}
๋ง์ฐฌ๊ฐ์ง๋ก ์ธ์๋ฅผ ์ ๋ฌํ์ง ๋ชปํ ์๋ ์์ต๋๋ค. ์ด ๊ฒฝ์ฐ Nest๋ ํด๋์ค ์ด๋ฆ์ ์ฌ์ฉํฉ๋๋ค.
@@filename(hero.controller)
@Controller()
export class HeroService {
@GrpcMethod()
findOne(data: HeroById, metadata: any): Hero {
const items = [
{ id: 1, name: 'John' },
{ id: 2, name: 'Doe' },
];
return items.find(({ id }) => id === data.id);
}
}
Client
ํด๋ผ์ด์ธํธ ์ธ์คํด์ค๋ฅผ ๋ง๋ค๋ ค๋ฉด @Client()
๋ฐ์ฝ๋ ์ดํฐ๋ฅผ ์ฌ์ฉํด์ผ ํฉ๋๋ค.
@Client({
transport: Transport.GRPC,
options: {
package: 'hero',
protoPath: join(__dirname, 'hero/hero.proto'),
},
})
client: ClientGrpc;
์ด์ ์์ ๋น๊ตํ์ฌ ์ฝ๊ฐ์ ์ฐจ์ด๊ฐ ์์ต๋๋ค. ClientProxy
ํด๋์ค ๋์ , getService()
๋ฉ์๋๋ฅผ ์ ๊ณตํ๋ ClientGrpc
๋ฅผ ์ฌ์ฉํฉ๋๋ค. getService()
์ ๋ค๋ฆญ ๋ฉ์๋๋ ์๋น์ค ์ด๋ฆ์ ์ธ์๋ก ์ฌ์ฉํ๊ณ ๊ฐ๋ฅํ ๊ฒฝ์ฐ ํด๋น ์ธ์คํด์ค๋ฅผ ๋ฐํํฉ๋๋ค.
@@filename(hero.controller)
onModuleInit() {
this.heroService = this.client.getService<HeroService>('HeroService');
}
heroService
๊ฐ์ฒด๋ .proto
ํ์ผ ๋ด์ ์ ์๋ ๊ฒ๊ณผ ๋์ผํ ๋ฉ์๋ ์ธํธ๋ฅผ ๋
ธ์ถํฉ๋๋ค. ๋ชจ๋ ๊ท์น์ ์๋ฌธ์ (์์ฐ ๊ท์น์ ๋ฐ๋ฅด๊ธฐ ์ํด)์
๋๋ค. ๊ธฐ๋ณธ์ ์ผ๋ก gRPC HeroService
์ ์์๋ FindOne()
ํจ์๊ฐ ํฌํจ๋์ด ์์ต๋๋ค. heroService
์ธ์คํด์ค๋ findOne()
๋ฉ์๋๋ฅผ ์ ๊ณตํ๋ค๋ ์๋ฏธ์
๋๋ค.
interface HeroService {
findOne(data: { id: number }): Observable<any>;
}
๋ชจ๋ ์๋น์ค ๋ฉ์๋๋ Observable
์ ๋ฆฌํดํฉ๋๋ค. Nest๋ RxJS ์คํธ๋ฆผ์ ์ง์ํ๊ณ ์ ์๋ํ๋ฏ๋ก HTTP ์ฒ๋ฆฌ๊ธฐ ๋ด์์๋ ๋ฐํํ ์ ์์ต๋๋ค.
@@filename(hero.controller)
@Get()
call(): Observable<any> {
return this.heroService.findOne({ id: 1 });
}
์ ์ฒด ์์ ์๋ ์ฌ๊ธฐ์์ ํ์ธํ ์ ์์ต๋๋ค.
gRPC Streaming
GRPC ์์ฒด๋ ์คํธ๋ฆผ
์ผ๋ก ์๋ ค์ง ๋กฑํ
๋ผ์ด๋ธ ์ฐ๊ฒฐ์ ์ง์ํฉ๋๋ค. ์คํธ๋ฆผ์ ์ฑํ
, ๊ด์ฐฐ ๋๋ ์ฒญํฌ ๋ฐ์ดํฐ ์ ์ก๊ณผ ๊ฐ์ ์๋น์ค ์ฌ๋ก์ ๋งค์ฐ ์ ์ฉํ ๋๊ตฌ๊ฐ ๋ ์ ์์ต๋๋ค. ๊ณต์ ๋ฌธ์ (์ฌ๊ธฐ)์์ ์์ธํ ๋ด์ฉ์ ํ์ธํ ์ ์์ต๋๋ค.
Nest๋ ๋ ๊ฐ์ง ๋ฐฉ๋ฒ์ผ๋ก GRPC ์คํธ๋ฆผ ํธ๋ค๋ฌ๋ฅผ ์ง์ํฉ๋๋ค.
RxJS
Subject
+Observable
ํธ๋ค๋ฌ: Controller ๋ฉ์๋ ๋ด์์ ๋ฐ๋ก ์๋ต์ ์ฐ๊ฑฐ๋Subject
/Observable
์๋น์์๊ฒ ์ ๋ฌํ๋ ๋ฐ ์ ์ฉ ํ ์ ์์ต๋๋ค์์ GRPC ํธ์ถ ์คํธ๋ฆผ ํธ๋ค๋ฌ: ๋ ธ๋ ํ์ค
Duplex
์คํธ๋ฆผ ํธ๋ค๋ฌ์ ๋ํ ๋๋จธ์ง ๋์คํจ์น๋ฅผ ์ฒ๋ฆฌํ๋ ์ผ๋ถ ์คํ๊ธฐ์ ์ ๋ฌํ๋ ๋ฐ ์ ์ฉ ํ ์ ์์ต๋๋ค.
Subject strategy
@GrpcStreamMethod()
๋ฐ์ฝ๋ ์ดํฐ๋ ํจ์ ๋งค๊ฐ ๋ณ์๋ฅผ RxJS Observable
๋ก ์ ๊ณตํฉ๋๋ค.
// Set decorator with selecting a Service definition from protobuf package
// the string is matching to: package proto_example.orders.OrdersService
@GrpcStreamMethod('orders.OrderService')
handleStream(messages: Observable<any>): Observable<any> {
const subject = new Subject();
messages.subscribe(message => {
console.log(message);
subject.next({
shipmentType: {
carrier: 'test-carrier',
},
});
});
return subject.asObservable();
}
@GrpcStreamMethod()
๋ฐ์ฝ๋ ์ดํฐ์์ ์ ์ด์ค ์ํธ ์์ฉ์ ์ง์ํ๋ ค๋ฉด ์ปจํธ๋กค๋ฌ ๋ฉ์๋์์ RxJS 'Observable'์ ๋ฐํํด์ผ ํฉ๋๋ค.
Pure GRPC call stream handler
@GrpcStreamCall()
๋ฐ์ฝ๋ ์ดํฐ๋ grpc.ServerDuplexStream๊ณผ ๊ฐ์ ํจ์ ๋งค๊ฐ ๋ณ์๋ฅผ ์ ๊ณตํ๋ฉฐ .on('data',callback)
, .write(message)
๋๋ .cancel()
๊ณผ ๊ฐ์ ํ์ค ๋ฉ์๋๋ฅผ ์ง์ํฉ๋๋ค. ์ฌ์ฉ ๊ฐ๋ฅํ ๋ฐฉ๋ฒ์ ๋ํ ์ ์ฒด ์ค๋ช
์๋ ์ฌ๊ธฐ์์ ์ฐพ์ ์ ์์ต๋๋ค.
// Set decorator with selecting a Service definition from protobuf package
// the string is matching to: package proto_example.orders.OrdersService
@GrpcStreamCall('orders.OrderService')
handleStream(stream: any) {
stream.on('data', (msg: any) => {
console.log(msg);
// Answer here or anywhere else using stream reference
stream.write({
shipmentType: {
carrier: 'test-carrier',
},
});
});
}
์ด ๋ฐ์ฝ๋ ์ดํฐ์๋ ํน์ ๋ฆฌํด ๋งค๊ฐ ๋ณ์๋ฅผ ์ ๊ณตํ ํ์๊ฐ ์์ต๋๋ค. ์คํธ๋ฆผ์ ๋ค๋ฅธ ํ์ค ์คํธ๋ฆผ ์ ํ๊ณผ ๊ฐ์ ๋ฐฉ์์ผ๋ก ์ฒ๋ฆฌ๋ ๊ฒ์ผ๋ก ์์๋ฉ๋๋ค.
Last updated
Was this helpful?