누구나 쉽게 만드는 LLM을 활용한 챗봇(Web)
AI/LLM

누구나 쉽게 만드는 LLM을 활용한 챗봇(Web)

연구소 인턴 2024. 10. 6.
300x250
반응형

1. 개요

지난 글에서 챗봇을 만드는 글을 올렸습니다. 구현한 챗봇은 CLI환경에서 작동하는 형태였는데, 현 시중에 나와있는 ChatGPT나 Gemini등 다양한 챗봇들은 Web Server를 통해 GUI환경으로 보여집니다.

그래서 Web을 구현해서 비슷한 형태로 챗봇을 구현해 보도록 하겠습니다.

 

2. Web 구현

이번 글에서 프로젝트의 구조는 아래와 같습니다.

conversation-app/
├── node_modules/
├── public/
├── src/
│   ├── components/
│   │   ├── ConversationList.js
│   │   ├── AdminPage.js
│   │   ├── Login.js
│   │   ├── SignupPage.js
│   │   ├── ConversationDetail.js
│   │   └── ChatInput.js
│   ├── App.js
│   ├── App.css
│   ├── index.js
│   └── ...
├── package.json
└── ...

먼저 Web을 구성할 React기반의 폴더를 만들어줍니다.

아래와 같은 명령어를 사용하기 위해서는 Node.js와 npm이 선행적으로 설치되어있어야합니다.

npx create-react-app conversation-app #"conversation-app" 부분은 폴더명입니다.
cd conversation-app

 

  • npx: 로컬이나 글로벌로 설치되지 않은 패키지를 일회성으로 실행할 수 있는 도구입니다.
  • create-react-app: React 프로젝트를 빠르게 설정해주는 도구입니다.
  • conversation-app: 생성될 프로젝트의 이름입니다. 원하는 이름으로 변경할 수 있습니다.

 

HTTP의 요청을 처리하기 위해서는 두 가지 방법이 있는데

axios와 fetch방식이 있습니다.

axios는 아래와 같이 React 프로젝트에서 설치를 해야합니다.

npm install axios

그 후 package.json파일에 dependencies 부분에 axios가 잘 들어가 있는지 확인합니다.

{
  "dependencies": {
    "axios": "^1.7.7",
  }
}

fetch는 브라우저의 내장 API로 추가적인 라이브러리 설치는 필요하지 않습니다.

axios방식으로 하다가 오류가 발생해서 저는 fetch 방식으로 진행하게 되었습니다.

 

가. 페이지 작성

src/components내의 페이지들을 만들겠습니다.

저는 사람들이 많이 이용하는 ChatGPT 시안을 활용하여 만들어 보겠습니다.

기본적인 GPT는 좌측에 대화기록이 있고 메인에서 채팅을 주고받는 형태로 되어있습니다.

그래서 저는 5가지 js를 만들어서 활용하겠습니다.

  • ConversationList.js
  • ConversationDetail.js
  • ChatInput.js
  • AdminPage.js
  • Login.js
  • SignupPage.js

1) ConversationList.js

ConversationList.js에서 먼저 React를 import 해줍니다.

import React, { useEffect, useState } from 'react';

사용자에게 대화 목록을 표시하는 역할을 위해 ConversationList를 함수형 컴포넌트로 설정합니다.

const ConversationList = ({ conversations, onSelectConversation }) => {
    return (
        <div className="conversation-list">
            {conversations.map((conversation) => (
                <div 
                    key={conversation.id} 
                    onClick={() => onSelectConversation(conversation)} 
                    className="conversation-item"
                >
                    <h4>대화 {conversation.id}</h4>
                </div>
            ))}
        </div>
    );
};

