자바스크립트 AI 챗봇 만들기 part4

자바스크립트 AI 챗봇 만들기 part4 : 외부 API 연동으로 챗봇 기능 확장

part3에서 TensorFlow.js의 QnA 모델을 적용해 챗봇이 질문에 답변하도록 만들었다. 이번에는 외부 API를 연동해 실용성을 더한다. OpenWeatherMap API를 사용해 날씨 정보를 제공하며, QnA 모델과 조화를 이루게 한다.


이제 챗봇은 문맥 기반 답변뿐 아니라 실시간 날씨 데이터도 알려준다. 초보자도 따라 할 수 있도록 상세히 설명한다.


part4에서 무엇을 다루는가?

part4의 목표는 다음과 같다:

- API의 기본 개념을 이해한다.

- OpenWeatherMap API를 등록하고 설정한다.

- 챗봇에 날씨 조회 기능을 추가한다.

- QnA 모델과 API 응답을 통합한다.

part3의 my-chatbot 폴더를 이어서 사용한다.


API란 무엇인가?

API(Application Programming Interface)는 프로그램 간 데이터를 주고받게 하는 인터페이스다. 챗봇에서는 외부 서비스에서 실시간 데이터를 가져와 활용한다. 예를 들어, 날씨 API는 도시 이름을 주면 현재 날씨를 반환한다.


이번에 사용할 OpenWeatherMap은 무료로 날씨 데이터를 제공한다. 공식 사이트에서 쉽게 설정한다.


OpenWeatherMap API 설정하기

API를 사용하려면 키를 발급받는다. 초보자도 할 수 있게 단계별로 설명한다.


1. 계정 생성

- OpenWeatherMap에 접속한다.

- 오른쪽 상단 "Sign Up"을 클릭하고 이메일, 비밀번호를 입력해 가입한다.

- 이메일 인증 후 로그인한다.


2. API 키 발급

- 로그인 후 상단 "API Keys" 탭으로 이동한다.

- 기본 키가 이미 생성돼 있다. 없다면 "Generate"를 클릭해 새 키를 만든다.

- 키를 복사한다. 예: abcd1234efgh5678

키는 비공개로 유지한다. 다른 사람과 공유하지 않는다.


날씨 조회 기능 추가하기

이제 챗봇에 날씨 API를 연동한다. fetch로 데이터를 가져오고, QnA 모델과 함께 사용한다.


1. API 호출 함수 작성

script.js에 날씨 조회 함수를 추가한다:

async function getWeather(city) {
    const apiKey = 'YOUR_API_KEY'; // 발급받은 키로 교체한다
    try {
        const response = await fetch(`https://api.openweathermap.org/data/2.5/weather?q=${city}&appid=${apiKey}&units=metric&lang=ko`);
        const data = await response.json();
        if (data.cod === 200) {
            return `${city}의 날씨는 ${data.weather[0].description}, 온도는 ${data.main.temp}°C다.`;
        }
        return '도시를 찾을 수 없다. 정확한 이름을 입력한다.';
    } catch (error) {
        console.error('날씨 API 오류:', error);
        return '날씨 데이터를 가져오지 못했다.';
    }
}

설명

- YOUR_API_KEY를 발급받은 키로 바꾼다.

- `units=metric`: 온도를 섭씨로 반환한다.

- `lang=ko`: 한국어로 응답한다.


2. 전체 코드 통합

script.js를 아래처럼 수정한다:

const messages = document.getElementById('chat-messages');
const userInput = document.getElementById('user-input');
const sendBtn = document.getElementById('send-btn');

function addMessage(text, isUser = false) {
    const message = document.createElement('p');
    message.textContent = text;
    message.style.textAlign = isUser ? 'right' : 'left';
    message.style.background = isUser ? '#ff4e00' : '#f0f0f0';
    message.style.color = isUser ? '#fff' : '#333';
    message.style.padding = '8px 12px';
    message.style.margin = '5px 0';
    message.style.borderRadius = '4px';
    message.style.display = 'inline-block';
    messages.appendChild(message);
    messages.scrollTop = messages.scrollHeight;
}

// 기본 문맥 정의
const context = `
    이 챗봇은 자바스크립트로 만들어졌다. 이름은 Chatbot이며 bdool에서 개발했다.
    현재 날짜는 2025년 2월 28일이다. 주요 기능은 대화와 질문에 답하는 것이다.
    날씨 정보도 제공할 수 있다. 개발자는 사용자를 돕기 위해 만든다.
`;

let qnaModel;

