Dec 20, 2020 โ€ข15 min read โ˜•

Golang gRPC server ๊ตฌ์ถ•ํ•˜๊ธฐ (1) - gRPC๋ž€ ๋ฌด์—‡์ธ๊ฐ€?

main

์˜ฌํ•ด ์ž…์‚ฌ ํ›„์— Golang์œผ๋กœ ๋งŽ์€ ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ–ˆ๋‹ค. Golang์œผ๋กœ gRPC ํ†ต์‹ ํ•˜๋Š” ์—ฌ๋Ÿฌ microservice ๋“ค์„ ๊ฐœ๋ฐœํ–ˆ๋‹ค. ๊ทธ๋Ÿฌ๋‹ค๊ฐ€ ๋ฌธ๋“, Golang์œผ๋กœ ๋งŽ์ด ๊ฐœ๋ฐœ ํ•˜๊ธด ํ–ˆ์ง€๋งŒ ๋‚ด๊ฐ€ gRPC์™€ gRPC server, gRPC gateway, middleware๋“ฑ์— ๋Œ€ํ•ด ์ •ํ™•ํ•˜๊ฒŒ ์ดํ•ดํ•˜๊ณ  ์žˆ๋‚˜? ๋ผ๋Š” ์ƒ๊ฐ์ด ๋“ค์—ˆ๋‹ค. ๊ทธ๋ž˜์„œ ๋‚ด๊ฐ€ ์ž˜ ์ดํ•ดํ•˜๊ณ  ์žˆ๋Š”๊ฐ€ ํ™•์ธํ•˜๊ธฐ ์œ„ํ•ด ๋‚ด๊ฐ€ ์‚ฌ์šฉํ•˜๊ณ  ์žˆ๋Š” ๊ธฐ์ˆ ๋“ค์„ ๊ณต๋ถ€ํ•˜๋ฉฐ ์ •๋ฆฌ ์ฐจ์›์—์„œ ๊ธ€์„ ์ž‘์„ฑํ•˜๋ ค๊ณ  ํ•œ๋‹ค.

gRPC ๊ฐœ๋… ์ •๋ฆฌ๋ถ€ํ„ฐ gRPC๋กœ ํ†ต์‹ ํ•˜๋Š” Golang server๋ฅผ ๊ตฌ์ถ•ํ•˜๋Š” ๊ฒƒ๊นŒ์ง€ ์ด๋ก ๋ถ€ํ„ฐ ์‹ค์ „๊นŒ์ง€ step by step์œผ๋กœ ์ฐจ๊ทผ์ฐจ๊ทผํžˆ ์‹œ๋ฆฌ์ฆˆ๋กœ ์ •๋ฆฌํ•ด๋‚˜๊ฐ€๋ ค๊ณ  ํ•œ๋‹ค.

Contents

  1. Golang gRPC server ๊ตฌ์ถ•ํ•˜๊ธฐ (1) - gRPC๋ž€ ๋ฌด์—‡์ธ๊ฐ€?
  2. Golang gRPC server ๊ตฌ์ถ•ํ•˜๊ธฐ (2)- gRPC server ํŒŒํ—ค์ณ๋ณด๊ธฐ
  3. Golang gRPC server ๊ตฌ์ถ•ํ•˜๊ธฐ (3) - RESTfulํ•˜๊ฒŒ gRPC server์™€ ํ†ต์‹ ํ•˜๊ธฐ
  4. Golang gRPC server ๊ตฌ์ถ•ํ•˜๊ธฐ (4) - gRPC middleware๋ž€?

gRPC๋ž€?

gRPC์˜ ๊ฐœ๋…๊ณผ ์‚ฌ์šฉ๋ฒ•์„ ๋ฐ”๋กœ ์„ค๋ช…ํ•˜๋Š” ๊ฒƒ๋ณด๋‹ค, gRPC ๋Œ€์‹ ์— ์–ด๋–ค๊ฑธ ์‚ฌ์šฉํ•˜๊ณ  ์žˆ๋Š”์ง€ ์‚ดํŽด๋ณด๋ฉด ๋” gRPC๋ฅผ ์ดํ•ดํ•˜๋Š”๋ฐ ๋„์›€์ด ๋  ๊ฒƒ ๊ฐ™๋‹ค (๋‹ค๋ฅธ๋ง๋กœ ํ•˜๋ฉด, gRPC์˜ ๋Œ€์ฒด์ œ๊ฐ€ ๋ฌด์—‡์ธ์ง€).

gRPC ๋Œ€์‹ ์— ํ”ํžˆ ๊ฐ€์žฅ ๋งŽ์ด ์‚ฌ์šฉ๋˜๋Š” ๊ฒƒ์ด JSON์„ ์‚ฌ์šฉํ•œ HTTP API์ด๋‹ค. HTTP API๋Š”

  • Client โ†โ†’ Server
  • Server โ†โ†’ Server

๊ฐ„ ์„œ๋กœ ๋ฐ์ดํ„ฐ๋ฅผ ์ฃผ๊ณ  ๋ฐ›์„ ๋•Œ ์‚ฌ์šฉ๋˜๋Š” ํ†ต์‹  ๋ฐฉ๋ฒ•์ด๋ผ๊ณ  ์ƒ๊ฐํ•  ์ˆ˜ ์žˆ๋‹ค.

๋ฐ‘์— ์˜ˆ์ œ๋ฅผ ๋ณด๋ฉด Client๊ฐ€ HTTP๋กœ RESTfulํ•˜๊ฒŒ ์„œ๋ฒ„์—๊ฒŒ GET์œผ๋กœ post๋“ค์„ ์š”์ฒญ ํ•œ๋‹ค. ๊ทธ๋ฆฌ๊ณ  ์„œ๋ฒ„๋Š” ์š”์ฒญ์— ๋Œ€ํ•ด JSON์œผ๋กœ post๋“ค์„ ๋ฆฌ์ŠคํŠธ๋กœ ์ „๋‹ฌํ•˜๊ฒŒ ๋œ๋‹ค. (HTTP์™€ REST๋ผ๋Š” ๊ฐœ๋…์€ ๊ฑฐ์˜ ํ•ญ์ƒ ๋ถ™์–ด๋‹ค๋…€์„œ ์‚ฌ์šฉ๋œ๋‹ค, ์ด ๋ธ”๋กœ๊ทธ๋ฅผ ์ฐธ๊ณ ํ•˜๋ฉด ์ดํ•ด์— ๋„์›€๋œ๋‹ค)

client-server

์˜ˆ์ œ๋ฅผ ๋ณด๋ฉด ์•Œ ์ˆ˜ ์žˆ๋“ฏ์ด JSON์„ ์‚ฌ์šฉํ•œ HTTP API๋Š” ๋ฐ์ดํ„ฐ๋ฅผ ์„œ๋กœ ์ฃผ๊ณ  ๋ฐ›์„ ๋•Œ ์‚ฌ์šฉ๋œ๋‹ค.

