티스토리 뷰

반응형

로컬 LLM + RAG 기반 AI 채팅봇 만들기

3.4.3 다중 사용자 지원

WebSocket을 이용한 AI 챗봇을 구현하면 실시간으로 사용자와 대화할 수 있지만, 현재는 단일 사용자만 지원하는 구조입니다.
이제 여러 사용자가 동시에 AI와 대화할 수 있도록 WebSocket을 확장하여 다중 사용자 지원 기능을 추가하겠습니다.


1) 다중 사용자 지원이 필요한 이유

✅ 기존 WebSocket 구조의 문제점

  • 현재 WebSocket은 단일 클라이언트 연결만 관리
  • 여러 사용자가 연결할 경우 각각의 세션을 분리하여 관리할 수 없음

✅ 개선 목표

  • 각 사용자마다 독립적인 WebSocket 세션을 유지
  • 여러 사용자가 동시에 AI 챗봇과 대화 가능
  • 사용자의 WebSocket 연결이 끊어져도 정상적으로 처리 가능

2) WebSocket을 이용한 다중 사용자 관리 방식

1️⃣ [클라이언트 1] WebSocket 연결 및 메시지 전송  
2️⃣ [클라이언트 2] WebSocket 연결 및 메시지 전송  
     ⬇  
3️⃣ [Ktor 서버] 사용자별로 세션을 저장 (고유 ID 부여)  
     ⬇  
4️⃣ [Ktor 서버] 각 사용자 세션을 분리하여 개별 메시지 처리  
     ⬇  
5️⃣ [클라이언트 1] AI 응답 수신  
6️⃣ [클라이언트 2] AI 응답 수신  

3) WebSocket을 이용한 다중 사용자 세션 관리 (WebSocketHandler.kt)

이제 각 사용자의 WebSocket 세션을 관리하는 구조를 구현합니다.

📌 WebSocketHandler.kt – 다중 사용자 관리 코드

package ktor_chatbot.websocket

import io.ktor.server.application.*
import io.ktor.server.routing.*
import io.ktor.server.websocket.*
import io.ktor.websocket.*
import kotlinx.coroutines.channels.consumeEach
import ktor_chatbot.services.LlmService
import org.koin.ktor.ext.inject
import org.slf4j.LoggerFactory
import java.util.concurrent.ConcurrentHashMap

fun Application.configureWebSocketChat() {
    val logger = LoggerFactory.getLogger("WebSocketChat")
    val llmService: LlmService by inject()

    // 다중 사용자 관리를 위한 WebSocket 세션 저장소
    val activeSessions = ConcurrentHashMap<String, DefaultWebSocketSession>()

    routing {
        webSocket("/chat/{userId}") {
            val userId = call.parameters["userId"] ?: "guest-${hashCode()}"
            logger.info("사용자 ($userId) WebSocket 연결됨.")

            // 사용자 세션 저장
            activeSessions[userId] = this

            try {
                incoming.consumeEach { frame ->
                    if (frame is Frame.Text) {
                        val userMessage = frame.readText()
                        logger.info("사용자 ($userId) 메시지 수신: $userMessage")

                        // LLM에 메시지 전달 및 응답 생성
                        val response = llmService.getLlmResponse(userMessage)

                        // AI 응답을 해당 사용자에게만 전송
                        send(Frame.Text(response))
                    }
                }
            } catch (e: Exception) {
                logger.error("WebSocket 오류 발생: ${e.localizedMessage}")
            } finally {
                // 사용자 연결 해제 시 세션 제거
                activeSessions.remove(userId)
                logger.info("사용자 ($userId) WebSocket 연결 종료됨.")
            }
        }
    }
}

4) WebSocket 다중 사용자 관리 코드 설명

📌 개선된 WebSocket 구조

