본문 바로가기
AI

Google Gemini 2.5 Pro와 LangChain으로 간단한 LLM 챗봇 서버 만들기

by markbyun 2025. 5. 8.

Introduction

이 글에서는 Google Gemini 2.5 ProLangChain을 활용해 가볍지만 강력한 챗봇 백엔드를 구현하는 방법을 다룹니다. 또한, 대화형 AI의 내부 구조를 이해하고, 대화 친화적인 프론트엔드 인터페이스를 배포하는 방법도 설명합니다.

프로토타입을 만들든, 기업 규모의 애플리케이션에 LLM을 통합하든, 이 패턴은 탄탄한 기반을 제공합니다.


Step 1: Install Dependencies

다음은 우리가 사용할 최소 기술 스택입니다:

Python Packages

pip install flask flask-cors langchain langchain-google-genai python-dotenv

Google API 키가 담긴 .env 파일이 있는지 확인하세요:

GOOGLE_API_KEY=your_google_api_key_here

Step 2: Chatbot Architecture

시스템이 작동하는 방식에 대한 개요는 다음과 같습니다:

User (Web UI)
     │
     ▼
HTTP POST /chat
     │
     ▼
Flask API
     │
     ▼
LangChain Prompt Template → Gemini 2.5 Pro (via Google Generative AI)
     │
     ▼
Response → JSON → UI
  • 프론트엔드는 사용자 입력을 포함한 POST 요청을 보냅니다.
  • Flask는 API를 처리하고 LangChain 체인을 호출합니다.
  • 체인은 프롬프트 템플릿을 적용하고 이를 Gemini 2.5 Pro에 전달합니다.
  • 결과는 JSON 응답으로 프론트엔드에 반환됩니다.

Step 3: Backend Code (Flask + LangChain + Gemini)

Key Takeaways

  • 프롬프트는 선언적으로 구성되며, LangChain은 이를 Gemini LLM으로 직접 전달합니다.
  • 현대적인 LangChain Runnable 스타일 (prompt | llm)을 사용하여 처리 흐름을 정의합니다.
  • Flask와 CORS를 이용한 빠른 셋업으로 어떤 프론트엔드와도 신속하게 통합할 수 있습니다.

Step 4: Frontend Code

챗봇과 상호작용하기 위한 최소한의 HTML/JavaScript 클라이언트입니다:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8" />
  <title>Gemini Chatbot</title>
  <style>
    body {
      font-family: 'Segoe UI', sans-serif;
      background: #f5f5f5;
      margin: 0;
      padding: 0;
      display: flex;
      flex-direction: column;
      height: 100vh;
    }

    #chat-container {
      display: flex;
      flex-direction: column;
      justify-content: space-between;
      height: 100vh;
      max-width: 700px;
      margin: 0 auto;
      padding: 1rem;
    }

    #messages {
      flex: 1;
      overflow-y: auto;
      background: white;
      padding: 1rem;
      border-radius: 10px;
      box-shadow: 0 0 8px rgba(0, 0, 0, 0.1);
    }

    .msg {
      margin: 0.5rem 0;
      padding: 0.75rem 1rem;
      border-radius: 18px;
      max-width: 80%;
      word-wrap: break-word;
      line-height: 1.4;
    }

    .user-msg {
      background-color: #d1e7dd;
      align-self: flex-end;
      text-align: right;
    }

    .bot-msg {
      background-color: #e2e3e5;
      align-self: flex-start;
      text-align: left;
    }

    #input-area {
      display: flex;
      gap: 0.5rem;
      padding-top: 1rem;
    }

    input[type="text"] {
      flex: 1;
      padding: 0.75rem;
      border-radius: 18px;
      border: 1px solid #ccc;
      font-size: 1rem;
    }

    button {
      padding: 0.75rem 1rem;
      border-radius: 18px;
      border: none;
      background-color: #007bff;
      color: white;
      font-size: 1rem;
      cursor: pointer;
    }

    button:hover {
      background-color: #0056b3;
    }

    .loading {
      font-size: 0.9rem;
      color: #999;
      margin-top: 0.5rem;
    }
  </style>
</head>
<body>
  <div id="chat-container">
    <div id="messages"></div>

    <div id="input-area">
      <input type="text" id="user_input" placeholder="Type your message..." onkeydown="handleEnter(event)" />
      <button onclick="sendMessage()">Send</button>
    </div>
    <div id="status" class="loading"></div>
  </div>

  <script>
    const messagesEl = document.getElementById("messages");
    const inputEl = document.getElementById("user_input");
    const statusEl = document.getElementById("status");

    function addMessage(content, isUser = false) {
      const msgDiv = document.createElement("div");
      msgDiv.classList.add("msg", isUser ? "user-msg" : "bot-msg");
      msgDiv.textContent = content;
      messagesEl.appendChild(msgDiv);
      messagesEl.scrollTop = messagesEl.scrollHeight;
    }

    async function sendMessage() {
      const input = inputEl.value.trim();
      if (!input) return;

      addMessage(input, true);
      inputEl.value = "";
      inputEl.disabled = true;
      statusEl.textContent = "Thinking...";

      try {
        const response = await fetch("http://localhost:8000/chat", {
          method: "POST",
          headers: { "Content-Type": "application/json" },
          body: JSON.stringify({ user_query: input })
        });

        const data = await response.json();
        addMessage(data.response, false);
      } catch (error) {
        addMessage("Error: Could not reach chatbot server.", false);
        console.error(error);
      } finally {
        inputEl.disabled = false;
        inputEl.focus();
        statusEl.textContent = "";
      }
    }

    function handleEnter(event) {
      if (event.key === "Enter") {
        sendMessage();
      }
    }
  </script>
</body>
</html>

Key UX Features

채팅 말풍선 사용자와 챗봇의 메시지가 다른 스타일로 표시됩니다
자동 스크롤 최신 메시지로 자동 스크롤됩니다
입력 반응성 엔터키 입력으로 메시지를 빠르게 전송할 수 있습니다
로딩 상태 Gemini가 응답 중일 때 “Thinking…”을 표시합니다
스타일링 깔끔하고 모바일 반응형 레이아웃을 제공합니다

이 코드를 그대로 앱에 삽입하거나, Tailwind 또는 Bootstrap을 사용해 디자인 시스템에 맞게 커스터마이징할 수 있습니다.

Step 5: Run the Server

python app.py

index.html 파일을 브라우저에서 열고 Gemini와 채팅을 시작하세요!

web UI sample

다른 도메인에서 테스트할 경우 CORS 설정이 제대로 되어 있고 포트가 열려 있는지 확인하세요.

References