๋ฐ‘์—๋Š” gRPC ํ™ˆํŽ˜์ด์ง€์— ๊ฐ€๋ฉด ๋ณผ ์ˆ˜ ์žˆ๋Š” ๊ทธ๋ฆผ์ด๋‹ค. ์ž์„ธํžˆ ๋“ค์—ฌ๋‹ค๋ณด๋ฉด ์œ„ HTTP API๋กœ ํ†ต์‹ ํ•˜๋Š” ๊ทธ๋ฆผ๊ณผ gRPC ํ™ˆํŽ˜์ด์ง€ ๊ทธ๋ฆผ์˜ ์ฐจ์ด์ ์€ GET๋Œ€์‹ ์— Proto Request๋ฅผ ์ „๋‹ฌํ•˜๋Š” ๊ฒƒ ๊ฐ™๊ณ , JSON๋Œ€์‹ ์— Proto Response๋ฅผ ์ „๋‹ฌํ•˜๋Š” ๊ฒƒ์ฒ˜๋Ÿผ ๋ณด์ธ๋‹ค. ๊ฒฐ๊ตญ, ํฐํ‹€์—์„œ ๋ดค์„ ๋•Œ๋Š” JSON์„ ์‚ฌ์šฉํ•œ HTTP API์™€ ๊ฐ™์ด gRPC๋„ ๊ฒฐ๊ตญ์—” client โ†โ†’ server, server โ†โ†’server๊ฐ„ ๋ฐ์ดํ„ฐ๋ฅผ ์ฃผ๊ณ  ๋ฐ›๋Š” ๋ฐฉ์‹ ์ค‘ ํ•˜๋‚˜๋ผ๊ณ  ์ƒ๊ฐํ•ด ๋ณผ ์ˆ˜ ์žˆ๋Š” ๊ฒƒ์ด๋‹ค.

grpc

์ด์ œ gRPC๊ฐ€ ๋Œ€๋žต ๋ญ”์ง€ ์ดํ•ด๋ฅผ ํ–ˆ๋‹ค๋ฉด, ์กฐ๊ธˆ ๋” ๊นŠ์ด gRPC์— ๋Œ€ํ•œ ์ดํ•ด๋„๋ฅผ ๋†’ํ˜€๋ณด๋ ค๊ณ  ํ•œ๋‹ค.

์ผ๋‹จ gRPC๊ฐ€ ๋ญ”์ง€ ์•Œ๋ ค๋ฉด RPC๊ฐ€ ๋ญ”์ง€ ์งš๊ณ  ๋„˜์–ด๊ฐ€์•ผ ํ•œ๋‹ค. RPC(Remote Procedure Call)๋Š” ์›๋ž˜ ๋ถ„์‚ฐํ™˜๊ฒฝ ์‹œ์Šคํ…œ์—์„œ ์„œ๋กœ ๋‹ค๋ฅธ ์ปดํ“จํ„ฐ ํ”„๋กœ๊ทธ๋žจ๋“ค์ด ์„œ๋กœ ๋‹ค๋ฅธ ์ฃผ์†Œ์—์„œ ์„œ๋กœ๋ฅผ ํ˜ธ์ถœํ•˜์ง€๋งŒ ๋งˆ์น˜ ๊ฐ™์€ ์ฃผ์†Œ์—์„œ ํ˜ธ์ถœํ•˜๋Š” ๊ฒƒ์ฒ˜๋Ÿผ ์ž‘๋™ํ•˜๊ฒŒ ํ•˜๋Š” ์›๊ฒฉ ํ”„๋กœ์‹œ์ ธ ํ”„๋กœํ† ์ฝœ์ด๋‹ค. ์ฆ‰, ํ”„๋กœ๊ทธ๋žจ๋“ค์€ ์„œ๋กœ๊ฐ€ ๋ˆ„๊ตฌ์ธ์ง€ ์•Œ ํ•„์š” ์—†์ด ์ •ํ•ด์ง„ ๋ฐฉ์‹๋Œ€๋กœ ๋‹จ์ˆœํžˆ ํ•จ์ˆ˜ ํ˜ธ์ถœ๋งŒ ํ•˜๋ฉด ๋˜๋Š” ๊ฒƒ์ด๋‹ค. ์œ„ ์‚ฌ์ง„์„ ์˜ˆ์ œ๋กœ ๋“ค๋ฉด Ruby Client๋Š” ํ†ต์‹ ํ•˜๊ณ  ์žˆ๋Š” C++ Service๊ฐ€ ์–ด๋Š ์œ„์น˜์— ์žˆ๋˜, ์–ด๋–ค ์ฝ”๋“œ๋กœ ์ž‘์„ฑ๋˜์—ˆ๋˜ ๊ฐ„์— ์ƒ๊ด€ ์—†์ด ์ •ํ•ด์ง„ Proto๋ผ๋Š” ๊ทœ๊ฒฉ๋งŒ ์žˆ์œผ๋ฉด ์ž์œ ๋กญ๊ฒŒ ๋ฐ์ดํ„ฐ๋ฅผ ์ฃผ๊ณ  ๋ฐ›์„ ์ˆ˜ ์žˆ๋Š” ๊ฒƒ์ด๋‹ค.

๊ทธ๋ ‡๋‹ค๋ฉด g RPC๋Š” ๋ฌด์—‡์ผ๊นŒ?

Google์ด ๋งŒ๋“  protobuf(a.k.a protocol buffer)๋ผ๋Š” ๋ฐฉ์‹์„ ์‚ฌ์šฉํ•ด RPC๋ผ๋Š” ํ”„๋กœํ† ์ฝœ์„ ๋ฐ์ดํ„ฐ๋ฅผ ์ฃผ๊ณ  ๋ฐ›๋Š” ํ”Œ๋žซํผ์„ ์˜๋ฏธํ•œ๋‹ค.

(๐Ÿคซ Google์ด ๋งŒ๋“ค์–ด์„œ gRPC์˜ g๋Š” google์˜ g๋ผ๋Š” ์†Œ๋ฌธ์ด ์žˆ๋‹ค - ๊ทธ๋ฆฌ๊ณ  ์‹ค์ œ๋กœ๋„ ๊ทธ๋Ÿด ๊ฒƒ์ด๋ผ๋Š” ์˜๊ฒฌ์ด ์ง€๋ฐฐ์ ์ด๋‹ค)

์—ฌ๊ธฐ๊นŒ์ง€ ์™”๋‹ค๋ฉด, gRPC๋Š” protobuf๋ผ๋Š” ๋ฐฉ์‹์„ ์‚ฌ์šฉํ•ด์„œ ํ†ต์‹ ํ•˜๋Š” ํ”„๋กœ๊ทธ๋žจ๋“ค๊ฐ„ ์„œ๋กœ์— ๋Œ€ํ•œ ๋„คํŠธ์›Œํฌ๋‚˜ ์ฃผ์†Œ ๋“ฑ์— ๋Œ€ํ•œ ์ •๋ณด๋ฅผ ๋ชจ๋ฅด๊ณ ๋„ ๋ฐ์ดํ„ฐ๋ฅผ ์ฃผ๊ณ  ๋ฐ›์„ ์ˆ˜ ์žˆ๋Š” ํ†ต์‹  ๋ฐฉ์‹ ์ค‘ ํ•˜๋‚˜๋ผ๊ณ  ์ดํ•ดํ•  ์ˆ˜ ์žˆ์„ ๊ฒƒ์ด๋‹ค.

์™œ gRPC๋ฅผ ์‚ฌ์šฉํ•˜๋Š”๊ฐ€?