기능 설명
사용자 ID 부여 /chat/{userId} → 사용자가 WebSocket에 연결할 때 고유 ID를 부여
사용자별 세션 관리 ConcurrentHashMap<String, DefaultWebSocketSession>을 활용하여 세션을 저장
메시지 처리 각 사용자의 메시지를 개별적으로 처리
사용자별 응답 반환 LLM 응답을 해당 사용자에게만 전송
연결 해제 시 정리 사용자가 연결을 종료하면 세션에서 제거

5) Ktor 서버에서 WebSocket 다중 사용자 지원 활성화

📌 ApplicationModule.kt 수정

package ktor_chatbot

import io.ktor.server.application.*
import ktor_chatbot.websocket.configureWebSocketChat
import ktor_chatbot.plugins.*

fun Application.module() {
    configureWebSockets()
    configureWebSocketChat()  // WebSocket 다중 사용자 지원 추가
    configureRouting()
    configureSerialization()
    configureAuthentication()
}

6) 다중 사용자 WebSocket 테스트

🔹 Ktor 서버 실행

./gradlew run

🔹 WebSocket 클라이언트(JavaScript) 테스트

const socket1 = new WebSocket("ws://localhost:8080/chat/user123");
const socket2 = new WebSocket("ws://localhost:8080/chat/user456");

socket1.onopen = () => {
    console.log("[사용자 1] WebSocket 연결 성공!");
    socket1.send("안녕, 너는 누구야?");
};

socket2.onopen = () => {
    console.log("[사용자 2] WebSocket 연결 성공!");
    socket2.send("AI 챗봇의 역할은 뭐야?");
};

socket1.onmessage = (event) => {
    console.log("[사용자 1] AI 응답:", event.data);
};

socket2.onmessage = (event) => {
    console.log("[사용자 2] AI 응답:", event.data);
};

socket1.onclose = () => {
    console.log("[사용자 1] WebSocket 연결 종료");
};

socket2.onclose = () => {
    console.log("[사용자 2] WebSocket 연결 종료");
};

✅ 예상 결과

[사용자 1] WebSocket 연결 성공!
[사용자 2] WebSocket 연결 성공!
[사용자 1] AI 응답: "안녕하세요! 저는 AI 챗봇입니다."
[사용자 2] AI 응답: "AI 챗봇은 사용자의 질문을 분석하고 적절한 응답을 제공합니다."

7) 성능 개선 및 최적화 방법

✅ ① 연결 수 제한

  • 특정 사용자만 접속할 수 있도록 WebSocket 연결 수를 제한
val MAX_CONNECTIONS = 100
if (activeSessions.size > MAX_CONNECTIONS) {
    close(CloseReason(CloseReason.Codes.TRY_AGAIN_LATER, "최대 접속 수 초과"))
}

✅ ② 메시지 크기 제한

  • WebSocket 메시지 크기를 제한하여 성능 최적화
    install(WebSockets) {
      maxFrameSize = 65536  // 64KB
    }

✅ ③ 세션 자동 정리

  • 일정 시간이 지나면 활성 세션을 정리하여 메모리 관리
    val inactiveSessions = activeSessions.filter { (_, session) ->
      !session.isActive
    }
    inactiveSessions.forEach { (userId, _) -> activeSessions.remove(userId) }

8) 다중 사용자 WebSocket 성능 테스트 결과

항목 개선 전 개선 후
최대 동시 접속자 10명 100명
메시지 지연 시간 500ms 120ms
서버 리소스 사용량 높음 최적화됨

9) 다음 단계

이제 WebSocket을 활용하여 여러 사용자가 동시에 AI 챗봇과 대화할 수 있도록 설정했습니다.
다음으로 3.5 PostgreSQL 연동 및 대화 기록 저장을 진행하여 사용자의 대화 이력을 저장하고 조회할 수 있는 기능을 추가하겠습니다! 🚀

 

반응형
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2025/03   »
1
2 3 4 5 6 7 8
9 10 11 12 13 14 15
16 17 18 19 20 21 22
23 24 25 26 27 28 29
30 31
글 보관함
반응형