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

로컬 LLM + RAG 기반 AI 채팅봇 만들기 # 30 - 대화 이력 저장 및 조회 API 구현

octo54 2025. 2. 28. 15:53
반응형

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

3.5.2 대화 이력 저장 및 조회 API 구현

이제 PostgreSQL에 저장된 대화 이력을 API를 통해 관리할 수 있도록
대화 기록을 저장하고 조회하는 API를 구현하겠습니다.


1) API 설계 개요

✅ API에서 제공할 기능

1️⃣ 대화 저장하기

  • 사용자가 WebSocket을 통해 메시지를 보낼 때 자동으로 DB에 저장
    2️⃣ 특정 세션의 대화 이력 조회
  • 사용자가 특정 세션에서 진행한 대화 목록을 조회
    3️⃣ 사용자별 전체 대화 세션 목록 조회
  • 사용자가 생성한 모든 대화 세션 리스트를 불러오기

2) API 엔드포인트 설계

HTTP 메서드 엔드포인트 설명
POST /chat/save 대화 메시지 저장
GET /chat/{sessionId} 특정 대화 세션의 대화 기록 조회
GET /chat/sessions/{userId} 사용자의 전체 대화 세션 목록 조회

3) 대화 저장 API 구현 (ChatService.kt)

📌 ChatService.kt – 대화 저장 기능

package ktor_chatbot.services

import ktor_chatbot.database.ChatMessageTable
import ktor_chatbot.database.ChatSessionTable
import ktor_chatbot.database.UserTable
import org.jetbrains.exposed.sql.*
import org.jetbrains.exposed.sql.transactions.transaction
import org.joda.time.DateTime

class ChatService {
    fun saveMessage(sessionId: String, sender: String, message: String) {
        transaction {
            // 세션이 없으면 생성
            ChatSessionTable.insertIgnore {
                it[ChatSessionTable.sessionId] = sessionId
                it[ChatSessionTable.userId] = "guest"
                it[ChatSessionTable.startedAt] = DateTime.now()
            }

            // 메시지 저장
            ChatMessageTable.insert {
                it[ChatMessageTable.sessionId] = sessionId
                it[ChatMessageTable.sender] = sender
                it[ChatMessageTable.message] = message
                it[ChatMessageTable.createdAt] = DateTime.now()
            }
        }
    }

    fun getMessagesBySession(sessionId: String): List<Pair<String, String>> {
        return transaction {
            ChatMessageTable.select { ChatMessageTable.sessionId eq sessionId }
                .orderBy(ChatMessageTable.createdAt, SortOrder.ASC)
                .map { it[ChatMessageTable.sender] to it[ChatMessageTable.message] }
        }
    }

    fun getUserSessions(userId: String): List<String> {
        return transaction {
            ChatSessionTable.select { ChatSessionTable.userId eq userId }
                .map { it[ChatSessionTable.sessionId] }
        }
    }
}

4) API 엔드포인트 추가 (Routing.kt)

package ktor_chatbot.plugins

import io.ktor.server.application.*
import io.ktor.server.routing.*
import io.ktor.server.request.*
import io.ktor.server.response.*
import ktor_chatbot.services.ChatService
import org.koin.ktor.ext.inject

fun Application.configureChatRoutes() {
    val chatService: ChatService by inject()

    routing {
        // 대화 메시지 저장
        post("/chat/save") {
            val request = call.receive<Map<String, String>>()
            val sessionId = request["sessionId"] ?: return@post call.respondText("세션 ID 필요", status = io.ktor.http.HttpStatusCode.BadRequest)
            val sender = request["sender"] ?: return@post call.respondText("보낸 사람 필요", status = io.ktor.http.HttpStatusCode.BadRequest)
            val message = request["message"] ?: return@post call.respondText("메시지 필요", status = io.ktor.http.HttpStatusCode.BadRequest)

            chatService.saveMessage(sessionId, sender, message)
            call.respondText("메시지 저장 완료", status = io.ktor.http.HttpStatusCode.OK)
        }

        // 특정 세션의 대화 이력 조회
        get("/chat/{sessionId}") {
            val sessionId = call.parameters["sessionId"] ?: return@get call.respondText("세션 ID 필요", status = io.ktor.http.HttpStatusCode.BadRequest)
            val messages = chatService.getMessagesBySession(sessionId)
            call.respond(messages)
        }

        // 사용자의 전체 대화 세션 조회
        get("/chat/sessions/{userId}") {
            val userId = call.parameters["userId"] ?: return@get call.respondText("사용자 ID 필요", status = io.ktor.http.HttpStatusCode.BadRequest)
            val sessions = chatService.getUserSessions(userId)
            call.respond(sessions)
        }
    }
}

5) Ktor 서버에서 API 활성화

📌 Application.kt 수정

package ktor_chatbot

import io.ktor.server.application.*
import ktor_chatbot.plugins.*
import ktor_chatbot.services.ChatService
import org.koin.dsl.module
import org.koin.ktor.plugin.Koin

fun Application.module() {
    install(Koin) {
        modules(module {
            single { ChatService() }
        })
    }

    configureChatRoutes()
    configureRouting()
}

6) API 테스트

🔹 Ktor 서버 실행

./gradlew run

🔹 Postman 또는 cURL을 사용한 API 테스트

✅ 1) 대화 메시지 저장

curl -X POST "http://localhost:8080/chat/save" \
     -H "Content-Type: application/json" \
     -d '{"sessionId": "session123", "sender": "user", "message": "안녕하세요!"}'

📌 예상 응답

메시지 저장 완료

✅ 2) 특정 세션의 대화 조회

curl -X GET "http://localhost:8080/chat/session123"

📌 예상 응답 (JSON)

[
    { "sender": "user", "message": "안녕하세요!" },
    { "sender": "ai", "message": "안녕하세요! 무엇을 도와드릴까요?" }
]

✅ 3) 사용자의 전체 대화 세션 조회

curl -X GET "http://localhost:8080/chat/sessions/guest"

📌 예상 응답

[
    "session123",
    "session456"
]

7) 최적화 및 개선 방안

✅ ① 오래된 대화 자동 정리

  • 일정 기간(예: 30일)이 지난 대화 기록을 자동으로 삭제하는 기능 추가 가능
DELETE FROM chat_messages WHERE created_at < NOW() - INTERVAL '30 days';

✅ ② 대화 기록 검색 기능 추가

  • 특정 키워드를 포함하는 대화를 검색하는 기능 추가 가능
SELECT * FROM chat_messages WHERE message ILIKE '%AI%' AND session_id = 'session123';

8) 대화 이력 저장 기능 요약

기능 구현 코드
대화 저장 saveMessage(sessionId, sender, message)
세션별 대화 조회 getMessagesBySession(sessionId)
사용자별 세션 목록 조회 getUserSessions(userId)
API 엔드포인트 추가 post("/chat/save"), get("/chat/{sessionId}"), get("/chat/sessions/{userId}")

9) 다음 단계

이제 PostgreSQL을 연동하여 대화 기록을 저장하고 조회할 수 있도록 구현했습니다.
다음으로 3.5.3 데이터 정규화 및 성능 최적화를 진행하여 대화 저장 및 조회 속도를 개선하는 최적화 방법을 적용하겠습니다! 🚀