๐ŸŽ“ ๊ณผํ•™ ๊ต์‚ฌ Chatbot โ€” ์ „์ฒด ์„ค๋ช… (๋ชจ๋‘)

๋ฐœํ–‰: (2026๋…„ 2์›” 21์ผ ์˜ค์ „ 09:08 GMT+9)
6 ๋ถ„ ์†Œ์š”
์›๋ฌธ: Dev.to

Source: Dev.to

๐ŸŒฑ 0๏ธโƒฃ ์šฐ๋ฆฌ๊ฐ€ ๋งŒ๋“ค๊ณ  ์žˆ๋Š” ๊ฒƒ

์šฐ๋ฆฌ๋Š” ๊ณผํ•™ ๊ต์‚ฌ AI ์ฑ—๋ด‡ ๋ฐฑ์—”๋“œ๋ฅผ ๋งŒ๋“ค๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค.

์ด AI:

  • ๊ณผํ•™ ์งˆ๋ฌธ์— ๋‹ต๋ณ€ํ•ฉ๋‹ˆ๋‹ค
  • ๊ต์‚ฌ์ฒ˜๋Ÿผ ์„ค๋ช…ํ•ฉ๋‹ˆ๋‹ค
  • ์ฑ„ํŒ… ๊ธฐ๋ก์„ ๊ธฐ์–ตํ•ฉ๋‹ˆ๋‹ค
  • ๊ณผํ•™์ด ์•„๋‹Œ ์งˆ๋ฌธ์€ ๋ฌด์‹œํ•ฉ๋‹ˆ๋‹ค

์˜ˆ์‹œ

Student: What is gravity?
AI: Gravity is a forceโ€ฆ

Student: Explain again
AI: As I explained earlierโ€ฆ

๐Ÿ‘‰ AI๋Š” ๋งฅ๋ฝ์„ ๊ธฐ์–ตํ•ฉ๋‹ˆ๋‹ค.

Science Teacher AI diagram

๐Ÿง  1๏ธโƒฃ ์‹œ์Šคํ…œ ์ž‘๋™ ๋ฐฉ์‹

Flow

Student โ†’ API โ†’ Filter โ†’ LangChain โ†’ OpenAI โ†’ Answer
                           โ†‘
                        Memory

Steps

  1. ํ•™์ƒ์ด ์งˆ๋ฌธ์„ ๋ณด๋ƒ…๋‹ˆ๋‹ค.
  2. Express API๊ฐ€ ์ด๋ฅผ ๋ฐ›์Šต๋‹ˆ๋‹ค.
  3. ๊ณผํ•™ ํ•„ํ„ฐ๊ฐ€ ๋‚ด์šฉ์„ ํ™•์ธํ•ฉ๋‹ˆ๋‹ค.
  4. LangChain์ด ๋ฉ”๋ชจ๋ฆฌ๋ฅผ ์ถ”๊ฐ€ํ•ฉ๋‹ˆ๋‹ค.
  5. OpenAI๊ฐ€ ๋‹ต๋ณ€์„ ์ƒ์„ฑํ•ฉ๋‹ˆ๋‹ค.
  6. API๊ฐ€ ์‘๋‹ต์„ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค.

๐Ÿงฐ 2๏ธโƒฃ Technologies

  • Node.js โ€“ ๋ฐฑ์—”๋“œ ๋Ÿฐํƒ€์ž„
  • Express โ€“ API ํ”„๋ ˆ์ž„์›Œํฌ
  • LangChain โ€“ LLM ์˜ค์ผ€์ŠคํŠธ๋ ˆ์ด์…˜
  • OpenAI โ€“ AI ๋ชจ๋ธ ์ œ๊ณต์—…์ฒด
  • BufferMemory โ€“ ์ฑ„ํŒ… ๋ฉ”๋ชจ๋ฆฌ ์ฒ˜๋ฆฌ
  • dotenv โ€“ ํ™˜๊ฒฝ ๋ณ€์ˆ˜
  • pnpm โ€“ ํŒจํ‚ค์ง€ ๋งค๋‹ˆ์ €

๐Ÿ“ฆ 3๏ธโƒฃ ํ”„๋กœ์ ํŠธ ๋งŒ๋“ค๊ธฐ

mkdir science-teacher-bot
cd science-teacher-bot
pnpm init

๐Ÿ“ฆ 4๏ธโƒฃ ์˜์กด์„ฑ ์„ค์น˜

pnpm add express cors dotenv langchain @langchain/openai nodemon

