{"id":3509,"date":"2024-10-14T08:24:00","date_gmt":"2024-10-13T23:24:00","guid":{"rendered":"http:\/\/43.203.250.216\/?p=3509"},"modified":"2025-10-01T16:26:17","modified_gmt":"2025-10-01T07:26:17","slug":"grpc%eb%a5%bc-%ec%9d%b4%ec%9a%a9%ed%95%9c-observer-pattern","status":"publish","type":"post","link":"https:\/\/litcoder.com\/?p=3509","title":{"rendered":"gRPC\ub97c \uc774\uc6a9\ud55c Observer Pattern"},"content":{"rendered":"\n<p>Remote\uc5d0\uc11c \uc81c\uacf5\ud558\ub294 \uae30\ub2a5\uc744 \ub9c8\uce58 local system\uc758 function call \ucc98\ub7fc \uc81c\uacf5 \ud55c\ub2e4\ub294 gRPC\uc758 \uac1c\ub150 \uc790\uccb4\ub294 1990\ub144\ub300\uc5d0\ub3c4 \uc788\uc5c8\ub358 \uac83\uc774\uae30\uc5d0 \uc0c8\ub85c\uc6b8 \uac83\uc740 \uc5c6\uc9c0\ub9cc, \uadf8 \uc704\uc5d0\uc11c &#8220;Subject\uc758 \ubcc0\uacbd\uc774 \uc788\uc744 \ub54c subscriber\uc5d0\uac8c notify \ud574\uc8fc\ub294 <a href=\"https:\/\/en.wikipedia.org\/wiki\/Observer_pattern\" data-type=\"link\" data-id=\"https:\/\/en.wikipedia.org\/wiki\/Observer_pattern\">Observer Pattern<\/a>\uc744 \uc5b4\ub5bb\uac8c \uad6c\ud604 \ud560 \uc218 \uc788\uc744\uae4c?&#8221;\ud558\ub294 \uc758\ubb38\uc774 \ub4e4\uc5c8\ub2e4.<\/p>\n\n\n\n<p>\uc774 \ud3ec\uc2a4\ud305\uc5d0\uc11c\ub294 gRPC\ub97c \uc774\uc6a9\ud55c observer pattern\uc758 \uc608\uc81c\ub85c server\uc5d0\uc11c \uc784\uc758\uc758 \uc8fc\uc2dd\uacfc \uadf8 \ubcc0\ub3d9 \uac00\uaca9\uc744 client\ub85c notify \ud574\uc8fc\uace0 \uc774\uac83\uc744 \ud654\uba74\uc5d0 \ucd9c\ub825\ud558\ub294 \uc608\uc81c\ub97c \uc791\uc131\ud574 \ubcf8\ub2e4. \uc804\uccb4 \ucf54\ub4dc\ub294 <a href=\"https:\/\/github.com\/litcoder\/grpcobsr\">https:\/\/github.com\/litcoder\/grpcobsr<\/a>\uc5d0\uc11c \ud655\uc778 \ud560 \uc218 \uc788\ub2e4.<\/p>\n\n\n\n<p>\uae30\ubcf8\uc801\uc778 gRPC\ub294 client\uc5d0\uc11c \ud544\uc694\ud55c \uc815\ubcf4\ub97c server\uc5d0\uac8c \uc694\uccad\ud574\uc11c \uadf8 \uacb0\uacfc\ub97c \ub3cc\ub824\ubc1b\ub294\ub2e4. \ubc18\uba74, Observer Pattern\uc740 \uadf8 \ubc18\ub300\ub85c server \uce21\uc5d0\uc11c \uc815\ubcf4\uc758 \ubcc0\uacbd \uc0ac\ud56d\uc774 \uc788\uc744 \ub54c \uc774\uac83\uc744 client \uce21\uc5d0 \uc54c\ub824 \uc8fc\uc5b4\uc57c \ud55c\ub2e4.<\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-large\"><img decoding=\"async\" src=\"https:\/\/upload.wikimedia.org\/wikipedia\/commons\/thumb\/a\/a8\/Observer_w_update.svg\/500px-Observer_w_update.svg.png\" alt=\"\"\/><\/figure>\n<\/div>\n\n\n<p>\uc774\ub97c \uac00\ub2a5\ud558\uac8c \ud558\ub294 \uac83\uc740 <a href=\"https:\/\/grpc.github.io\/grpc\/cpp\/classgrpc_1_1_server_write_reactor.html\">ServerWriteRector<\/a>\uc640 <a href=\"https:\/\/grpc.github.io\/grpc\/cpp\/classgrpc_1_1_client_read_reactor.html\">ClientReadReactor<\/a> template\uc778\ub370, \uc774\ub4e4\uc740 client\uc758 \uc694\uccad\uc5d0 \ub300\ud574 server\uac00 \uc5ec\ub7ec \uac1c\uc758 \uc751\ub2f5\uc744 \ube44\ub3d9\uae30\uc801\uc73c\ub85c \uc804\uc1a1\ud558\ub294 \uc774\ub978\ubc14 gRPC\uc758 server-side streaming\uc744 \uad6c\ud604\ud558\ub294\ub370 \uac00\uc7a5 \ud575\uc2ec\uc774 \ub418\ub294 \uc694\uc18c\ub4e4\uc774\ub2e4. \uc6b0\ub9ac \uc608\uc81c\uc758 \uacbd\uc6b0 client\ub294 server\ub85c &#8220;\uc8fc\uc2dd \uac00\uaca9\uc744 \uc54c\ub824 \uc8fc\uc138\uc694&#8221;\ub77c\ub294 request\ub97c \uc804\uc1a1\ud558\uba74 ClientReadReactor\uc758 OnReadDone() \ud568\uc218\ub85c \ubcc0\uacbd\ub41c \uc8fc\uc2dd\uacfc \uac00\uaca9\uc774 \ud558\ub098\uc529 \ub4e4\uc5b4\uc624\ub294 \uc2dd\uc774\ub2e4.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Proto file<\/h2>\n\n\n\n<p>\uc8fc\uc2dd \uc815\ubcf4\ub97c \uc81c\uacf5\ud558\ub294 proto file\uc740 \ub2e4\uc74c\uacfc \uac19\uc774 \uc815\uc758 \ud55c\ub2e4. \ud074\ub77c\uc774\uc5b8\ud2b8\uac00 UpdateStockPrice()\ub97c \uc694\uccad\ud558\uba74 \uc11c\ubc84\uac00 StockPriceResponse\ub97c \uc5ec\ub7ec \uac1c stream\uc73c\ub85c \uc804\uc1a1\ud574 \uc8fc\ub294\ub370 \uadf8 \uc548\uc5d0\ub294 \uac01\uac01\uc758 \uc8fc\uc2dd\uc758 \uc885\ubaa9(symbol)\uacfc \uac00\uaca9(price) \uc815\ubcf4\uac00 \ub2f4\uaca8\uc838 \uc788\ub2e4.<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">syntax = \"proto3\";\nimport \"google\/protobuf\/empty.proto\";\n\nmessage StockPriceResponse {\n    string symbol = 1;\n    double price = 2;\n}\n\nservice StockService {\n    rpc UpdateStockPrice(google.protobuf.Empty) returns (stream StockPriceResponse);\n}<\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">StockPriceWriteReactor<\/h2>\n\n\n\n<p><a href=\"https:\/\/github.com\/litcoder\/grpcobsr\/blob\/main\/src\/writereactor.cpp\" data-type=\"link\" data-id=\"https:\/\/github.com\/litcoder\/grpcobsr\/blob\/main\/src\/writereactor.cpp\">StockPriceWriteReactor<\/a>\ub294 \uc11c\ubc84\uc5d0\uc11c \ub3d9\uc791\ud558\ub294 reactor\uc774\ub2e4. <\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"cpp\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">class StockPriceWriteReactor\n    : public ::grpc::ServerWriteReactor&lt;::StockPriceResponse>\n{\npublic:\n  StockPriceWriteReactor(int evCnt);\n\n  void OnWriteDone(bool ok) override;\n  void OnDone() override;\n  void OnCancel() override;\n\nprivate:\n  void NextWrite();\n\n  int _mReqEventCount;\n  int _mCurEventCount;\n  StockPriceResponse _mResp;\n  StockRepository _mStockRepo;\n};<\/pre>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>NextWrite():<\/strong> \ud074\ub77c\uc774\uc5b8\ud2b8\ub85c \uc804\uc1a1\ud560 \ub370\uc774\ud130\ub97c \uc0dd\uc131\ud558\uace0 stream\uc5d0 \uc4f4\ub2e4.<\/li>\n\n\n\n<li><strong>OnWriteDone():<\/strong> NextWrite()\uc5d0\uc11c \ud638\ucd9c\ud558\ub294 StartWrite()\uc5d0 \uc758\ud574 \ud558\ub098\uc758 \uc815\ubcf4\uac00 \uc4f0\uc5ec\uc84c\uc744 \ub54c \ud638\ucd9c\ub418\ub294 callback\uc774\ub2e4. \uc774\uc804\uc758 \uc804\uc1a1\uc774 \uc131\uacf5\uc801\uc73c\ub85c \ub418\uc5c8\ub294\uc9c0 \uac80\uc0ac\ud558\uace0 \ub2e4\uc74c \uc815\ubcf4\ub97c \uc804\uc1a1\ud55c\ub2e4.<\/li>\n\n\n\n<li><strong>OnDone():<\/strong> Stream \uc804\uc1a1 \uc644\ub8cc\ub97c \uc758\ubbf8\ud558\ub294 Finish()\ub97c \ud638\ucd9c\ud558\uba74 \ubd88\ub9ac\ub294 callback\uc774\ub2e4. \ud574\ub2f9 \uc778\uc2a4\ud134\uc2a4\uc758 \uc0ac\uc6a9\uc774 \uc885\ub8cc\ub418\uc5c8\ub2e4\ub294 \uc758\ubbf8\uc774\ubbc0\ub85c \uba54\ubaa8\ub9ac\ub97c \ud574\uc81c\ud55c\ub2e4.<\/li>\n<\/ul>\n\n\n\n<h2 class=\"wp-block-heading\">Server \uad6c\ud604<\/h2>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"cpp\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">class StockServiceImpl final : public StockService::CallbackService\n{\npublic:\n  ...\n  ServerWriteReactor&lt;::StockPriceResponse> *UpdateStockPrice(\n      CallbackServerContext *context,\n      const google::protobuf::Empty *empty) override\n  {\n    return new StockPriceWriteReactor(_mEventCount);\n  }\n  ...\n};\n<\/pre>\n\n\n\n<p>gRPC \ube44\ub3d9\uae30 \ud638\ucd9c\uc744 \uc704\ud55c \uc11c\ubc84\ub294 StockService::Server\uac00 \uc544\ub2cc <strong>StockService::CallbackService<\/strong>\ub97c \uc0c1\uc18d\ubc1b\uc544 \uad6c\ud604\ud55c\ub2e4. StockService::Server\uac00 write stream\uc5d0 \uc804\uc1a1\ud560 \ub0b4\uc6a9\uc744 \uc4f0\uace0 grpc::Status\ub97c \ubc18\ud658 \ud558\ub3c4\ub85d \ud558\ub294 \uac83\uacfc \ub2ec\ub9ac CallbackService\ub294 \uc55e\uc11c \uc815\uc758\ud55c ServerWriteReactor&lt;StockPriceResponse>* type\uc744 \ubc18\ud658 \ud558\ub3c4\ub85d \uc815\uc758 \ub418\uc5b4\uc788\ub2e4.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">StockPriceReadReactor<\/h2>\n\n\n\n<p><a href=\"https:\/\/github.com\/litcoder\/grpcobsr\/blob\/main\/src\/readreactor.cpp\">StockPriceReadReactor<\/a>\ub294 \ud074\ub77c\uc774\uc5b8\ud2b8\uc5d0\uc11c \ub3d9\uc791\ud558\ub294 reactor\uc774\ub2e4.<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"cpp\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">class StockPriceReadReactor\n    : public ::grpc::ClientReadReactor&lt;::StockPriceResponse>\n{\npublic:\n  StockPriceReadReactor(\n      std::shared_ptr&lt;StockService::Stub> stub, std::shared_ptr&lt;Publisher> pub);\n  void OnReadDone(bool ok) override;\n  void OnDone(const ::grpc::Status &amp;s) override;\n  ::grpc::Status Await();\n\nprivate:\n  std::shared_ptr&lt;Publisher> _mPub;\n  ::grpc::ClientContext _mContext;\n  ::StockPriceResponse _mResp;\n\n  std::mutex _mMtx;\n  std::condition_variable _mCondVar;\n  ::grpc::Status _mStatus;\n  bool _mAllDone = false;\n};<\/pre>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>OnReadDone(): <\/strong>StartRead() \ud638\ucd9c\uc744 \ud1b5\ud574 \uc11c\ubc84\ub85c \ubd80\ud130 \ud558\ub098\uc758 record\uc778 \uc8fc\uc2dd \uc815\ubcf4 \uc5c5\ub370\uc774\ud2b8\ub97c \ubc1b\uc744 \ub54c \ub9c8\ub2e4 \ud638\ucd9c\ub418\ub294 callback\uc774\ub2e4. \uc774\uac83\uc744 \uc774\uc6a9\ud574 Observer pattern\uc5d0\uc11c event publisher\uac00 \uc790\uc2e0\uc5d0\uac8c \ub4f1\ub85d\ub41c subscriber\ub4e4\uc5d0\uac8c event\ub97c notify\ud558\ub294 \ucf54\ub4dc\ub97c \uad6c\ud604\ud560 \uc218 \uc788\ub2e4.<\/li>\n\n\n\n<li><strong>OnDone(): <\/strong>\uc11c\ubc84\ub85c \ubd80\ud130 stream \uc885\ub8cc\ub97c \ubc1b\uc73c\uba74 \ud638\ucd9c\ub418\ub294 callback\uc774\ub2e4. Await()\uacfc \uacf5\uc720\ub418\ub294 condition_variable\uc744 \uc774\uc6a9\ud574 process\uac00 \uc885\ub8cc \ub420 \uc218 \uc788\ub3c4\ub85d \ud55c\ub2e4.<\/li>\n\n\n\n<li><strong>Await(): <\/strong>\ube44\ub3d9\uae30 \ud638\ucd9c\uc740 multi threading\uc744 \uc804\uccb4 \ud558\ubbc0\ub85c, \uc774 \ud568\uc218\ub294 \uc11c\ubc84\ub85c \ubd80\ud130 \ubc1b\ub294 stream\uc774 \uc885\ub8cc\ub420 \ub54c\uae4c\uc9c0 main thread\uac00 \uc885\ub8cc\ub418\uc9c0 \uc54a\uace0 \uc720\uc9c0 \ub418\ub3c4\ub85d \ud574\uc900\ub2e4.<\/li>\n\n\n\n<li><strong>mutext\uc640 condition_variable: <\/strong>\uc704\uc5d0\uc11c \uc124\uba85\ud55c OnDone()\uacfc Await()\uc774 thread control\uc744 \ud560 \uc218 \uc788\ub3c4\ub85d \ud574\uc8fc\ub294 \ub3d9\uae30\ud654 \ubcc0\uc218 \ub4e4\uc774\ub2e4.<\/li>\n<\/ul>\n\n\n\n<h2 class=\"wp-block-heading\">Client \uad6c\ud604<\/h2>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"cpp\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">class StockClient\n{\npublic:\n  ...\n  void updateStockPrice()\n  {\n    StockPriceReadReactor reader(_mStub, _mPub);\n    Status status = reader.Await();\n    if (status.ok())\n    {\n      spdlog::info(\"PriceListing succeed.\");\n    }\n    else\n    {\n      spdlog::error(\"Failed to get prices.\");\n      spdlog::error(\"{}({})\", status.error_message(), status.error_code());\n    }\n  }\n  ...\n};<\/pre>\n\n\n\n<p>\ud074\ub77c\uc774\uc5b8\ud2b8 \ucf54\ub4dc\ub294 StockPriceReadReactor\uc758 instance\ub97c \ub9cc\ub4e4\uace0 Await()\uc744 \ud638\ucd9c\ud574\uc11c \uc11c\ubc84\ub85c \ubd80\ud130 \uc804\uc1a1\uc774 \uc644\ub8cc\ub418\uae30\ub97c \uae30\ub2e4\ub9b0\ub2e4. \uadf8\ub7fc \uc11c\ubc84\ucabd\uc73c\ub85c &#8220;\uc8fc\uc2dd \uac00\uaca9 \uc8fc\uc138\uc694&#8221;\ub77c\ub294 request\ub294 \ub204\uac00 \ub0a0\ub9ac\ub0d0\uace0? StockPriceReadReactor\uc758 \uc0dd\uc131\uc790\uc5d0 \ub2e4\uc74c\uacfc \uac19\uc774 stub\uc5d0 UpdateStockPrice()\ub97c \ud638\ucd9c\ud558\ub294 \ubd80\ubd84\uc774 \uc815\uc758\ub418\uc5b4 \uc788\ub2e4.<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"cpp\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">StockPriceReadReactor::StockPriceReadReactor(\n    std::shared_ptr&lt;StockService::Stub> stub, std::shared_ptr&lt;Publisher> pub)\n    : _mPub(pub)\n{\n\n  ::google::protobuf::Empty empty;\n  stub->async()->UpdateStockPrice(&amp;_mContext, &amp;empty, this);\n  StartRead(&amp;_mResp);\n  StartCall();\n}<\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">\ub3d9\uc791 \ud655\uc778<\/h2>\n\n\n\n<p>Code repo: <a href=\"https:\/\/github.com\/litcoder\/grpcobsr\/tree\/main\">https:\/\/github.com\/litcoder\/grpcobsr<\/a><\/p>\n\n\n\n<figure class=\"wp-block-image size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"898\" height=\"603\" src=\"https:\/\/litcoder.com\/wp-content\/uploads\/2024\/10\/grpc_observer.png\" alt=\"\" class=\"wp-image-3511\" srcset=\"https:\/\/litcoder.com\/wp-content\/uploads\/2024\/10\/grpc_observer.png 898w, https:\/\/litcoder.com\/wp-content\/uploads\/2024\/10\/grpc_observer-300x201.png 300w, https:\/\/litcoder.com\/wp-content\/uploads\/2024\/10\/grpc_observer-768x516.png 768w, https:\/\/litcoder.com\/wp-content\/uploads\/2024\/10\/grpc_observer-624x419.png 624w\" sizes=\"auto, (max-width: 898px) 100vw, 898px\" \/><\/figure>\n\n\n\n<h2 class=\"wp-block-heading\">References<\/h2>\n\n\n\n<ul class=\"wp-block-list\">\n<li><a href=\"https:\/\/sultanov.dev\/blog\/grpc-long-lived-streaming-using-observer-pattern\/\">gRPC Long-lived Streaming using Observer Pattern<\/a>: Java\ub85c gRPC\uc758 Observer pattern\uc744 \uad6c\ud604\ud55c \ub0b4\uc6a9\uc744 \uc124\uba85\ud55c \uae00\uc774\ub2e4. \uc0ac\uc2e4 \ubcf8 \ud3ec\uc2a4\ud305\uc758 \uc2dc\uc791\ub3c4 \uc6d0\ub798\ub294 \uc774 \uad6c\ud604\uc744 C++\ub85c \ubcc0\uacbd\ud574 \ubcf4\uace0\uc790 \ud558\ub294 \uc758\ub3c4\uc600\uc73c\ub098 \uc548\ud0c0\uae5d\uac8c\ub3c4 C++\uc5d0\uc11c \uc0ac\uc6a9\ud560 \uc218 \uc5c6\ub294 \uc758\uc874\uc131 \ub54c\ubb38\uc5d0 \ub9ce\uc740 \ubd80\ubd84\uc744 \uc0c8\ub86d\uac8c \uc791\uc131\ud574\uc57c \ud588\ub2e4.<\/li>\n\n\n\n<li><a href=\"https:\/\/grpc.io\/docs\/languages\/cpp\/callback\/\">Asynchronous Callback API Tutorial<\/a>: gPRC\uc5d0 \ub300\ud55c \ube44\ub3d9\uae30 \ud638\ucd9c\uc5d0 \ub300\ud574 \uc608\uc81c\ub97c \ud3ec\ud568\ud574\uc11c \ub9e4\uc6b0 \uc790\uc138\ud788 \uc124\uba85\ud55c \uae00\uc774\ub2e4. Observer pattern\uc744 \uc9c1\uc811 \uc5b8\uae09\ud558\uace0 \uc788\uc9c0\ub294 \uc54a\uc73c\ub098 \ud65c\uc6a9\ub3c4\uac00 \ub192\uc740 Unary, Server-side streaming, Client-side streaming \uadf8\ub9ac\uace0 Bidirectional streaming\uc744 \uc124\uba85\ud55c\ub2e4.<\/li>\n\n\n\n<li><a href=\"https:\/\/grpc.github.io\/grpc\/cpp\/\">gRPC API reference<\/a>: API\ub4e4\uc5d0 \ub300\ud55c \uc124\uba85\uc744 \ucc3e\uc544 \ubcfc \uc218 \uc788\ub2e4. \uce5c\uc808\ud558\uac8c \uc124\uba85\ub41c \ubb38\uc11c\ub294 \uc544\ub2c8\uc9c0\ub9cc \uadf8\ub798\ub3c4 \uc5c6\ub294 \uac83 \ubcf4\ub2e4\ub294 \ubb50&#8230;<\/li>\n<\/ul>\n\n\n\n<p><\/p>\n","protected":false},"excerpt":{"rendered":"<p>Remote\uc5d0\uc11c \uc81c\uacf5\ud558\ub294 \uae30\ub2a5\uc744 \ub9c8\uce58 local system\uc758 function call \ucc98\ub7fc \uc81c\uacf5 \ud55c\ub2e4\ub294 gRPC\uc758 \uac1c\ub150 \uc790\uccb4\ub294 1990\ub144\ub300\uc5d0\ub3c4 \uc788\uc5c8\ub358 \uac83\uc774\uae30\uc5d0 \uc0c8\ub85c\uc6b8 \uac83\uc740 \uc5c6\uc9c0\ub9cc, \uadf8 \uc704\uc5d0\uc11c &#8220;Subject\uc758 \ubcc0\uacbd\uc774 \uc788\uc744 \ub54c subscriber\uc5d0\uac8c notify \ud574\uc8fc\ub294 Observer Pattern\uc744 \uc5b4\ub5bb\uac8c \uad6c\ud604 \ud560 \uc218 \uc788\uc744\uae4c?&#8221;\ud558\ub294 \uc758\ubb38\uc774 \ub4e4\uc5c8\ub2e4. \uc774 \ud3ec\uc2a4\ud305\uc5d0\uc11c\ub294 gRPC\ub97c \uc774\uc6a9\ud55c observer pattern\uc758 \uc608\uc81c\ub85c server\uc5d0\uc11c \uc784\uc758\uc758 \uc8fc\uc2dd\uacfc \uadf8 \ubcc0\ub3d9 \uac00\uaca9\uc744 client\ub85c notify \ud574\uc8fc\uace0 \uc774\uac83\uc744 [&hellip;]<\/p>\n","protected":false},"author":2,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_jetpack_memberships_contains_paid_content":false,"footnotes":""},"categories":[5],"tags":[337,247,330,339,338],"class_list":["post-3509","post","type-post","status-publish","format-standard","hentry","category-programming","tag-async","tag-c","tag-grpc","tag-observer-pattern","tag-server-side-streaming"],"jetpack_featured_media_url":"","jetpack_sharing_enabled":true,"_links":{"self":[{"href":"https:\/\/litcoder.com\/index.php?rest_route=\/wp\/v2\/posts\/3509","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/litcoder.com\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/litcoder.com\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/litcoder.com\/index.php?rest_route=\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"https:\/\/litcoder.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=3509"}],"version-history":[{"count":9,"href":"https:\/\/litcoder.com\/index.php?rest_route=\/wp\/v2\/posts\/3509\/revisions"}],"predecessor-version":[{"id":3702,"href":"https:\/\/litcoder.com\/index.php?rest_route=\/wp\/v2\/posts\/3509\/revisions\/3702"}],"wp:attachment":[{"href":"https:\/\/litcoder.com\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=3509"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/litcoder.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=3509"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/litcoder.com\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=3509"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}