<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>GRPC on 엔지니어 생존기록</title><link>https://netscout.github.io/tags/gRPC/</link><description>Recent content in GRPC on 엔지니어 생존기록</description><generator>Hugo -- gohugo.io</generator><language>ko</language><lastBuildDate>Sun, 22 Mar 2026 10:34:52 +0900</lastBuildDate><atom:link href="https://netscout.github.io/tags/gRPC/index.xml" rel="self" type="application/rss+xml"/><item><title>go를 알아보자(2) - gRPC</title><link>https://netscout.github.io/posts/go%EB%A5%BC-%EC%95%8C%EC%95%84%EB%B3%B4%EC%9E%90-2-grpc/</link><pubDate>Sun, 22 Mar 2026 10:34:52 +0900</pubDate><guid>https://netscout.github.io/posts/go%EB%A5%BC-%EC%95%8C%EC%95%84%EB%B3%B4%EC%9E%90-2-grpc/</guid><description>paxos 기반의 분산 key-value 스토어 를 만들면서 가장 처음에 &amp;lsquo;뭐지?&amp;rsquo; 싶었던 건 gRPC였습니다. gRPC자체를 몰랐다기 보다는 뭔가 코드가 완전 암호같아 보였기 때문이죠. 그래서 gRPC만 우선 따로 간단하게 이해해보자 하고 간단한 앱을 만들어 보고 …</description><content>&lt;p>&lt;a href="https://github.com/netscout/dkv">paxos 기반의 분산 key-value 스토어&lt;/a> 를 만들면서 가장 처음에 &amp;lsquo;뭐지?&amp;rsquo; 싶었던 건 gRPC였습니다. gRPC자체를 몰랐다기 보다는 뭔가 코드가 완전 암호같아 보였기 때문이죠. 그래서 gRPC만 우선 따로 간단하게 이해해보자 하고 간단한 앱을 만들어 보고 한 줄씩 따라가면서 이해해보려고 노력했습니다.&lt;/p>
&lt;h2 id="grpc란">gRPC란?&lt;/h2>
&lt;p>RPC(Remote Procedure Call)은 원격, 그러니까 실행되는 프로그램 입장에서 자신이 아닌 외부의 함수를 호출하는 걸 뜻합니다. 그럼 &lt;code>g&lt;/code>는 뭐냐 싶었는데 찾아보니 이건 좀 &lt;a href="https://github.com/bojand/fsto-2018-grpc/blob/master/slides.md#grpc-">개발자들의 말장난&lt;/a> 같은 느낌이네요&amp;hellip; 버전마다 뜻이 다르답니다. 뭐 그렇다니까 그렇겠죠&amp;hellip;&lt;/p>
&lt;p align="center">&lt;img src="./image.png" width="400">&lt;/p>
&lt;p>어쨌거나 gRPC는 바이너리 형식인 Protocol Buffer(protobuf)를 사용해서 REST에 비해 매우 빠르고, 엄격하게 주고 받을 데이터 및 호출할 함수에 대한 정의를 해줄 수 있는 등의 장점이 있습니다. 단점이라면&amp;hellip; 코드가 기존 REST에 비해서 좀 이해하기 어렵다? 별도의 코드 생성과정이 필요하다? 뭐 이런게 있겠네요. 그래서 저도 좀 이해하기가 어려웠었죠.&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>항목&lt;/th>
&lt;th>REST&lt;/th>
&lt;th>gRPC&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>프로토콜&lt;/td>
&lt;td>HTTP/1.1 (주로)&lt;/td>
&lt;td>HTTP/2&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>데이터 형식&lt;/td>
&lt;td>JSON (텍스트)&lt;/td>
&lt;td>Protocol Buffers (바이너리)&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>계약 정의&lt;/td>
&lt;td>OpenAPI/Swagger (선택)&lt;/td>
&lt;td>.proto 파일 (필수)&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>스트리밍&lt;/td>
&lt;td>제한적&lt;/td>
&lt;td>양방향 스트리밍 지원&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>브라우저 지원&lt;/td>
&lt;td>네이티브&lt;/td>
&lt;td>gRPC-Web 필요&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;h2 id="그냥-한-번-해보자">그냥 한 번 해보자!&lt;/h2>
&lt;p>자 그럼 일단 그냥 한 번 해보죠. 아주 간단하게 서버와 클라이언트가 인사를 주고 받는 예제를 만들어 보겠습니다.&lt;/p>
&lt;h3 id="폴더-생성-및-go-모듈을-초기화">폴더 생성 및 go 모듈을 초기화&lt;/h3>
&lt;p>&lt;code>grpc-helloworld&lt;/code> 폴더를 만들고 모듈을 초기화 합니다.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">&amp;gt; mkdir grpc-helloworld
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&amp;gt; &lt;span class="nb">cd&lt;/span> grpc-helloworld
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&amp;gt; go mod init grpc-helloworld
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="grpc-계약contract-작성">gRPC 계약(contract) 작성&lt;/h3>
&lt;p>이제 gRPC를 통해 어떤 메시지를 주고 받을 거며, 어떤 원격의 함수를 호출할 수 있는지 계약(contract)을 정의해주겠습니다. &lt;code>greet.proto&lt;/code> 파일에 다음과 같이 작성합니다.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-proto" data-lang="proto">&lt;span class="line">&lt;span class="cl">&lt;span class="n">syntax&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s">&amp;#34;proto3&amp;#34;&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span>&lt;span class="kn">package&lt;/span> &lt;span class="nn">greet&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span>&lt;span class="c1">// 생성된 go 코드가 위치할 경로
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="k">option&lt;/span> &lt;span class="n">go_package&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s">&amp;#34;./pb&amp;#34;&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span>&lt;span class="c1">// gRPC를 통해 제공할 서비스(함수 목록)
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="kd">service&lt;/span> &lt;span class="n">Greeter&lt;/span> &lt;span class="p">{&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span> &lt;span class="k">rpc&lt;/span> &lt;span class="n">SayHello&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">HelloRequest&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="k">returns&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">HelloReply&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{}&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span>&lt;span class="c1">// 요청 메시지 형식
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="kd">message&lt;/span> &lt;span class="nc">HelloRequest&lt;/span> &lt;span class="p">{&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span> &lt;span class="kt">string&lt;/span> &lt;span class="n">name&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mi">1&lt;/span>&lt;span class="p">;&lt;/span> &lt;span class="c1">// 필드 번호
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span>&lt;span class="c1">// 응답 메시지 형식
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="kd">message&lt;/span> &lt;span class="nc">HelloReply&lt;/span> &lt;span class="p">{&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span> &lt;span class="kt">string&lt;/span> &lt;span class="kd">message&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mi">1&lt;/span>&lt;span class="p">;&lt;/span> &lt;span class="c1">// 필드 번호
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>추가 설명이 필요 없을 정도로 직관적이죠?&lt;/p>
&lt;h3 id="grpc-코드-생성">gRPC 코드 생성&lt;/h3>
&lt;p>이제 proto 파일에 정의된 서비스를 호출 및 응답하는데 사용할 코드를 생성해야 합니다.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Protocol Buffer 컴파일러 설치&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># macos의 경우&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&amp;gt; brew install protobuf
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># linux(ubuntu / debian)의 경우&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># &amp;gt; sudo apt install -y protobuf-compiler&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># go install 명령으로 설치된 패키지 경로 설정&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&amp;gt; &lt;span class="nb">echo&lt;/span> &lt;span class="s1">&amp;#39;export PATH=&amp;#34;$PATH:$(go env GOPATH)/bin&amp;#34;&amp;#39;&lt;/span> &amp;gt;&amp;gt; ~/.zshrc
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&amp;gt; &lt;span class="nb">source&lt;/span> ~/.zshrc
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># grpc 관련 패키지 설치&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&amp;gt; go install google.golang.org/protobuf/cmd/protoc-gen-go@latest
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&amp;gt; go install google.golang.org/grpc/cmd/protoc-gen-go-grpc@latest
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 코드 생성&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&amp;gt; protoc --go_out&lt;span class="o">=&lt;/span>. --go-grpc_out&lt;span class="o">=&lt;/span>. greet.proto
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>pb 폴더를 확인해보면 &lt;code>greet_grpc.pb.go&lt;/code>, &lt;code>greet.pb.go&lt;/code> 파일 두 개가 생성된 걸 확인할 수 있습니다.&lt;/p>
&lt;ul>
&lt;li>greet.pb.go: proto 파일에 정의된 서비스와 요청/응답 메시지에 대한 정의를 담고 있습니다.&lt;/li>
&lt;li>greet_grpc.pb.go: gRPC 클라이언트와 서버의 구현을 담고 있습니다.&lt;/li>
&lt;/ul>
&lt;h3 id="서버-코드-작성">서버 코드 작성&lt;/h3>
&lt;p>일단 코드를 작성하기 전에 다음 명령을 실행하여 부수적으로 필요한 패키지들을 모두 설치하고, go.mod 파일을 정리하도록 합니다.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">&amp;gt; go mod tidy
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>그리고 &lt;code>server/main.go&lt;/code> 파일을 다음과 같이 작성합니다.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-go" data-lang="go">&lt;span class="line">&lt;span class="cl">&lt;span class="kn">package&lt;/span> &lt;span class="nx">main&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kn">import&lt;/span> &lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s">&amp;#34;context&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s">&amp;#34;log&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s">&amp;#34;net&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s">&amp;#34;grpc-helloworld/pb&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s">&amp;#34;google.golang.org/grpc&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kd">type&lt;/span> &lt;span class="nx">server&lt;/span> &lt;span class="kd">struct&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="cm">/*
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> &amp;#34;protoc --go_out=. --go-grpc_out=. greet.proto&amp;#34; 명령으로 생성된 코드에는 greeterServer, greeterClient 구현이 있음.
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> UnimplementedGreeterServer 구조체 역시 protoc가 생성한 것으로 proto 파일에 정의된 Greeter 서비스의 모든 메서드에 대해 껍데기 구현을 가지고 있음.(호출시 codes.Unimplemented 오류 리턴)
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> 이는 추후 proto 파일에 새로운 메서드가 추가되고, 이에 대한 구현이 되지 않았을 때 컴파일 단계의 오류를 방지하기 위함.
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> -&amp;gt; 컴파일은 잘 되지만, 해당 함수를 호출하면 codes.Unimplemented 오류 리턴.
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> */&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">pb&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">UnimplementedGreeterServer&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// pb.UnimplementedGreeterServer의 SayHello 함수를 오버라이드(더 정확히는 덮어서 감추기, shadowing)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kd">func&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="nx">s&lt;/span> &lt;span class="o">*&lt;/span>&lt;span class="nx">server&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="nf">SayHello&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">ctx&lt;/span> &lt;span class="nx">context&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">Context&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">in&lt;/span> &lt;span class="o">*&lt;/span>&lt;span class="nx">pb&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">HelloRequest&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="o">*&lt;/span>&lt;span class="nx">pb&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">HelloReply&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="kt">error&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="o">&amp;amp;&lt;/span>&lt;span class="nx">pb&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">HelloReply&lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="nx">Message&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s">&amp;#34;Hello &amp;#34;&lt;/span> &lt;span class="o">+&lt;/span> &lt;span class="nx">in&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nf">GetName&lt;/span>&lt;span class="p">()},&lt;/span> &lt;span class="kc">nil&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kd">func&lt;/span> &lt;span class="nf">main&lt;/span>&lt;span class="p">()&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// 50051번 포트에 대해 네트워크 리스너를 생성&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">lis&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">err&lt;/span> &lt;span class="o">:=&lt;/span> &lt;span class="nx">net&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nf">Listen&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">&amp;#34;tcp&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s">&amp;#34;:50051&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="nx">err&lt;/span> &lt;span class="o">!=&lt;/span> &lt;span class="kc">nil&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">log&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nf">Fatalf&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">&amp;#34;failed to listen: %v&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">err&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// gRPC 서버 생성&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">s&lt;/span> &lt;span class="o">:=&lt;/span> &lt;span class="nx">grpc&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nf">NewServer&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// Greeter grpc 서비스에 서버를 등록&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">pb&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nf">RegisterGreeterServer&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">s&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="o">&amp;amp;&lt;/span>&lt;span class="nx">server&lt;/span>&lt;span class="p">{})&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// 서버 시작&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">log&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nf">Println&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">&amp;#34;Server listening at :50051&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="nx">err&lt;/span> &lt;span class="o">:=&lt;/span> &lt;span class="nx">s&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nf">Serve&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">lis&lt;/span>&lt;span class="p">);&lt;/span> &lt;span class="nx">err&lt;/span> &lt;span class="o">!=&lt;/span> &lt;span class="kc">nil&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">log&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nf">Fatalf&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">&amp;#34;failed to serve: %v&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">err&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>이 코드에서 유심히 봐야 할 내용은(처음에 제가 이해를 못했던 내용은) server 선언부 입니다. 이걸 이해하려면 일단 &lt;code>greet_grpc.pb.go&lt;/code> 파일의 다음 내용을 봐야 합니다.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-go" data-lang="go">&lt;span class="line">&lt;span class="cl">&lt;span class="o">...&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// UnimplementedGreeterServer must be embedded to have&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// forward compatible implementations.&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">//&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// NOTE: this should be embedded by value instead of pointer to avoid a nil&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// pointer dereference when methods are called.&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kd">type&lt;/span> &lt;span class="nx">UnimplementedGreeterServer&lt;/span> &lt;span class="kd">struct&lt;/span>&lt;span class="p">{}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kd">func&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="nx">UnimplementedGreeterServer&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="nf">SayHello&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">context&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">Context&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="o">*&lt;/span>&lt;span class="nx">HelloRequest&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="o">*&lt;/span>&lt;span class="nx">HelloReply&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="kt">error&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="kc">nil&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">status&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nf">Error&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">codes&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">Unimplemented&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s">&amp;#34;method SayHello not implemented&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="o">...&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>proto 파일에 정의했던 서비스의 함수가 UnimplementedGreeterServer의 메서드로 정의되어 있는데요, 내용을 보면 &lt;code>이 함수가 아직 구현되지 않았다&lt;/code> 는 오류 메시지입니다. 그러니까 gRPC 서버를 구현할 때, &lt;code>SayHello&lt;/code> 라는 메서드를 직접 구현하지 않으면 대신 이 기본 구현이 실행되면서 &lt;code>서비스를 제공할 함수를 구현해야 한다&lt;/code> 고 알려주는 거죠.&lt;/p>
&lt;p>근데 왜 이렇게 할까요? 이건 안전장치입니다.&lt;/p>
&lt;p>새로운 gRPC 함수가 추가된다고 생각해보죠. 그러면 우선 proto 파일을 수정합니다. 그리고 코드를 생성하죠. 그런데 만약 gRPC 서버 구현을 맡은 사람이 새 함수를 구현하는 걸 깜빡하면 어떻게 될까요? 갑자기 gRPC 서버는 컴파일 조차 되지 않을겁니다. 인터페이스의 함수를 모두 구현하지 못했기 때문이죠.&lt;/p>
&lt;p>그런데 기본 구현을 생성해서 그걸 서버 구현에 임베드 하게 된다면? 구현을 못 한 상태에서도 기존 구현은 아무 문제 없이 컴파일 되고 동작합니다!&lt;/p>
&lt;h3 id="클라이언트-코드-작성">클라이언트 코드 작성&lt;/h3>
&lt;p>다음으로 &lt;code>client/main.go&lt;/code>를 다음과 같이 작성합니다.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-go" data-lang="go">&lt;span class="line">&lt;span class="cl">&lt;span class="kn">package&lt;/span> &lt;span class="nx">main&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kn">import&lt;/span> &lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s">&amp;#34;context&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s">&amp;#34;grpc-helloworld/pb&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s">&amp;#34;log&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s">&amp;#34;time&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s">&amp;#34;google.golang.org/grpc&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s">&amp;#34;google.golang.org/grpc/credentials/insecure&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kd">func&lt;/span> &lt;span class="nf">main&lt;/span>&lt;span class="p">()&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// 1. 서버에 연결하기&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">conn&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">err&lt;/span> &lt;span class="o">:=&lt;/span> &lt;span class="nx">grpc&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nf">NewClient&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">&amp;#34;localhost:50051&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">grpc&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nf">WithTransportCredentials&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">insecure&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nf">NewCredentials&lt;/span>&lt;span class="p">()))&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="nx">err&lt;/span> &lt;span class="o">!=&lt;/span> &lt;span class="kc">nil&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">log&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nf">Fatalf&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">&amp;#34;failed to dial: %v&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">err&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">defer&lt;/span> &lt;span class="nx">conn&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nf">Close&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// 2. GreeterClient 생성&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">client&lt;/span> &lt;span class="o">:=&lt;/span> &lt;span class="nx">pb&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nf">NewGreeterClient&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">conn&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// 3. SayHello 메서드 호출(1초 동안 서버가 응답하지 않으면 컨텍스트 취소)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">ctx&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">cancel&lt;/span> &lt;span class="o">:=&lt;/span> &lt;span class="nx">context&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nf">WithTimeout&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">context&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nf">Background&lt;/span>&lt;span class="p">(),&lt;/span> &lt;span class="nx">time&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">Second&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">defer&lt;/span> &lt;span class="nf">cancel&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">r&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">err&lt;/span> &lt;span class="o">:=&lt;/span> &lt;span class="nx">client&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nf">SayHello&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">ctx&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="o">&amp;amp;&lt;/span>&lt;span class="nx">pb&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">HelloRequest&lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="nx">Name&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s">&amp;#34;World&amp;#34;&lt;/span>&lt;span class="p">})&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="nx">err&lt;/span> &lt;span class="o">!=&lt;/span> &lt;span class="kc">nil&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">log&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nf">Fatalf&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">&amp;#34;failed to say hello: %v&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">err&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">log&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nf">Printf&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">&amp;#34;Server response: %s&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">r&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nf">GetMessage&lt;/span>&lt;span class="p">())&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>gRPC 서버에 연결해서 요청을 보내고 응답을 수신합니다. 만약 요청이 처리되는데 1초 이상 소요된다면, 요청을 취소하게 설정되었습니다.&lt;/p>
&lt;h3 id="서버와-클라이언트-실행">서버와 클라이언트 실행&lt;/h3>
&lt;p>2개의 터미널을 열고 먼저 서버를 실행합니다.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">❯ go run server/main.go
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">2026/03/22 10:22:14 Server listening at :50051
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>그리고 클라이언트를 실행해서 요청을 전송하고 응답을 받습니다.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">❯ go run client/main.go
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">2026/03/22 10:22:51 Server response: Hello World
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="정리">정리&lt;/h2>
&lt;p>매우 간단한 예제지만 gRPC 계약 정의와 코드 생성 및 서버와 클라이언트 사용 등의 내용은 충분히 확인할 수 있었습니다. 이걸 몰랐을 때는 완전히 블랙박스 처럼 보였던 코드와 과정을 좀 더 명확하게 이해할 수 있었죠! 이 글의 모든 학습과정은 gemini가 생성한 튜토리얼을 기반으로 저의 삽질을 통해 진행하였습니다.&lt;/p></content></item></channel></rss>