โ† Back to list
eco2-team

grpc-service

by eco2-team

๐ŸŒฑ ์ด์ฝ”์—์ฝ”(Ecoยฒ) BE

โญ 0๐Ÿด 0๐Ÿ“… Jan 25, 2026

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)

AspectHTTPCelerygRPC
Latency~10ms~50ms+~1-3ms
Type Safety๋Ÿฐํƒ€์ž„์—†์Œ์ปดํŒŒ์ผํƒ€์ž„
Asyncio์ œํ•œ์ ๋ฏธ์ง€์›๋„ค์ดํ‹ฐ๋ธŒ
Streaming๋ณต์žก๋ฏธ์ง€์›๊ธฐ๋ณธ ์ง€์›
LangGraph ํ†ตํ•ฉOKawait ๋ถˆ๊ฐ€์ตœ์ 

Reference Files

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()

Score

Total Score

60/100

Based on repository quality metrics

โœ“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

Reviews

๐Ÿ’ฌ

Reviews coming soon