gRPC๊ฐ€ ๋ญ”์ง€ ์ดํ•ดํ–ˆ๋‹ค๋ฉด, ๋„๋Œ€์ฒด ์–ด๋–ค ์žฅ์ ๋“ค์ด ์žˆ๊ธธ๋ž˜ gRPC ์‚ฌ์šฉ์ด ์š”์ฆ˜์— ๐Ÿ”ฅ ํ•ซํ•ด์ง€๊ณ  ๐Ÿ”ฅ ์žˆ๋Š”์ง€ ์•Œ์•„๋ณด๋ ค๊ณ  ํ•œ๋‹ค.

feat. gRPC๋ฅผ ์‚ฌ์šฉํ•˜๊ณ  ์žˆ๋Š” ๐Ÿš€ ์Šคํƒ€ํŠธ์—…๋“ค

gRPC์˜ ์žฅ์ ์€ ๋‹ค์Œ๊ณผ ๊ฐ™๋‹ค.

  1. ๋น ๋ฅด๋‹ค

    protocol buffer๋ผ๋Š” ๋ฐฉ์‹์€ XML๊ณผ ๊ฐ™์ด ๊ตฌ์กฐํ™”๋œ ๋ฐ์ดํ„ฐ๋ฅผ ์ง๋ ฌํ™”(serialize)ํ•˜๋Š” ๋ฐฉ์‹์ธ๋ฐ ์••์ถ•์„ ํ•ด์„œ ํ›จ์”ฌ ๋น ๋ฅด๊ณ , ์‚ฌ์šฉ๋ฒ•๋„ ๊ฐ„๋‹จํ•˜๊ณ , ๋ฐ์ดํ„ฐ์˜ ํฌ๊ธฐ๋„ ์ž‘๋‹ค. JSON ์ง๋ ฌํ™”๋ณด๋‹ค ์ตœ๋Œ€ 8๋ฐฐ ๋” ๋น ๋ฅผ ์ˆ˜ ์žˆ๋‹ค๊ณ  ํ•œ๋‹ค ref.

  2. ์—„๊ฒฉํ•œ ์‚ฌ์–‘ ๋•๋ถ„์— ์ƒ๊ธฐ๋Š” ๊ฐ€์ด๋“œ์˜ ์กด์žฌ ์œ ๋ฌด (IDL)

    gRPC๋กœ ํ†ต์‹ ํ•˜๊ธฐ ์œ„ํ•ด์„œ๋Š” protocol buffer๋กœ ์–ด๋–ป๊ฒŒ ๋ฐ์ดํ„ฐ๋ฅผ ์ฃผ๊ณ  ๋ฐ›์„ ๊ฒƒ์ธ์ง€ ์ •์˜ํ•ด๋†”์•ผ ํ•œ๋‹ค. ๊ทธ๋ ‡๊ธฐ ๋•Œ๋ฌธ์—, ๋ฐ์ดํ„ฐ๋ฅผ ์ฃผ๊ณ  ๋ฐ›์„ ๋•Œ๋Š” ์–ด๋–ค ํ˜•์‹์œผ๋กœ ์ „๋‹ฌํ•ด์•ผํ•˜๋Š”์ง€์— ๋Œ€ํ•œ ๊ฐ€์ด๋“œ๊ฐ€ ์ฝ”๋“œ๋กœ ์ƒ์„ฑ๋˜๊ธฐ ๋•Œ๋ฌธ์— ๋”ฐ๋กœ API ๋ฌธ์„œ๊ฐ™์€ ๊ฒƒ์„ ๋งŒ๋“ค ํ•„์š”๊ฐ€ ์—†๊ฒŒ ๋˜๋Š” ๊ฒƒ์ด๋‹ค. ์—ฌ๋Ÿฌ ํšŒ์‚ฌ์—์„œ๋„ ์ด๋Ÿฐ ์—„๊ฒฉํ•œ ์‚ฌ์–‘ ๋•๋ถ„์— ๊ฐœ๋ฐœ ์ƒ์‚ฐ์„ฑ์ด ๋†’์•„์ง€๋Š” ์ด์œ ๋„ ์žˆ์–ด์„œ gRPC๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๊ฒฝ์šฐ๊ฐ€ ๋‹ค์ˆ˜ ์žˆ๋‹ค. ref. ๋ฑ…ํฌ์ƒ๋Ÿฌ๋“œ ๊ธฐ์ˆ  ๋ธ”๋กœ๊ทธ

  3. HTTP/2

    gRPC๋Š” HTTP/2๋ฅผ ์ง€์›ํ•œ๋‹ค. gRPC๋Š” HTTP/1.1๊ณผ ํ˜ธํ™˜๋˜์ง€๋งŒ HTTP/2๋งŒ์˜ ๊ณ ๊ธ‰ ๊ธฐ๋Šฅ๋“ค๋„ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค. ๋™์ผํ•œ ์—ฐ๊ฒฐ๋กœ ๋ณ‘๋ ฌ์ ์ธ ์š”์ฒญ์„ ์ฒ˜๋ฆฌํ•  ์ˆ˜ ์žˆ๊ณ , ์—ฐ๊ฒฐ์„ ์œ ์ง€ํ•ด์„œ connection์„ ๋งค๋ฒˆ ํ•˜๋Š”๋ฐ ์‚ฌ์šฉ๋˜๋Š” cost๋„ ์ค„์ผ ์ˆ˜ ์žˆ๋‹ค.

gRPC์˜ ์ด๋Ÿฌํ•œ ์žฅ์ ๋“ค์€ ์š”์ƒˆ ์œ ํ–‰ํ•˜๊ณ  ์žˆ๋Š” Microservice์— ์•ˆ์„ฑ๋งž์ถค์ด๋‹ค. Microservie๋Š” ๊ฐ„๋‹จํžˆ ๋งํ•˜๋ฉด ์„œ๋น„์Šค์˜ ๋ชจ๋“  ๊ธฐ๋Šฅ๋“ค์„ ํ•˜๋‚˜์˜ ํฐ ์„œ๋ฒ„์— ๋ชฐ์•„๋†“๋Š” ๊ฒƒ์ด ์•„๋‹Œ, ๊ฐ๊ฐ์˜ ๋…๋ฆฝ์ ์ธ ๊ธฐ๋Šฅ์œผ๋กœ ์—ฌ๋Ÿฌ ์„œ๋ฒ„๋กœ ๋งŒ๋“ค๊ณ  ์„œ๋ฒ„๋“ค๊ฐ„ ํ†ต์‹ ํ•˜๊ฒŒ ๋งŒ๋“œ๋Š” ์•„ํ‚คํ…์ฒ˜๋ฅผ ์˜๋ฏธํ•œ๋‹ค. ref. ๋งˆ์ดํฌ๋กœ์„œ๋น„์Šค ์•„ํ‚คํ…์ฒ˜์˜ ์žฅ๋‹จ์ 

microservice

