티스토리 뷰

반응형

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

3.4 WebSocket을 이용한 실시간 채팅 기능 구현

3.4.1 WebSocket 기본 구조

WebSocket은 클라이언트와 서버 간의 양방향 통신을 지원하는 프로토콜로, 실시간 채팅 시스템을 구현하는 데 최적화되어 있습니다.
본 챕터에서는 Ktor에서 WebSocket을 설정하는 기본 구조를 구축하고, 실시간 메시지를 주고받는 방식을 설명합니다.


1) WebSocket이 필요한 이유

✅ 기존 HTTP 기반 요청/응답 방식의 한계

  • HTTP는 요청(Request)-응답(Response) 구조로, 클라이언트가 요청을 보내야만 응답을 받을 수 있음
  • 실시간 데이터 전송이 어렵고, 대기 시간이 길어짐
  • 상태 유지가 어렵기 때문에 연속적인 대화 흐름을 관리하기 어려움

✅ WebSocket의 장점

  • 서버와 클라이언트 간의 지속적인 연결 유지 가능
  • 실시간 데이터 전송 지원 → 대화형 챗봇 구현 가능
  • 데이터 오버헤드가 적음 → HTTP보다 네트워크 효율성이 높음

2) Ktor에서 WebSocket 설정하기

Ktor는 WebSocket을 기본적으로 지원하며, ktor-websockets 라이브러리를 사용하여 WebSocket을 쉽게 구현할 수 있습니다.

📌 WebSocket 라이브러리 추가 (build.gradle.kts)

dependencies {
    implementation("io.ktor:ktor-server-websockets:2.3.5")
}

3) WebSocket 기본 설정 (WebSocketModule.kt)

WebSocket을 지원하도록 Ktor 서버 설정을 추가합니다.

package ktor_chatbot.plugins

import io.ktor.server.application.*
import io.ktor.server.websocket.*
import java.time.Duration

fun Application.configureWebSockets() {
    install(WebSockets) {
        pingPeriod = Duration.ofSeconds(30)  // 클라이언트와 주기적으로 연결 유지
        timeout = Duration.ofSeconds(15)  // 연결이 끊겼을 때 타임아웃 설정
        maxFrameSize = Long.MAX_VALUE  // 최대 프레임 크기 설정
        masking = false
    }
}

📌 WebSocket 설정 옵션

  • pingPeriod = Duration.ofSeconds(30)30초마다 클라이언트와 연결 확인
  • timeout = Duration.ofSeconds(15)15초 동안 응답이 없으면 타임아웃 처리
  • maxFrameSize = Long.MAX_VALUE최대 메시지 크기 설정 (기본적으로 무제한)
  • masking = false보안용 마스킹 비활성화 (서버에서 불필요한 CPU 사용 방지)

4) WebSocket 라우트 추가 (Routing.kt)

이제 WebSocket을 통해 메시지를 송수신할 수 있도록 API 엔드포인트를 추가합니다.

package ktor_chatbot.plugins

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 org.slf4j.LoggerFactory

fun Application.configureRouting() {
    val logger = LoggerFactory.getLogger("WebSocketRouting")

    routing {
        webSocket("/chat") {
            logger.info("클라이언트 WebSocket 연결됨.")

            try {
                incoming.consumeEach { frame ->
                    if (frame is Frame.Text) {
                        val receivedText = frame.readText()
                        logger.info("클라이언트 메시지: $receivedText")

                        // 응답 메시지 전송
                        send(Frame.Text("AI 응답: '$receivedText' 메시지를 받았습니다."))
                    }
                }
            } catch (e: Exception) {
                logger.error("WebSocket 오류 발생: ${e.localizedMessage}")
            } finally {
                logger.info("WebSocket 연결 종료됨.")
            }
        }
    }
}

📌 코드 설명

  • webSocket("/chat")/chat 경로에서 WebSocket 요청을 수락
  • incoming.consumeEach { frame -> ... } → 클라이언트가 보낸 메시지를 수신
  • if (frame is Frame.Text) {} → 텍스트 메시지를 판별하고 처리
  • send(Frame.Text(...)) → AI 응답을 클라이언트에 전송

5) Ktor WebSocket 서버 실행 및 테스트

🔹 Ktor 서버 실행

./gradlew run

🔹 WebSocket 테스트 (웹 브라우저 콘솔 이용)

웹 브라우저의 개발자 도구(Console)에서 WebSocket 연결 및 메시지 전송을 테스트할 수 있습니다.

const socket = new WebSocket("ws://localhost:8080/chat");

socket.onopen = () => {
    console.log("WebSocket 연결 성공!");
    socket.send("안녕하세요, AI!");
};

socket.onmessage = (event) => {
    console.log("서버 응답:", event.data);
};

socket.onclose = () => {
    console.log("WebSocket 연결 종료");
};

✅ 예상 결과

[클라이언트] WebSocket 연결 성공!
[클라이언트] 서버 응답: AI 응답: '안녕하세요, AI!' 메시지를 받았습니다.
[클라이언트] WebSocket 연결 종료

6) WebSocket 기본 구조 정리

기능 구현 코드
WebSocket 설정 configureWebSockets() (WebSocket 활성화)
WebSocket 라우트 추가 webSocket("/chat") (클라이언트 연결 처리)
메시지 수신 및 응답 처리 incoming.consumeEach { frame -> ... }
서버 실행 및 테스트 ./gradlew run + 브라우저 콘솔 테스트

7) 다음 단계

이제 WebSocket을 활용하여 실시간으로 클라이언트와 메시지를 주고받을 수 있도록 설정했습니다.
다음으로 3.4.2 실시간 대화 데이터 처리를 진행하여 AI 챗봇이 WebSocket을 통해 LLM과 상호작용하는 기능을 추가하겠습니다! 🚀

 

반응형
공지사항
최근에 올라온 글
최근에 달린 댓글
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
글 보관함
반응형