๐Ÿ“ 5๏ธโƒฃ ํด๋” ๊ตฌ์กฐ

science-teacher-bot/
โ”‚
โ”œโ”€โ”€ src/
โ”‚   โ”œโ”€โ”€ memory.mjs
โ”‚   โ”œโ”€โ”€ llm.mjs
โ”‚   โ”œโ”€โ”€ filter.mjs
โ”‚   โ”œโ”€โ”€ route.mjs
โ”‚   โ””โ”€โ”€ server.mjs
โ”‚
โ”œโ”€โ”€ .env
โ””โ”€โ”€ package.json

๐Ÿ” 6๏ธโƒฃ OpenAI ํ‚ค

.env ํŒŒ์ผ์„ ์ƒ์„ฑํ•˜์„ธ์š”:

OPENAI_API_KEY=your_key_here
PORT=3000

๐Ÿ’พ 7๏ธโƒฃ memory.mjs

import { BufferMemory } from "langchain/memory";

export const memory = new BufferMemory({
  returnMessages: true,
  memoryKey: "chat_history",
});

What it does

  • ์ฑ„ํŒ… ๊ธฐ๋ก์„ ์ €์žฅํ•ฉ๋‹ˆ๋‹ค
  • ์ด์ „ ๋ฉ”์‹œ์ง€๋ฅผ ๊ธฐ์–ตํ•ฉ๋‹ˆ๋‹ค
  • AI์— ์ปจํ…์ŠคํŠธ๋ฅผ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค

๐Ÿง  8๏ธโƒฃ llm.mjs

import "dotenv/config";
import { ChatOpenAI } from "@langchain/openai";
import { ConversationChain } from "langchain/chains";
import { memory } from "./memory.mjs";

const llm = new ChatOpenAI({
  model: "gpt-4o-mini",
  temperature: 0.3,
});

const template = `
You are a science teacher.
Answer only science questions.
Explain in a simple way.

Conversation:
{chat_history}

Student: {input}
Teacher:
`;

export const chatChain = new ConversationChain({
  llm,
  memory,
  prompt: template,
});

ConversationChain ๋‹ค์ด์–ด๊ทธ๋žจ

๋ฌด์—‡์„ ํ•˜๋Š”๊ฐ€

  • AI ๋ชจ๋ธ์„ ์ƒ์„ฑํ•ฉ๋‹ˆ๋‹ค
  • ๊ต์‚ฌ ํ–‰๋™์„ ์ •์˜ํ•ฉ๋‹ˆ๋‹ค
  • ๋ฉ”๋ชจ๋ฆฌ ์ปดํฌ๋„ŒํŠธ๋ฅผ ์—ฐ๊ฒฐํ•ฉ๋‹ˆ๋‹ค
  • ์ฑ—๋ด‡ ์ฒด์ธ์„ ๊ตฌ์ถ•ํ•ฉ๋‹ˆ๋‹ค

๐Ÿ”ฌ 9๏ธโƒฃ filter.mjs

export function isScience(text) {
  const keywords = [
    "physics", "chemistry", "biology",
    "atom", "cell", "energy", "force",
    "gravity", "plant", "reaction",
    "photosynthesis", "molecule"
  ];

  return keywords.some(word =>
    text.toLowerCase().includes(word)
  );
}

What it does

  • ์งˆ๋ฌธ์ด ๊ณผํ•™๊ณผ ๊ด€๋ จ๋œ ๊ฒƒ์ธ์ง€ ํ™•์ธํ•ฉ๋‹ˆ๋‹ค
  • ๋น„๊ณผํ•™ ์ฃผ์ œ๋ฅผ ์ฐจ๋‹จํ•ฉ๋‹ˆ๋‹ค

๐ŸŒ 1๏ธโƒฃ0๏ธโƒฃ route.mjs

import express from "express";
import { chatChain } from "./llm.mjs";
import { isScience } from "./filter.mjs";

export const router = express.Router();

router.post("/", async (req, res) => {
  const { text } = req.body;

  if (!text) {
    return res.json({ error: "Question required" });
  }

  if (!isScience(text)) {
    return res.json({ answer: "I only answer science questions." });
  }

  const response = await chatChain.predict({ input: text });
  res.json({ answer: response });
});

