â ã¹ãã«äžèŠ§ã«æ»ã

grpc-service
by eco2-team
ð± ìŽìœììœ(Eco²) BE
â 0ðŽ 0ð
2026幎1æ25æ¥
SKILL.md
name: grpc-service description: gRPC ìë¹ì€ íµí© ê°ìŽë. Port/Adapter íšíŽ, Proto ì ì, Async Client 구í ì ì°žì¡°. "grpc", "proto", "subagent", "character", "location", "rpc" í€ìëë¡ ížëŠ¬ê±°.
gRPC Service Integration Guide
Eco² gRPC íµí© ìí€í ì²
âââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââ
â gRPC Integration (Clean Architecture) â
âââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââ€
â â
â Application Layer (Ports) â
â ââ CharacterClientPort (Protocol) â
â ââ LocationClientPort (Protocol) â
â ââ DTOs: CharacterDTO, LocationDTO â
â â â
â ⌠â
â Infrastructure Layer (Adapters) â
â ââ CharacterGrpcClient âââ character-api:50051 â
â ââ LocationGrpcClient âââ location-api:50051 â
â â â
â ⌠â
â Proto Files (Contract) â
â ââ character.proto â character_pb2.py, character_pb2_grpc.py â
â ââ location.proto â location_pb2.py, location_pb2_grpc.py â
â â
âââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââ
ì gRPCìžê°? (vs HTTP/Celery)
| Aspect | HTTP | Celery | gRPC |
|---|---|---|---|
| Latency | ~10ms | ~50ms+ | ~1-3ms |
| Type Safety | ë°íì | ìì | 컎íìŒíì |
| Asyncio | ì íì | 믞ì§ì | ë€ìŽí°ëž |
| Streaming | ë³µì¡ | 믞ì§ì | Ʞ볞 ì§ì |
| LangGraph íµí© | OK | await ë¶ê° | ìµì |
Reference Files
- Port ì ì: See port-definition.md
- Client 구í: See client-implementation.md
- Proto ê°ìŽë: See proto-guide.md
- í ì€íž íšíŽ: See testing-patterns.md
Quick Start
1. Port ì ì (Application Layer)
# application/ports/character_client.py
from abc import ABC, abstractmethod
from dataclasses import dataclass
@dataclass(frozen=True)
class CharacterDTO:
"""ìºëŠí° ì 볎 DTO"""
name: str
character_type: str
dialog: str
match_label: str
class CharacterClientPort(ABC):
"""ìºëŠí° ìë¹ì€ íŽëŒìŽìžíž Port"""
@abstractmethod
async def get_character_by_waste_category(
self,
waste_category: str,
) -> CharacterDTO | None:
"""íꞰ묌 칎í
ê³ ëŠ¬ë¡ ìºëŠí° ì¡°í"""
...
@abstractmethod
async def get_catalog(self) -> list[CharacterDTO]:
"""ì 첎 ìºëŠí° ëª©ë¡ ì¡°í"""
...
2. gRPC Client 구í (Infrastructure Layer)
# infrastructure/integrations/character/grpc_client.py
import grpc.aio
from .proto import character_pb2, character_pb2_grpc
class CharacterGrpcClient(CharacterClientPort):
"""gRPC êž°ë° ìºëŠí° íŽëŒìŽìžíž"""
def __init__(self, host: str = "character-api", port: int = 50051):
self._host = host
self._port = port
self._channel: grpc.aio.Channel | None = None
self._stub: character_pb2_grpc.CharacterServiceStub | None = None
async def _get_stub(self) -> character_pb2_grpc.CharacterServiceStub:
"""Lazy ì°ê²° (첫 ížì¶ ì ì±ë ìì±)"""
if self._stub is None:
self._channel = grpc.aio.insecure_channel(
f"{self._host}:{self._port}"
)
self._stub = character_pb2_grpc.CharacterServiceStub(self._channel)
return self._stub
async def get_character_by_waste_category(
self,
waste_category: str,
) -> CharacterDTO | None:
try:
stub = await self._get_stub()
request = character_pb2.GetByMatchRequest(
match_label=waste_category
)
response = await stub.GetCharacterByMatch(request)
if not response.found:
return None
return CharacterDTO(
name=response.name,
character_type=response.character_type,
dialog=response.dialog,
match_label=response.match_label,
)
except grpc.aio.AioRpcError as e:
logger.error("gRPC error", code=e.code().name, details=e.details())
return None
async def close(self) -> None:
"""ì±ë ì¢
ë£"""
if self._channel:
await self._channel.close()
self._channel = None
self._stub = None
3. LangGraph ë žë íµí©
# infrastructure/orchestration/langgraph/nodes/character_node.py
def create_character_node(
character_client: CharacterClientPort,
llm: LLMClientPort,
) -> Callable:
"""ìºëŠí° Subagent ë
žë ìì±"""
async def character_node(state: dict) -> dict:
query = state["query"]
# LLMìŒë¡ íꞰ묌 칎í
ê³ ëŠ¬ ì¶ì¶
category = await extract_waste_category(llm, query)
# gRPC ížì¶
character = await character_client.get_character_by_waste_category(
category
)
return {
"character_context": character.__dict__ if character else None,
}
return character_node
4. ìì¡Žì± ì£Œì
# setup/dependencies.py
from functools import lru_cache
@lru_cache
def get_character_client() -> CharacterClientPort:
settings = get_settings()
return CharacterGrpcClient(
host=settings.character_grpc_host,
port=settings.character_grpc_port,
)
async def cleanup():
"""ì í늬ìŒìŽì
ì¢
ë£ ì 늬ìì€ ì 늬"""
client = get_character_client()
await client.close()
ã¹ã³ã¢
ç·åã¹ã³ã¢
60/100
ãªããžããªã®åè³ªææšã«åºã¥ãè©äŸ¡
âSKILL.md
SKILL.mdãã¡ã€ã«ãå«ãŸããŠãã
+20
âLICENSE
ã©ã€ã»ã³ã¹ãèšå®ãããŠãã
+10
â説ææ
100æå以äžã®èª¬æããã
0/10
â人æ°
GitHub Stars 100以äž
0/15
âæè¿ã®æŽ»å
3ã¶æä»¥å ã«æŽæ°ããã
0/10
âãã©ãŒã¯
10å以äžãã©ãŒã¯ãããŠãã
0/5
âIssue管ç
ãªãŒãã³Issueã50æªæº
+5
âèšèª
ããã°ã©ãã³ã°èšèªãèšå®ãããŠãã
+5
âã¿ã°
1ã€ä»¥äžã®ã¿ã°ãèšå®ãããŠãã
0/5
ã¬ãã¥ãŒ
ð¬
ã¬ãã¥ãŒæ©èœã¯è¿æ¥å ¬éäºå®ã§ã