Microservice์—๋Š” ๋…๋ฆฝ์ ์ธ ๊ธฐ๋Šฅ์„ ์ˆ˜ํ–‰ํ•˜๋Š” ์„œ๋ฒ„๋“ค์ด ์ˆ˜๋งŽ์ด ์žˆ๊ธฐ ๋•Œ๋ฌธ์— ์ด ์„œ๋ฒ„๋“ค๊ฐ„ ๋‚ด๋ถ€ ํ†ต์‹ ์ด ๋งค์šฐ ๋งŽ๊ฒŒ ๋œ๋‹ค. ์ฒ˜๋ฆฌํ•˜๋Š” ๋ฐ์ดํ„ฐ์–‘์ด ๋งŽ๊ธฐ ๋•Œ๋ฌธ์— ํšจ์œจ์ ์œผ๋กœ ๋ฐ์ดํ„ฐ๋ฅผ ํ†ต์‹ ํ•  ์ˆ˜ ์žˆ์–ด์•ผ ํ•œ๋‹ค. ๊ทธ๋ฆฌ๊ณ  gRPC๋Š” JSON, XML๋ณด๋‹ค ํ›จ์”ฌ ๋น ๋ฅด๊ณ  ํšจ์œจ์ ์œผ๋กœ ๋งŽ์€ ๋ฐ์ดํ„ฐ๋ฅผ ์••์ถ•ํ•ด์„œ ์‚ฌ์šฉํ•˜๊ธฐ ๋•Œ๋ฌธ์— ์ด๋Ÿฐ ์•„ํ‚คํ…์ฒ˜์— ์ ํ•ฉํ•œ ๊ฒƒ์ด๋‹ค. ์ถ”๊ฐ€๋กœ, ์„œ๋ฒ„๊ฐ„ ํ†ต์‹ ํ• ๋•Œ ์–ด๋–ค request๋ฅผ ๋ณด๋‚ด์•ผ ํ•˜๋Š”์ง€ ์—„๊ฒฉํ•œ ์‚ฌ์–‘์œผ๋กœ ์ฝ”๋“œ๋กœ ์ •์˜๋˜์–ด ์žˆ์œผ๋‹ˆ microservice๋“ค์„ ๊ด€๋ฆฌํ•˜๋Š”๋ฐ ๋” ์ˆ˜์›”ํ•œ ๊ฒƒ์ด๋‹ค.

๋ฌผ๋ก , gRPC์˜ ๋‹จ์ ๋„ ์กด์žฌํ•˜๊ธด ํ•œ๋‹ค.

  • ์ผ๋‹จ ๊ฐ€์žฅ ํฌ๊ฒŒ ๋ธŒ๋ผ์šฐ์ €์™€ ์„œ๋ฒ„๊ฐ„์€ gRPCํ†ต์‹ ์ด ์•„์ง ์ง€์›๋˜์ง€ ์•Š๋Š”๋‹ค. ๊ทธ๋ž˜์„œ ๋ธŒ๋ผ์šฐ์ €์—์„œ๋Š” ํ”ํžˆ JSON์œผ๋กœ ์„œ๋ฒ„๋กœ ์š”์ฒญํ•˜๋ฉด ์„œ๋ฒ„๋Š” grpc-gateway๋ฅผ ํ†ตํ•ด protobuf ํ˜•์‹์œผ๋กœ ๋ฐ์ดํ„ฐ๋ฅผ ๋ณ€ํ™˜ํ•œ ๋’ค์— ์‚ฌ์šฉํ•˜๊ฒŒ ๋œ๋‹ค.
  • ๊ทธ๋ฆฌ๊ณ , ์‚ฌ๋žŒ์ด ์ฝ์„ ์ˆ˜ ์—†๋Š” ๋ฐ์ดํ„ฐ ํ˜•์‹์ด๊ธฐ ๋•Œ๋ฌธ์— ๋„คํŠธ์›Œํฌ ๋‹จ์—์„œ ๋ฐ์ดํ„ฐ๋ฅผ ๋ณด๊ณ  ์‹ถ์œผ๋ฉด ์ถ”๊ฐ€์ ์ธ ์ž‘์—…๋“ค์ด ํ•„์š”ํ•˜๋‹ค.

์ด์™€ ๊ฐ™์ด gRPC์—๋Š” ์žฅ๋‹จ์ ๋“ค์ด ์žˆ์ง€๋งŒ, microservice ์•„ํ‚คํ…์ฒ˜์—์„œ๋Š” ์‚ฌ์šฉํ•˜๊ธฐ ์ ํ•ฉํ•œ ๊ธฐ์ˆ ์ด๊ธฐ ๋•Œ๋ฌธ์— ๋‹จ์ ์ด ์žˆ์Œ์—๋„ ๋ถˆ๊ตฌํ•˜๊ณ  ๋งŽ์€ ํšŒ์‚ฌ๋“ค์ด ํ•ด๋‹น ๊ธฐ์ˆ ์„ ์ ์šฉํ•˜๊ณ  ์žˆ๋Š” ์ถ”์„ธ์ธ ๊ฒƒ์ด๋‹ค.

(์‚ฌ์šฉ๋นˆ๋„๊ฐ€ ๋†’์•„์ง€๊ณ ๋Š” ์žˆ์œผ๋‚˜, JSON์„ ์‚ฌ์šฉํ•œ HTTP API ํ†ต์‹ ์— ๋น„ํ•ด์„œ๋Š” ์•„์ง๋„ ์ ์œ ์œจ์€ ๋งค์šฐ ๋‚ฎ๊ธด ํ•˜๋‹ค)

์•ž์„œ gRPC์™€ protobuf์— ๋Œ€ํ•ด ๊ฐ„๋žตํžˆ ์†Œ๊ฐœ๋ฅผ ๋“ค์—ˆ์œผ๋‹ˆ ์ด์ œ gRPC๋ฅผ ์‚ฌ์šฉํ•œ server๋ฅผ ๊ตฌ์ถ•ํ•ด๋ณด๋ ค๊ณ  ํ•œ๋‹ค.

๊ฐ„๋‹จํ•œ gRPC server ๊ตฌํ˜„ํ•˜๊ธฐ

Protobuf๋Š” ํ˜„์žฌ C++, Java, Python, Go, Ruby, Objective-C๋“ฑ์„ ์ง€์›ํ•˜์ง€๋งŒ(ref), ์ด ํฌ์ŠคํŠธ์—์„œ๋Š” ๋‚ด๊ฐ€ ์ฃผ๋กœ ์‚ฌ์šฉํ•˜๋Š” Go ์œ„์ฃผ๋กœ ๋‹ค๋ฃจ๋ ค๊ณ  ํ•œ๋‹ค.

Go ์–ธ์–ด๋ฅผ ์„ค์น˜ํ•˜๋Š” ๋ฐฉ๋ฒ•๋“ค์€ ์›Œ๋‚™ ๋‹ค๋ฅธ ๋ธ”๋กœ๊ทธ๋“ค์—์„œ ์ž์„ธํžˆ ๋‚˜์™€์žˆ๊ธฐ ๋•Œ๋ฌธ์— ๋”ฐ๋กœ ๋‹ค๋ฃจ์ง€ ์•Š์œผ๋ ค๊ณ  ํ•œ๋‹ค. ๋‹ค๋งŒ, ๋‚ด๊ฐ€ Go๋ฅผ ์ฒ˜์Œ ์„ค์น˜ํ•ด์„œ ์‚ฌ์šฉํ•˜๋ ค๊ณ  ํ•  ๋•Œ ์ฐธ๊ณ ํ–ˆ๋˜ ๋ธ”๋กœ๊ทธ๋“ค ๋ช‡๊ฐœ๋งŒ ๊ณต์œ ํ•˜๋ ค๊ณ  ํ•œ๋‹ค:

์œ„ ์˜ˆ์ œ๋“ค์„ ์ฐธ๊ณ ํ•˜๋ฉด Go ์–ธ์–ด๋ฅผ ์ปดํ“จํ„ฐ์— ์ž˜ ์„ค์น˜ํ•  ์ˆ˜ ์žˆ์„ ๊ฒƒ์ด๋‹ค. ๊ทธ๋ ‡๋‹ค๋ฉด ์ด์ œ ๊ฐ„๋‹จํ•œ gRPC server๋ฅผ ๊ตฌํ˜„ํ•ด๋ณด๋ ค๊ณ  ํ•œ๋‹ค.

(๋ธ”๋กœ๊ทธ์— ๋‚˜์˜ค๋Š” ์ฝ”๋“œ๋“ค์€ ์ „๋ถ€ Github Repository์—์„œ๋„ ๋ณผ ์ˆ˜ ์žˆ๋‹ค)

๋จผ์ € go mod init ๋ชจ๋“ˆ ์ด๋ฆ„์œผ๋กœ Go ๋ชจ๋“ˆ์„ ๋งŒ๋“ ๋‹ค.

// ์˜ˆ์‹œ
go mod init github.com/dojinkimm/go-grpc-example

go.mod์— ๋‹ค๋ฅธ ๋ชจ๋“ˆ๋“ค์„ importํ•  ์ˆ˜ ์žˆ๊ณ , importํ•˜๊ฒŒ ๋˜๋ฉด ํ•ด๋‹น ๋ชจ๋“ˆ๋“ค์„ ํ”„๋กœ์ ํŠธ ๋‚ด์—์„œ ์ž์œ ๋กญ๊ฒŒ ์‚ฌ์šฉ ํ•  ์ˆ˜ ์žˆ๊ฒŒ ๋œ๋‹ค. go.mod ํŒŒ์ผ์ด ์ƒ์„ฑ๋˜๊ณ  ๋‚˜์„œ๋Š” grpc ๋ชจ๋“ˆ์„ importํ•ด์•ผ ํ•œ๋‹ค. Go๋กœ ๊ตฌํ˜„๋œ gRPC์˜ ์ฝ”๋“œ๋Š” ์˜คํ”ˆ์†Œ์Šค๋กœ grpc-go repository์— ์žˆ๋‹ค. go get -u ๋ชจ๋“ˆ ์ด๋ฆ„ ์œผ๋กœ grpc-go ํŒจํ‚ค์ง€๋ฅผ import ํ•ด์˜จ๋‹ค.

go get -u google.golang.org/grpc

grpc-go ๋ชจ๋“ˆ์„ importํ•˜๋ฉด main.go์—์„œ ๋ถˆ๋Ÿฌ์™€์„œ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค. gRPC server๋ฅผ ๊ตฌํ˜„ํ•˜๋Š” ๊ฒƒ์€ ๋งค์šฐ ๊ฐ„๋‹จํ•œ๋‹ค.

  1. ๋จผ์ € net ํ‘œ์ค€ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ํŒจํ‚ค์ง€(net package)๋ฅผ ์‚ฌ์šฉํ•ด์„œ ์–ด๋–ค ๋„คํŠธ์›Œํฌ์— ์–ด๋–ค port number๋กœ ์„œ๋ฒ„๋ฅผ ์‹คํ–‰์‹œํ‚ฌ์ง€ ์ •์˜ํ•œ๋‹ค.
  2. importํ•œ google.golang.org/grpc ๋ชจ๋“ˆ์—์„œ NewServer() ํ•จ์ˆ˜๋ฅผ ํ˜ธ์ถœํ•ด์™€์„œ gRPC server๋ฅผ ๋งŒ๋“ ๋‹ค (ref ์ฝ”๋“œ).
  3. net ํŒจํ‚ค์ง€๋ฅผ ์‚ฌ์šฉํ•ด์„œ ๋งŒ๋“  listener connection์„ Serve()๋ผ๋Š” ํ•จ์ˆ˜์˜ ์ธ์ž๋กœ ๋„ฃ์–ด์ค€๋‹ค (ref ์ฝ”๋“œ).
// simple/main.go
package main

import (
	"log"
	"net"

	"google.golang.org/grpc"
)

const portNumber = "9000"

func main(){
	lis, err := net.Listen("tcp", ":" + portNumber)
	if err != nil {
		log.Fatalf("failed to listen: %v", err)
	}

	grpcServer := grpc.NewServer()

	log.Printf("start gRPC server on %s port", portNumber)
	if err := grpcServer.Serve(lis); err != nil {
		log.Fatalf("failed to serve: %s", err)
	}
}

์ด์ œ ์ด main.go ๋ฅผ go run simple/main.go๋กœ ์‹คํ–‰์‹œํ‚ค๋ฉด 9000 port number์— gRPC server๋ฅผ ์‹คํ–‰์‹œํ‚ฌ ์ˆ˜ ์žˆ๊ฒŒ ๋œ๋‹ค.

simple-grpc

๋ฌผ๋ก , ์•„๋ฌด request๋„ ๋ชป ๋ฐ›๋Š” ๋ฌด์šฉ์ง€๋ฌผ์˜ gRPC server์ด์ง€๋งŒ, ์ด๋ ‡๊ฒŒ ๋ช‡์ค„์˜ ์ฝ”๋“œ๋กœ ๊ฐ„๋‹จํ•œ gRPC server๋ฅผ ๊ตฌํ˜„ํ–ˆ๋‹ค.

Protobuf ์„œ๋น„์Šค ์ •์˜ํ•˜๊ธฐ

์œ„์—์„œ gRPC server๋ฅผ ๊ตฌํ˜„ํ•˜๋Š” ๋ฒ•์„ ๋ฐฐ์› ์œผ๋‹ˆ, ์ด์ œ๋Š” microservice์— ๋งž๊ฒŒ ํŠน์ • ์„œ๋น„์Šค๋ฅผ ๋‹ด๋‹นํ•˜๋Š” gRPC server๋ฅผ ๊ตฌํ˜„ํ•ด๋ณด๋ ค๊ณ  ํ•œ๋‹ค. gRPC sever๋Š” protobuf ๋ผ๋Š” ๋ฐฉ์‹์„ ์‚ฌ์šฉํ•ด์„œ ์ •ํ•ด์ง„ ์–‘์‹๋Œ€๋กœ ๋ฐ์ดํ„ฐ๋ฅผ ์ฃผ๊ณ  ๋ฐ›๋Š”๋‹ค. ๊ทธ๋ž˜์„œ, protobuf๋กœ ๋งŒ๋“ค gRPC server์˜ ๋ฉ”์„ธ์ง€๋“ค์„ ๋จผ์ € ์ •์˜ ํ•ด์•ผ ํ•œ๋‹ค.

์ด๋ฒˆ์— ๊ตฌํ˜„ํ•˜๊ฒŒ ๋  gRPC server๋Š” ์œ ์ €์˜ ์ •๋ณด๋ฅผ ๋‹ด๋‹นํ•˜๋Š” microservice ํ˜•ํƒœ์ด๋‹ค. Protobuf ์‚ฌ์šฉ๋ฒ•์— ๋Œ€ํ•ด ๋” ์ž์„ธํžˆ ์•Œ๊ณ  ์‹ถ์œผ๋ฉด ๋ฐ‘์— ๋‘ ์‚ฌ์ดํŠธ๋ฅผ ์ฐธ๊ณ ํ•˜๋ฉด ๋„์›€์ด ๋งŽ์ด ๋œ๋‹ค.