๋ฌด์—‡์„ ํ•˜๋Š”๊ฐ€

  • ์งˆ๋ฌธ์„ ๋ฐ›๋Š”๋‹ค
  • ์ž…๋ ฅ์„ ๊ฒ€์ฆํ•œ๋‹ค
  • ๊ณผํ•™ ์งˆ๋ฌธ์ธ์ง€ ํ™•์ธํ•œ๋‹ค
  • AI ์ฒด์ธ์„ ํ˜ธ์ถœํ•œ๋‹ค
  • ๋‹ต๋ณ€์„ ๋ฐ˜ํ™˜ํ•œ๋‹ค

๐Ÿš€ 1๏ธโƒฃ1๏ธโƒฃ server.mjs

import express from "express";
import cors from "cors";
import "dotenv/config";
import { router } from "./route.mjs";

const app = express();

app.use(cors());
app.use(express.json());

app.use("/ask", router);

app.get("/", (req, res) => {
  res.send("Science Teacher AI running");
});

const PORT = process.env.PORT || 3000;
app.listen(PORT, () => {
  console.log("Server running on", PORT);
});

๋ฌด์—‡์„ ํ•˜๋Š”๊ฐ€

  • Express ์„œ๋ฒ„๋ฅผ ์ƒ์„ฑํ•ฉ๋‹ˆ๋‹ค
  • CORS ๋ฐ JSON ํŒŒ์‹ฑ์„ ํ™œ์„ฑํ™”ํ•ฉ๋‹ˆ๋‹ค
  • /ask API ๋ผ์šฐํŠธ๋ฅผ ๋งˆ์šดํŠธํ•ฉ๋‹ˆ๋‹ค
  • ๋ฐฑ์—”๋“œ๋ฅผ ์‹œ์ž‘ํ•ฉ๋‹ˆ๋‹ค

โ–ถ๏ธ 1๏ธโƒฃ2๏ธโƒฃ ํ”„๋กœ์ ํŠธ ์‹คํ–‰

package.json์— ์Šคํฌ๋ฆฝํŠธ๋ฅผ ์ถ”๊ฐ€ํ•ฉ๋‹ˆ๋‹ค:

{
  "scripts": {
    "dev": "node src/server.mjs"
  }
}

์„œ๋ฒ„๋ฅผ ์‹œ์ž‘ํ•ฉ๋‹ˆ๋‹ค:

pnpm dev

๐Ÿงช 1๏ธโƒฃ3๏ธโƒฃ API ํ…Œ์ŠคํŠธ

์—”๋“œํฌ์ธํŠธ: POST http://localhost:3000/ask

์š”์ฒญ ๋ณธ๋ฌธ (JSON):

{
  "text": "What is photosynthesis?"
}

AI์˜ ๋‹ต๋ณ€์ด ํฌํ•จ๋œ JSON ์‘๋‹ต์„ ๋ฐ›์•„์•ผ ํ•ฉ๋‹ˆ๋‹ค.

{
  "text": "What is photosynthesis?"
}

์‘๋‹ต:

{
  "answer": "Photosynthesis is the process..."
}

๐Ÿง  1๏ธโƒฃ4๏ธโƒฃ ๋ฉ”๋ชจ๋ฆฌ ๋ฐ๋ชจ

์งˆ๋ฌธ:

  • ์ค‘๋ ฅ์€ ๋ฌด์—‡์ธ๊ฐ€์š”
  • ๋‹ค์‹œ ์„ค๋ช…ํ•ด ์ฃผ์„ธ์š”

AI๋Š” ์ปจํ…์ŠคํŠธ๋ฅผ ๊ธฐ์–ตํ•ฉ๋‹ˆ๋‹ค.

โš ๏ธ 1๏ธโƒฃ5๏ธโƒฃ ์ค‘์š”ํ•œ ๋ฉ”๋ชจ

ํ˜„์žฌ ๋ฉ”๋ชจ๋ฆฌ:

  • ๋ชจ๋“  ์‚ฌ์šฉ์ž์™€ ๊ณต์œ 
  • ์žฌ์‹œ์ž‘ ์‹œ ์ดˆ๊ธฐํ™”

์‹ค์ œ ์•ฑ์—์„œ๋Š”:

  • DB ๋ฉ”๋ชจ๋ฆฌ
  • ์„ธ์…˜ ID
  • ๋ฒกํ„ฐ ์Šคํ† ์–ด

๐Ÿ† 1๏ธโƒฃ6๏ธโƒฃ ๋‹น์‹ ์ด ๋งŒ๋“  ๊ฒƒ (ํžˆ์–ด๋กœ ๋ ˆ๋ฒจ)

  • โœ… LLM ๋ฐฑ์—”๋“œ
  • โœ… LangChain ํ†ตํ•ฉ
  • โœ… ๋ฉ”๋ชจ๋ฆฌ ์ฑ—๋ด‡
  • โœ… ๊ณผํ•™ ํ•„ํ„ฐ
  • โœ… Express API
  • โœ… Teacher AI

