์ผ๋‹จ ํ•˜๊ณ  ๋ณด๋Š” ์‚ฌ๋žŒ

๋‚˜์ค‘๋ณด๋‹จ ์ง€๊ธˆ์— ์ง‘์ค‘ํ•˜๋˜, ์ง€๊ธˆ๋ณด๋‹จ ๋‚˜์ค‘์— ์™„๋ฒฝํ•ด์ง€์ž๐Ÿ’ช๐Ÿป

๐Ÿคš๐Ÿป ๋” ๋‚˜์€ ๊ฐœ๋ฐœ์ž ํ•˜๊ธฐ/๐ŸŽจ ์ฝ”๋“œ ๋ฆฌ๋””์ž์ธ ์ผ์ง€

[Websocket] ์›น์†Œ์ผ“ ๋กœ๊ทธ ai search์— ์ ์žฌ: ์ŠคํŠธ๋ฆฌ๋ฐ ๋‹จ์ผ ๊ด€๋ฆฌ

JanginTech 2025. 9. 21. 15:40

๐Ÿงฉ ๋ฐฐ๊ฒฝ ์„ค๋ช…

1. ์ผ๋‹จ ๋‚ด๊ฐ€ ์ง€๊ธˆ ๋ชธ ๋‹ด๊ณ  ์žˆ๋Š” ํ”Œ์ ์ด REST ๋กœ๊ทธ ์ ์žฌ๋Š” ๊ตฌํ˜„๋˜์–ด ์žˆ๋Š”๋ฐ ws๋Š” ์•ˆ ๋˜์–ด ์žˆ์Œ

2. ๊ทธ๋ž˜์„œ ํ•ด์•ผ ๋จ

3. ์‹œ๋„

4. ๊ทธ๋Ÿฐ๋ฐ ์ƒˆ๋กœ ๋ถ™์ธ ์›น์†Œ์ผ“ ์„œ๋น„์Šค๋Š” ๋กœ๊ทธ ์ˆ˜์ง‘ ๊ตฌ์กฐ๊ฐ€ ์ „ํ˜€ ๋‹ค๋ฆ„์„ ๊นจ๋‹ฌ์Œ(๋‹น์—ฐํ•จ)

 

 

 

๐Ÿ’ญ ๊ณ ๋ฏผ์˜ ๋‚ด์šฉ

send_json() ์ด ํ˜ธ์ถœ๋  ๋•Œ๋งˆ๋‹ค ๋กœ๊ทธ๊ฐ€ ์ˆ˜๋ฐฑ ๊ฑด์”ฉ ์Œ“์ž„.

resToken์ด 738์ธ ์‘๋‹ต์ด ์ŠคํŠธ๋ฆฌ๋ฐ ๋˜๋ฉด ,,, ์ ์–ด๋„ 738+@ ๊ฑด์ด ์Œ“์ด๋Š” ๊ฑฐ์ž„ ๊ทธ ํ•˜๋‚˜์˜ ๋ฆฌ์‹œ๋ธŒ๋งˆ๋‹ค;;

REST์ฒ˜๋Ÿผ ์ตœ์ข… ํ•˜๋‚˜๋งŒ ๋‚จ๊ฒจ์•ผ ๋จ

 

 

๐Ÿ› ๏ธ ํ•ด๊ฒฐ์„ ์œ„ํ•œ ์‹œ๋„๋“ค

1. ๊ตฌ๊ธ€๋ง

๊ฒ€์ƒ‰ ํ‚ค์›Œ๋“œ: FastAPI Websocket, Logging, Websocket ๋กœ๊ทธ ์ ์žฌ

๊ฐ„์ง€๋Ÿฌ์šด ๋ถ€๋ถ„์„ ๋”ฑ ๊ธ์–ด์ค„ ๊ธฐ์ˆ ๋ธ”๋กœ๊ทธ๋ฅผ ์ฐพ์„ ์ˆ˜ ์—†์—ˆ์Œ 

 

2. ์ง์ ‘ ์‹œ๋„

  • Tabp ํด๋ž˜์Šค ๋งŒ๋“ค์–ด์„œ send_json() ์„ ๊ฐ€๋กœ์ฑ”
  • ๋งˆ์ง€๋ง‰ ํŽ˜์ด๋กœ๋“œ๋งŒ ์ €์žฅํ•ด์„œ ๋‹จ์ผ ์ ์žฌ ๊ตฌํ˜„ํ•จ
  • _logOnly ํ”Œ๋ž˜๊ทธ๋กœ ํด๋ผ ์ „์†ก์„ ์ฐจ๋‹จ
# โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
# 0) Tap: ์ตœ์ข… ํŽ˜์ด๋กœ๋“œ๋งŒ ๋ณด์กด (statusCode/statusMsg/result)
# โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
class WebSocketTap:
    def __init__(self, websocket):
        self._ws = websocket
        self.final_payload = None  # {"statusCode":..,"statusMsg":..,"result":..}

    async def send_json(self, data, *args, **kwargs):
        # ws_rag ๋‚ด๋ถ€: if websocket: await websocket.send_json({...})
        if isinstance(data, dict):
            # ๋งค ํ˜ธ์ถœ๋งˆ๋‹ค ๋ฎ์–ด์จ์„œ "๊ฐ€์žฅ ๋งˆ์ง€๋ง‰" ํŽ˜์ด๋กœ๋“œ๋งŒ ์œ ์ง€
            self.final_payload = {
                "statusCode": data.get("statusCode"),
                "statusMsg":  data.get("statusMsg"),
                "result":     data.get("result"),
            }
        return await self._ws.send_json(data, *args, **kwargs)

    # ํ•„์š”ํ•˜๋ฉด text๋„ ์˜ค๋ฒ„๋ผ์ด๋“œ ๊ฐ€๋Šฅํ•˜์ง€๋งŒ, ์—ฌ๊ธฐ์„  ์ตœ์ข… json๋งŒ ๋Œ€์ƒ
    def __getattr__(self, name):
        return getattr(self._ws, name)

# โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
# 1) ๋กœ๊ทธ ํŽธ์ง‘ → ์ตœ์ข… 1๊ฑด๋งŒ insert ํ˜ธ์ถœ → ai search์— ์ ์žฌ
# โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
async def websocketCallLog(logData: dict):
    doc = {
        "event":        logData.get("event"),
        "userId":       logData.get("userId"),
        "requestKey":   logData.get("requestKey"),
        "infId":        logData.get("infId"),
        "inData":       logData.get("inData"),
        "statusCode":   logData.get("statusCode"),
        "statusMsg":    logData.get("statusMsg"),
        "tapResult":    logData.get("tapResult"),     # if websocket: send_json(...)์˜ result
        "methodResult": logData.get("methodResult"),  # getStrm() ๋ฆฌํ„ด
    }
    await insertWebsocketCallLog(doc)  # ai search์— ์ ์žฌ


# โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
# 2) ์„œ๋น„์Šค ํƒœ์Šคํฌ: Tap์œผ๋กœ ๊ฐ์‹ธ ํ˜ธ์ถœ → ๋งˆ์ง€๋ง‰ ํŽ˜์ด๋กœ๋“œ๋งŒ ๋กœ๊ทธ 1๊ฑด
# โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
async def websocketSendTextAsync(websocket, data):
    ws_tap = WebSocketTap(websocket)
    method_result = None
    try:
        # ํŠน์ • ์ธํ„ฐํŽ˜์ด์Šค ์ž‘์—… ํ›„ result
        method_result = await ws_rag.getStrm(event, data, ws_tap)
        
    finally:
        # ์ตœ์ข… ํŽ˜์ด๋กœ๋“œ๊ฐ€ ์—†์„ ์ˆ˜๋„ ์žˆ์œผ๋‹ˆ ๋ฐฉ์–ด ๋กœ์ง๋„ ํ•„์š”ํ•  ๋“ฏํ•จ
        final = ws_tap.final_payload or {}
        
        # ai search์— ๋„ฃ์„ ๋ฐ์ดํ„ฐ ํŽธ์ง‘ํ•ด์„œ ๋˜์ ธ์ฃผ๊ธฐ!
        logData = {
            "event":        event,
            "userId":       f"{websocket.client.host}:{websocket.client.port}",
            "requestKey":   req_key,
            "infId":        inf_id,
            "inData":       data,
            "statusCode":   final.get("statusCode"),
            "statusMsg":    final.get("statusMsg"),
            "tapResult":    final.get("result"),
            "methodResult": method_result,
        }
        # ๋ฐ˜๋“œ์‹œ ํ•œ ๋ฒˆ๋งŒ ์ ์žฌ
        await websocketCallLog(logData)


# โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
# 3) ์—”๋“œํฌ์ธํŠธ: ๋ฉ€ํ‹ฐ๋ฉด ํƒœ์Šคํฌ, ๋‹จ๊ฑด์ด๋ฉด await (๋งˆ์ง€๋ง‰์— close)
# โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
@app.websocket("/ws/rag")
async def websocket_endpoint(websocket: WebSocket):
    await websocket.accept()
    try:
        while True:
            raw = await websocket.receive_text()
            if raw == "__ping__":
                await websocket.send_text("pong")
                continue

            try:
                data = json.loads(raw)
                assert isinstance(data, dict)
            except Exception as e:
                await websocket.send_json({"statusCode": 400, "statusMsg": f"INVALID_JSON: {e}"})
                continue

            multi = data.get("multi")
            if multi:
                # ๋ฉ€ํ‹ฐ์—ฌ๋„ ๊ฐ ์š”์ฒญ๋ณ„๋กœ ์ตœ์ข… 1๊ฑด๋งŒ ์ ์žฌ๋จ
                asyncio.create_task(websocketSendTextAsync(websocket, data))
            else:
                await websocketSendTextAsync(websocket, data)

    finally:
        try:
            await websocket.close()
        except Exception:
            pass

๋Œ€์ถฉ ์ด๋Ÿฐ ๋กœ์ง์ด์˜€์Œ

 

๐Ÿ“ˆ ๋ณ€ํ™”

  • ๋‚ด๊ฐ€ ์›ํ•˜๋Š” ๋Œ€๋กœ ๋จ
    • ๋กœ๊ทธ ์ ์žฌ๋Ÿ‰๋„ ๋งŽ์ด ์ค„์ด๊ณ (์•ฝ 90% ์ด์ƒ ๊ฐ์†Œ)
    • ํ™”๋ฉด์—๋Š” ์ •์ƒ ์‘๋‹ต๋งŒ ๋ณด์ด๊ฒŒ!
    • ๋””๋ฒ„๊ทธ/์—๋Ÿฌ๋Š” ์ธ๋ฑ์Šค์—์„œ๋งŒ ๋ณผ ์ˆ˜ ์žˆ์Œ
  • ์ด ํ”Œ์ ์ด ๊ฐ–๊ณ  ์žˆ๋Š” REST ๋กœ๊ทธ ์ ์žฌ ๊ตฌ์กฐ์™€ ์–ด๋А ์ •๋„ ์ผ๊ด€์„ฑ์„ ๊ฐ€์ง€๊ฒŒ๋” ํ•จ(๋ฉ”์„œ๋“œ๋ช…, ํ”Œ๋กœ์šฐ ๋“ฑ)

 

๐Ÿ“Œ ํšŒ๊ณ  & ๋‹ค์Œ ๋ชฉํ‘œ

์›น์†Œ์ผ“์ด REST์™€ ๋ณธ์งˆ์ ์œผ๋กœ ๋‹ค๋ฅธ ๊ตฌ์กฐ๋ผ "REST์ฒ˜๋Ÿผ ํ•˜๊ฒ ๋‹ค"๋Š” ๋ฐœ์ƒ์ด ๋ฌธ์ œ์˜€๋˜ ๊ฒƒ ๊ฐ™์Œ

์ด๋ฒˆ ํฌ์ŠคํŒ…์—์„œ REST ๋กœ์ง ๊ตฌ์กฐ๋ฅผ ์„ค๋ช…ํ•˜์ง„ ์•Š์•˜์ง€๋งŒ REST ๋กœ์ง์„ ์ฐธ๊ณ ํ•˜์—ฌ ws์— ๋งž๊ฒŒ ๋ฆฌ๋””์ž์ธ ํ•œ ๊ฒƒ์ด ์žฌ๋ฐŒ์—ˆ์Œ

์„œ๋น„์Šค ์„ฑ๊ฒฉ์— ๋งž์ถฐ ์ปค์Šคํ…€ํ•ด์•ผ ํ•œ๋‹ค๋Š” ๊ฒƒ์ด ์ด๋ฒˆ ๊ณผ์—…์„ ํ†ตํ•ด ์–ป์€ ๊ตํ›ˆ์ž„!