User ์„œ๋น„์Šค๋Š” ๋‘ ๊ฐœ์˜ rpc๋ฅผ ๊ฐ–๋„๋ก ๊ตฌํ˜„ํ•  ์˜ˆ์ •์ด๋‹ค.

  1. GetUser๋Š” user id๋ฅผ ์ „๋‹ฌํ•˜๋ฉด, user id์— ๋งž๋Š” ์œ ์ €์˜ ์ •๋ณด๋ฅผ ๋ฆฌํ„ดํ•˜๋Š” rpc์ด๋‹ค
  2. ListUsers๋Š” ์„œ๋น„์Šค์— ์กด์žฌํ•˜๋Š” ๋ชจ๋“  ์œ ์ €๋“ค์˜ ์œ ์ € ์ •๋ณด๋ฅผ ๋ฆฌํ„ดํ•˜๋Š” rpc์ด๋‹ค
// protos/v1/user/user.proto
syntax = "proto3";

package v1.user;

option go_package = "github.com/dojinkimm/go-grpc-example/protos/v1/user";

service User {
    rpc GetUser(GetUserRequest) returns (GetUserResponse);
    rpc ListUsers(ListUsersRequest) returns (ListUsersResponse);
}

message UserMessage {
    string user_id = 1;
    string name = 2;
    string phone_number = 3;
    int32 age = 4;
}

message GetUserRequest {
    string user_id = 1;
}

message GetUserResponse {
    UserMessage user_message = 1;
}

message ListUsersRequest{}

message ListUsersResponse {
    repeated UserMessage user_messages = 1;
}

proto๋ฅผ ์ •์˜ํ–ˆ๋‹ค๋ฉด, ๊ทธ ๋‹ค์Œ์€ ์‹ค์ œ๋กœ gRPC server์—์„œ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋„๋ก protoc ์ปดํŒŒ์ผ๋Ÿฌ๋ฅผ ์‚ฌ์šฉํ•ด์„œ ์ปดํŒŒ์ผ ํ•ด์•ผ ํ•œ๋‹ค. ref

protoc ์ปดํŒŒ์ผ๋Ÿฌ๋ฅผ ๋‹ค์šด ๋ฐ›๊ณ  ๋‚˜์„œ, ๋ฐ‘์˜ ๋ช…๋ น์–ด๋ฅผ ํ™œ์šฉํ•ด์„œ Golang protobuf plugin์„ ์„ค์น˜ํ•œ๋‹ค.

go install google.golang.org/protobuf/cmd/protoc-gen-go

์„ค์น˜๊ฐ€ ์™„๋ฃŒ๋˜์—ˆ๋‹ค๋ฉด ์ด์ œ ์„œ๋น„์Šค๋ฅผ ์ •์˜ํ•œ proto ํŒŒ์ผ์„ gRPC server์—์„œ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋„๋ก ์ปดํŒŒ์ผ ํ•ด์ค€๋‹ค. user/user.proto์— ํŒŒ์ผ์„ ์ƒ์„ฑํ–ˆ๊ธฐ ๋•Œ๋ฌธ์— ๋ฐ‘์˜ ๋ช…๋ น์–ด๋ฅผ ์‚ฌ์šฉํ•ด์„œ ์ปดํŒŒ์ผ ํ•ด์ค€๋‹ค.

protoc -I=. \
	    --go_out . --go_opt paths=source_relative \
	    --go-grpc_out . --go-grpc_opt paths=source_relative \
	    protos/v1/user/user.proto

์ปดํŒŒ์ผ์ด ๋˜๋ฉด user.protoํŒŒ์ผ์ด ์žˆ๋Š” ํด๋”์— user.pb.go์™€ user_grpc.pb.go ํŒŒ์ผ๋“ค์ด ํ•จ๊ป˜ ์ƒ์„ฑ๋œ๋‹ค.

proto

์ •์˜ํ•œ Protobuf๋กœ gRPC server ๊ตฌํ˜„ํ•˜๊ธฐ

User ์„œ๋น„์Šค๋ฅผ ์ •์˜ํ–ˆ์œผ๋‹ˆ ์ด์ œ, ์„œ๋น„์Šค๋ฅผ ๋‹ด๋‹นํ•˜๋Š” microserviceํ˜•ํƒœ์˜ gRPC server๋ฅผ ๊ตฌํ˜„ํ•ด๋ณด๋ ค๊ณ  ํ•œ๋‹ค. User๋“ค์˜ ์ •๋ณด๋ฅผ ๊ฐ€์ ธ์˜ค๋Š” gRPC server์ด๊ธฐ ๋•Œ๋ฌธ์— ์‹ค์ œ microservice์—์„œ๋ผ๋ฉด user ๋“ค์˜ ์ •๋ณด๋ฅผ DB์—์„œ ๊ฐ€์ ธ์˜ค๊ฒ ์ง€๋งŒ, ์˜ˆ์ œ์ด๊ธฐ ๋•Œ๋ฌธ์— staticํ•˜๊ฒŒ ์„ ์–ธ์„ ํ•ด๋†จ๋‹ค.

// data/user.go
package data

import (
	userpb "github.com/dojinkimm/go-grpc-example/protos/v1/user"
)

var UserData = []*userpb.UserMessage{
	{
		UserId: "1",
		Name: "Henry",
		PhoneNumber: "01012341234",
		Age: 22,
	},
	{
		UserId: "2",
		Name: "Michael",
		PhoneNumber: "01098128734",
		Age: 55,
	},
	{
		UserId: "3",
		Name: "Jessie",
		PhoneNumber: "01056785678",
		Age: 15,
	},
	{
		UserId: "4",
		Name: "Max",
		PhoneNumber: "01099999999",
		Age: 37,
	},
	{
		UserId: "5",
		Name: "Tony",
		PhoneNumber: "01012344321",
		Age: 25,
	},
}

user ์„œ๋น„์Šค์—๋Š” ์ด 5๋ช…์˜ ์œ ์ €๊ฐ€ ์กด์žฌํ•˜๊ณ  ์œ ์ €๋“ค์ด ๊ฐ–๊ณ  ์žˆ๋Š” ๊ฐ’๋“ค์€ user.proto์—์„œ ์ •์˜ํ•œ ๊ฐ’๋“ค๊ณผ ๊ฐ™๋‹ค.

// simple-user/main.go
import (
	"context"
	"log"
	"net"

	"google.golang.org/grpc"

	"github.com/dojinkimm/go-grpc-example/data"
	userpb "github.com/dojinkimm/go-grpc-example/v1/user"
)

const portNumber = "9000"

type userServer struct {
	userpb.UserServer
}

// GetUser returns user message by user_id
func (s *userServer) GetUser(ctx context.Context, req *userpb.GetUserRequest) (*userpb.GetUserResponse, error) {
	userID := req.UserId

	var userMessage *userpb.UserMessage
	for _, u := range data.Users {
		if u.UserId != userID {
			continue
		}
		userMessage = u
		break
	}

	return &userpb.GetUserResponse{
		UserMessage: userMessage,
	}, nil
}