이 컴포넌트는 두 가지의  props를 받습니다:

  • conversations: 각 대화 객체를 포함하는 배열
  • onSelectConversation: 대화 항목이 클릭될 때 호출되는 함수

 

  • conversations 배열을 map 함수를 사용하여 순회하면서 각 대화 객체에 대해 div 요소를 생성합니다.
  • 각 대화 항목 div의 주요 속성:
    • key={conversation.id}: React에서 리스트를 렌더링할 때 각 항목을 고유하게 식별하기 위해 사용합니다.
    • onClick={() => onSelectConversation(conversation)}: 대화 항목이 클릭되면 onSelectConversation 함수가 호출되고, 해당 대화 객체가 인자로 전달됩니다.
    • className="conversation-item": 각 대화 항목에 적용되는 CSS 클래스입니다.

 

2) ConversationDetail.js

const ConversationDetail = ({ token, conversationId }) => {
    const [conversation, setConversation] = useState(null);
    const [loading, setLoading] = useState(false);
    const [error, setError] = useState(null);
  • conversation : 불러온 대화 대이터를 저장
  • loading : 데이터 로딩 상태
  • error : 데이터 요청 중 발생한 오류 메시지 저장
useEffect(() => {
        if (conversationId !== null && token) {
            fetchConversation();
        }
    }, [conversationId, token]);

 

 

  • 의존성 배열: [conversationId, token]
    • conversationId 또는 token이 변경될 때마다 실행됩니다.
  • 조건문:
    • conversationId가 null이 아니고 token이 존재할 경우 fetchConversation 함수를 호출하여 데이터를 불러옵니다.
const fetchConversation = async () => {
        setLoading(true);
        setError(null);
        try {
            const response = await fetch(`http://localhost:8000/conversations/${token}/${conversationId}`);
            if (!response.ok) {
                throw new Error('대화를 불러오는 중 오류가 발생했습니다.');
            }
            const data = await response.json();
            setConversation(data.conversation);
        } catch (err) {
            console.error(err);
            setError(err.message);
        } finally {
            setLoading(false);
        }
    };

 

 API를 호출하여 특정 대화의 상세 정보를 가져오는 로직으로 과정은 아래와 같습니다.

  • 과정:
    1. setLoading(true): 로딩 상태를 활성화합니다.
    2. setError(null): 이전의 오류 상태를 초기화합니다.
    3. API 요청:
      • URL: "http://localhost:8000/conversations/${token}/${conversationId}"
    4. 응답 처리:
      • 응답이 성공적이지 않으면 오류를 던집니다.
      • 성공 시, 응답 데이터를 JSON으로 파싱하고 setConversation(data.conversation)을 통해 상태를 업데이트합니다.
    5. 오류 처리:
      • 오류 발생 시, 콘솔에 로그를 남기고 setError(err.message)를 통해 오류 메시지를 상태에 저장합니다.
    6. 최종 처리:
      • setLoading(false): 로딩 상태를 비활성화합니다.
    if (loading) {
        return <div>로딩 중...</div>;
    }

    if (error) {
        return <div style={{ color: 'red' }}>{error}</div>;
    }

    if (conversation === null) {
        return <div>안녕하세요. BJ-GPT입니다. 무엇이든 물어보시면 답변해드릴께요.</div>;
    }

    return (
        <div className='main-content'>
            <h3>대화 내용</h3>
            <p><strong>사용자:</strong> {conversation.user_input}</p>
            <p><strong>BJ-GPT:</strong> {conversation.ai_response}</p>
        </div>
    );
};

 

  • 로딩 상태 (loading이 true인 경우):
    • <div>로딩 중...</div>을 렌더링하여 사용자에게 로딩 중임을 표시합니다.
  • 오류 상태 (error가 존재하는 경우):
    • 오류 메시지를 빨간색 텍스트로 표시합니다.
  • 대화 데이터 없음 (conversation이 null인 경우):
    • 기본 인삿말인 "안녕하세요. BJ-GPT입니다. 무엇이든 물어보시면 답변해드릴께요."를 표시합니다.
  • 대화 데이터 존재 (conversation이 존재하는 경우):
    • 대화 내용을 상세히 표시합니다:
      • 사용자 입력: conversation.user_input
      • BJ-GPT의 응답: conversation.ai_response

 

300x250
반응형

댓글