// 모델 로드
async function loadQnAModel() {
    try {
        qnaModel = await qna.load();
        console.log('QnA 모델이 로드됐다');
        addMessage('챗봇 준비가 완료됐다! 질문을 기다린다.');
    } catch (err) {
        console.error('모델 로드 실패:', err);
        addMessage('모델 로드에 실패했다. 기본 응답만 제공한다.');
    }
}

// 날씨 조회
async function getWeather(city) {
    const apiKey = 'YOUR_API_KEY'; // 발급받은 키로 교체한다
    try {
        const response = await fetch(`https://api.openweathermap.org/data/2.5/weather?q=${city}&appid=${apiKey}&units=metric&lang=ko`);
        const data = await response.json();
        if (data.cod === 200) {
            return `${city}의 날씨는 ${data.weather[0].description}, 온도는 ${data.main.temp}°C다.`;
        }
        return '도시를 찾을 수 없다. 정확한 이름을 입력한다.';
    } catch (error) {
        console.error('날씨 API 오류:', error);
        return '날씨 데이터를 가져오지 못했다.';
    }
}

// 질문 처리
async function getAnswer(question) {
    if (!qnaModel) {
        return '모델이 아직 로드되지 않았다. 잠시 기다린다.';
    }
    try {
        const answers = await qnaModel.findAnswers(question, context);
        if (answers.length > 0 && answers[0].text) {
            return answers[0].text;
        }
        return '질문에 대한 답을 찾지 못했다. 다른 질문을 해본다.';
    } catch (err) {
        console.error('답변 생성 실패:', err);
        return '답변을 생성하지 못했다.';
    }
}

// 키워드 및 날씨 응답
async function getResponse(input) {
    input = input.toLowerCase();
    if (input.includes('안녕')) {
        return '안녕! 나도 반갑다.';
    }
    if (input.includes('뭐해')) {
        return '너랑 대화하려고 기다린다!';
    }
    if (input.includes('날씨')) {
        const cityMatch = input.match(/(?:\S+\s+)?(\S+) 날씨/);
        const city = cityMatch ? cityMatch[1] : '서울';
        return await getWeather(city);
    }
    return await getAnswer(input);
}

async function sendMessage() {
    const inputText = userInput.value.trim();
    if (inputText) {
        addMessage(inputText, true);
        const response = await getResponse(inputText);
        setTimeout(() => addMessage(response), 500);
        userInput.value = '';
    }
}

sendBtn.addEventListener('click', sendMessage);

userInput.addEventListener('keypress', (e) => {
    if (e.key === 'Enter') {
        sendMessage();
    }
});

// 페이지 로드 시 모델 초기화
window.onload = loadQnAModel;

코드 설명

- getWeather: OpenWeatherMap API로 날씨 데이터를 가져온다.

- getResponse: 입력에 "날씨"가 포함되면 도시를 추출해 날씨를 조회한다. 없으면 QnA 모델로 답변한다.

- 정규식: "서울 날씨"처럼 도시를 인식한다. 기본값은 "서울"이다.


테스트

브라우저에서 테스트한다:

- "서울 날씨 어때?" → "서울의 날씨는 맑음, 온도는 10°C다."

- "챗봇 이름이 뭐야?" → "Chatbot"

- "안녕" → "안녕! 나도 반갑다."

API 키가 올바른지, 모델이 로드됐는지 확인한다.


API와 QnA 모델의 통합

QnA 모델은 문맥 기반 답변을 제공하고, API는 실시간 데이터를 가져온다. 이를 조합하면 챗봇이 더 유용해진다.


- **문맥 의존**: QnA는 고정된 문맥에서만 답한다.

- **API 확장**: 날씨처럼 외부 데이터를 추가한다.

입력에 따라 키워드 응답, API 호출, QnA 답변을 우선순위로 처리한다.


성능 및 한계

장점

- 실시간 날씨 데이터를 제공한다.

- QnA와 자연스럽게 통합된다.


한계

- API 호출은 인터넷 연결이 필요하다.

- 무료 API는 호출 횟수 제한(1분당 60회)이 있다.

API 키가 노출되지 않게 환경 변수를 사용한다. 배포 시 고려한다.


개선 방법

- **캐싱**: 자주 묻는 도시의 날씨를 저장한다.

- **다양한 API**: 뉴스나 시간 API를 추가한다.


마무리

챗봇에 날씨 API를 연동했다. 이제 질문 답변과 실시간 데이터를 모두 처리한다. 다음 포스팅에서 성능을 최적화하고 배포한다.


+ Recent posts