// ListUsers returns all user messages
func (s *userServer) ListUsers(ctx context.Context, req *userpb.ListUsersRequest) (*userpb.ListUsersResponse, error) {
	userMessages := make([]*userpb.UserMessage, len(data.Users))
	for i, u := range data.Users {
		userMessages[i] = u
	}

	return &userpb.ListUsersResponse{
		UserMessages: userMessages,
	}, nil
}

func main() {
	lis, err := net.Listen("tcp", ":"+portNumber)
	if err != nil {
		log.Fatalf("failed to listen: %v", err)
	}

	grpcServer := grpc.NewServer()
	userpb.RegisterUserServer(grpcServer, &userServer{})

	log.Printf("start gRPC server on %s port", portNumber)
	if err := grpcServer.Serve(lis); err != nil {
		log.Fatalf("failed to serve: %s", err)
	}
}

์œ ์ €๋“ค์˜ ์ •๋ณด๋ฅผ ์ •์˜ํ–ˆ์œผ๋‹ˆ, ์ด์ œ user.proto๋ฅผ ์‚ฌ์šฉํ•ด user service๋ฅผ ๋‹ด๋‹นํ•˜๋Š” gRPC server๋ฅผ ๊ตฌํ˜„ํ•  ์ฐจ๋ก€์ด๋‹ค. ๊ธฐ์กด ํ‹€์€ ์œ„์—์„œ ๊ตฌํ˜„ํ•œ service ์—†๋Š” gRPC server์™€ ์œ ์‚ฌํ•˜๋‹ค. ์ถ”๊ฐ€๋œ ๋ถ€๋ถ„์€ 2๊ฐ€์ง€์ด๋‹ค.

  • User ์„œ๋น„์Šค๋ฅผ gRPC server์— ๋“ฑ๋กํ•˜๋Š” ๋ถ€๋ถ„
  • User ์„œ๋น„์Šค์˜ rpc๋“ค์„ ๊ตฌํ˜„ํ•œ ๋ถ€๋ถ„

gRPC server์—์„œ ์ •์˜ํ•œ user ์„œ๋น„์Šค๋ฅผ ์‚ฌ์šฉํ•˜๋„๋ก ๋งŒ๋“œ๋Š” ๊ฒƒ์€ ๋งค์šฐ ๊ฐ„๋‹จํ•˜๋‹ค. gRPC server์— ์ •์˜ํ•œ user ์„œ๋น„์Šค๋ฅผ ์‚ฌ์šฉํ•˜๋„๋ก ๋งŒ๋“œ๋Š” ํ•จ์ˆ˜๊ฐ€ ์ด๋ฏธ ์ปดํŒŒ์ผ๋œ user_grpc.pb.go ํŒŒ์ผ์— ์กด์žฌํ•œ๋‹ค. RegisterUserServer ํ•จ์ˆ˜๋ฅผ ๊ฐ€์ ธ์™€์„œ user ์„œ๋น„์Šค๋ฅผ ๋“ฑ๋กํ•˜๋ฉด ๋œ๋‹ค. ์ด ์ž‘์—…์ด ๋๋‚˜๋ฉด ์•„์ฃผ ๊ฐ„๋‹จํ•˜๊ฒŒ user ์„œ๋น„์Šค๋ฅผ ๋‹ด๋‹นํ•˜๋Š” gRPC server๊ฐ€ ์ƒ์„ฑ๋˜๋Š” ๊ฒƒ์ด๋‹ค.

type userServer struct {
	userpb.UserServer
}

...
grpcServer := grpc.NewServer()
userpb.RegisterUserServer(grpcServer, &userServer{})
...

๊ทธ ๋‹ค์Œ์—๋Š” user.proto์— ์ •์˜ํ•œ rpc๋“ค์„ ๊ตฌํ˜„ํ•˜๋Š” ์ฐจ๋ก€๊ฐ€ ๋‚จ์•˜๋‹ค. user_id๋กœ ์œ ์ €์˜ ์ •๋ณด๋ฅผ ๊ฐ–๊ณ ์˜ค๋Š” GetUser rpc์™€ ์œ ์ €๋“ค์˜ ์ •๋ณด ๋ชจ๋‘๋ฅผ ๊ฐ–๊ณ ์˜ค๋Š” ListUsers rpc๋Š” ๋ฐ‘๊ณผ ๊ฐ™์ด ๊ตฌํ˜„ํ•  ์ˆ˜ ์žˆ์„ ๊ฒƒ์ด๋‹ค. ๋กœ์ง์€ ๊ฐ„๋‹จํ•˜๋‹ˆ ๋”ฐ๋กœ ์„ค๋ช…ํ•˜์ง€ ์•Š๊ฒ ๋‹ค.

// GetUser returns user message by user_id
func (s *userServer) GetUser(ctx context.Context, req *userpb.GetUserRequest) (*userpb.GetUserResponse, error) {
	userID := req.UserId

	var userMessage *userpb.UserMessage
	for _, u := range data.UserData {
		if u.UserId != userID {
			continue
		}
		userMessage = u
		break
	}

	return &userpb.GetUserResponse{
		UserMessage: userMessage,
	}, nil
}

// ListUsers returns all user messages
func (s *userServer) ListUsers(ctx context.Context, req *userpb.ListUsersRequest) (*userpb.ListUsersResponse, error) {
	userMessages := make([]*pb.UserMessage, len(data.UserData))
	for i, u := range data.UserData {
		userMessages[i] = u
	}

	return &userpb.ListUsersResponse{
		UserMessages: userMessages,
	}, nil
}

user ์„œ๋น„์Šค๋ฅผ gRPC server์— ๋“ฑ๋กํ•˜๊ณ  + ํ•„์š”ํ•œ rpc๋“ค์„ ๊ตฌํ˜„ํ–ˆ๋‹ค๋ฉด ์ด์ œ ์‹ค์ œ๋กœ ์ด gRPC server๊ฐ€ ์˜๋„ํ•œ๋Œ€๋กœ ์ž‘๋™ํ•˜๋Š”์ง€ ํ™•์ธ์ด ํ•„์š”ํ•˜๋‹ค. ํ”ํžˆ ์‚ฌ์šฉ๋˜๋Š” JSON์œผ๋กœ ํ†ต์‹ ํ•˜๋Š” HTTP API๋ผ๋ฉด localhost:9000/users/:user_id ์ด๋Ÿฐ ์‹์œผ๋กœ curl์ด๋‚˜ Postman ๊ฐ™์€ ํˆด๋กœ ์ž˜ ๋˜๋Š”์ง€ ํ™•์ธํ•  ์ˆ˜ ์žˆ์—ˆ์„ ๊ฒƒ์ด๋‹ค. ํ•˜์ง€๋งŒ, gRPC server๋Š” ๋”ฐ๋กœ curl๋กœ ์ ‘๊ทผํ•  ์ˆ˜ ์žˆ๋Š” HTTP endpoint๊ฐ€ ์—†๊ธฐ ๋•Œ๋ฌธ์— ๊ธฐ์กด ๋ฐฉ์‹์œผ๋กœ๋Š” ํ™•์ธํ•  ์ˆ˜ ์—†๋‹ค.