์ด๊ฒƒ์€ ์‹ค์ œ AI ์•ฑ ์•„ํ‚คํ…์ฒ˜์ž…๋‹ˆ๋‹ค.

๐ŸŽ“ 1๏ธโƒฃ7๏ธโƒฃ How to Explain to Students

  • Express๋Š” ํ•™์ƒ ์งˆ๋ฌธ์„ ๋ฐ›์Šต๋‹ˆ๋‹ค.
  • LangChain์€ AI์™€ ๋ฉ”๋ชจ๋ฆฌ๋ฅผ ์—ฐ๊ฒฐํ•ฉ๋‹ˆ๋‹ค.
  • Memory๋Š” ๋Œ€ํ™”๋ฅผ ์ €์žฅํ•ฉ๋‹ˆ๋‹ค.
  • Filter๋Š” ๊ณผํ•™ ๊ด€๋ จ ๋‹ต๋ณ€๋งŒ ๋ณด์žฅํ•ฉ๋‹ˆ๋‹ค.
0 ์กฐํšŒ
Back to Blog

๊ด€๋ จ ๊ธ€

๋” ๋ณด๊ธฐ ยป

โ€˜Lazyโ€™ ๋ฐฑ์—”๋“œ ๊ตฌ์ถ•์„ ๋ฉˆ์ถฐ๋ผ: ๋ฏธ๋ž˜๋Š” Agentic FaaS์™€ MDL์ด๋‹ค

์šฐ๋ฆฌ๋Š” ํ˜„์žฌ ์†Œํ”„ํŠธ์›จ์–ด๋ฅผ ๊ตฌ์ถ•ํ•˜๋Š” ๋ฐฉ์‹์— ํฐ ๋ณ€ํ™”๋ฅผ ๊ฒช๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค. ํ”„๋ก ํŠธ์—”๋“œ๋Š” ๋ฏฟ์„ ์ˆ˜ ์—†์„ ์ •๋„๋กœ ๋™์ ์ด ๋˜์—ˆ์ง€๋งŒ, ๋ฐฑ์—”๋“œโ€”์šด์˜์˜ โ€œ๋‘๋‡Œโ€โ€”๋Š” ์•„์ง๋„โ€ฆ

์„œ๋ธŒ๋„ทํŒ… ์„ค๋ช…

Subnetting์ด๋ž€ ๋ฌด์—‡์ธ๊ฐ€? ํฐ ์•„ํŒŒํŠธ ๊ฑด๋ฌผ์„ ์—ฌ๋Ÿฌ ์ธต์œผ๋กœ ๋‚˜๋ˆ„๋Š” ๊ฒƒ๊ณผ ๊ฐ™๋‹ค. ๊ฐ ์ธต ์„œ๋ธŒ๋„ท์€ ์ž์ฒด ๋ฒˆํ˜ธ๊ฐ€ ๋งค๊ฒจ์ง„ ์œ ๋‹›(hosts)์„ ๊ฐ€์ง€๊ณ , ๊ทธ๋ฆฌ๊ณ  ๊ฑด๋ฌผโ€ฆ

API test scripts ์ž‘์„ฑ์„ ์ค‘๋‹จํ•˜์„ธ์š”. ๋Œ€์‹  ํ‰๋ฒ”ํ•œ ์˜์–ด๋ฅผ ์‚ฌ์šฉํ•˜์„ธ์š”.

๊ฐœ์š” Octrafic๋Š” API ํ…Œ์ŠคํŠธ๋ฅผ ํ‰๋ฒ”ํ•œ ์˜์–ด๋กœ ์ž‘์„ฑํ•  ์ˆ˜ ์žˆ๊ฒŒ ํ•ด์ค๋‹ˆ๋‹ค. ํ…Œ์ŠคํŠธํ•˜๊ณ  ์‹ถ์€ ๋‚ด์šฉ์„ ์„ค๋ช…ํ•˜๋ฉด, AI๊ฐ€ ๊ท€ํ•˜์˜ OpenAPI๋ฅผ ๊ธฐ๋ฐ˜์œผ๋กœ ์ ์ ˆํ•œ HTTP ์š”์ฒญ์„ ์ƒ์„ฑํ•ฉ๋‹ˆ๋‹ค.