(grpc-gateway๋ฅผ ํ™œ์šฉํ•˜๋ฉด ๊ฐ€๋Šฅํ•˜๊ณ  ์ด ๋ถ€๋ถ„์€ ๋‹ค๋ฅธ ํฌ์ŠคํŠธ์—์„œ ๋‹ค๋ฃฐ ์˜ˆ์ •)

gRPC server๋กœ curlํ•˜๋ ค๋ฉด ๊ทธ๊ฑธ ๊ฐ€๋Šฅํ•ด์ฃผ๋Š” ํˆด๋“ค์„ ์„ค์น˜ํ•ด์•ผ ํ•œ๋‹ค:

์ด ํฌ์ŠคํŠธ์—์„œ๋Š” bloomrpc๋ฅผ ํ™œ์šฉํ•ด์„œ user ์„œ๋น„์Šค๋ฅผ ๋‹ด๋‹นํ•˜๋Š” gRPC server๊ฐ€ ์ž˜ ์ž‘๋™ํ•˜๋Š”์ง€ ํ™•์ธํ•ด๋ณด๋ ค๊ณ  ํ•œ๋‹ค. ์„ค์น˜ ๋ฐฉ๋ฒ•์€ ์ƒ๋žตํ•˜๊ณ  ์‚ฌ์šฉ๋ฒ•์œผ๋กœ ๋ฐ”๋กœ ๋“ค์–ด๊ฐ€๋ณด๋ ค๊ณ  ํ•œ๋‹ค.

1. Proto ํŒŒ์ผ importํ•˜๊ธฐ

ํ”„๋กœ๊ทธ๋žจ์˜ ์™ผ์ชฝ์œ„์— + ๋ฅผ ๋ˆŒ๋Ÿฌ์„œ proto ํŒŒ์ผ์„ ์ฐพ์Šต๋‹ˆ๋‹ค.

bloomrpc1

user.proto๋ฅผ ์ฐพ์•„์„œ ์—ด๊ธฐ๋ฅผ ๋ˆ„๋ฆ…๋‹ˆ๋‹ค.

bloomrpc2

user.proto๋ฅผ importํ•˜๊ฒŒ ๋˜๋ฉด ๋ฐ‘๊ณผ ๊ฐ™์ด user.proto ์— ์ •์˜๋œ rpc๋“ค์— ํ†ต์‹ ํ•  ์ˆ˜ ์žˆ๋Š” ํ˜•ํƒœ๊ฐ€ ๋งŒ๋“ค์–ด์ง‘๋‹ˆ๋‹ค.

bloomrpc3

2. gRPC ํ†ต์‹ ํ•˜๊ธฐ

๋จผ์ € main.go๋ฅผ ์‹คํ–‰์‹œ์ผœ์„œ http://localhost:9000 ์— gRPC server๋ฅผ ์—ด์–ด์ค๋‹ˆ๋‹ค.

bloomrpc4

Server๋ฅผ ์‹คํ–‰์‹œํ‚ค๊ณ  ๋‚˜์„œ bloomrpc์—์„œ ์ƒ์„ฑํ•ด์ค€ rpc๋“ค์„ ํด๋ฆญํ•˜๋ฉด ์ž๋™์œผ๋กœ request๋ฅผ ๋ณด๋‚ผ ์ˆ˜ ์žˆ๋Š” ํ˜•์‹์œผ๋กœ ํฌ๋งท์„ ๋ณ€๊ฒฝ์‹œ์ผœ์ค๋‹ˆ๋‹ค. ๊ทธ๋Ÿฐ ๋‹ค์Œ์— request ํ˜•์‹์— ๋งž๊ฒŒ ๋ฐ์ดํ„ฐ๋ฅผ ๋ณด๋‚ด๋ฉด ์˜๋„ํ•œ ๋Œ€๋กœ ๋ฐ์ดํ„ฐ๊ฐ€ ์ž˜ response๋กœ ์˜ค๋Š” ๊ฒƒ์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

GetUser rpc ํ˜ธ์ถœ ๊ฒฐ๊ณผ bloomrpc5

ListUsers rpc ํ˜ธ์ถœ ๊ฒฐ๊ณผ bloomrpc6

๋งˆ๋ฌด๋ฆฌ

Golang gRPC server ๊ตฌ์ถ•ํ•˜๊ธฐ ์‹œ๋ฆฌ์ฆˆ์˜ 1ํŽธ์ด ๋๋‚ฌ๋‹ค. ์ด๋ฒˆ ํฌ์ŠคํŠธ์—์„œ๋Š” gRPC๊ฐ€ ๋ฌด์—‡์ธ์ง€์— ๋Œ€ํ•œ ๊ฐœ๋…์„ ์ž ๊น ์งš์–ด๋ดค๊ณ , gRPC๋กœ ํ†ต์‹ ํ•˜๋Š” Golang server๋ฅผ ๊ฐ„๋‹จํ•˜๊ฒŒ ๊ตฌ์ถ•ํ•ด๋ดค๋‹ค. ๋‹ค์Œ ์‹œ๋ฆฌ์ฆˆ 2ํŽธ์—์„œ๋Š” grpc-go์—์„œ ์ง€์›ํ•˜๋Š” ๋งŽ์€ ์œ ์šฉํ•œ ๊ธฐ๋Šฅ๋“ค์„ ํŒŒํ—ค์ณ๋ณด๋ ค๊ณ  ํ•œ๋‹ค(ex. health checking, load balanding ๋“ฑ๋“ฑ), ๊ทธ๋ฆฌ๊ณ  ์‹ค์ œ๋กœ microservice์—์„œ gRPC server๋“ค๊ฐ„ ์–ด๋–ป๊ฒŒ ํ†ต์‹ ์„ ํ•˜๋Š”์ง€ ์•Œ์•„๋ณด๋ ค๊ณ  ํ•œ๋‹ค.

๋ธ”๋กœ๊ทธ ๊ธ€์„ ์ž‘์„ฑํ•˜๋ฉด์„œ ๋‚˜๋„ ์ƒˆ๋กœ ๋ฐฐ์šฐ๋Š” ๊ฒƒ๋“ค์ด ์žˆ๋‹ค๋ณด๋‹ˆ ์ดํ•ด๋ฅผ ์ž˜ ํ•˜์ง€ ๋ชปํ•ด ์ถฉ๋ถ„ํžˆ ์„ค๋ช…ํ•˜์ง€ ๋ชปํ•œ ๋ถ€๋ถ„๋„ ์žˆ์„ ๊ฒƒ ๊ฐ™๋‹ค. ๋ณด์™„์ด ํ•„์š”ํ•œ ๋ถ€๋ถ„์„ ๋ณด์‹œ๋Š” ๋ถ„๋“ค์€ ์–ธ์ œ๋‚˜ ํ”ผ๋“œ๋ฐฑ ์ฃผ์‹œ๋ฉด ๊ฐ์‚ฌํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค.

์ด ์˜ˆ์ œ์— ๋‚˜์˜ค๋Š” ๋ชจ๋“  ์ฝ”๋“œ๋“ค์€ Github Repository์— ๊ณต๊ฐœ๋˜์–ด ์žˆ๋‹ค.

ยฉ 2022 Dojin Kim, Built with Gatsby