๐Ÿ’ป ๊ฐœ๋ฐœ ์ผ์ง€/SpringBoot

[SpringBoot/Kotlin] SSE ํ™œ์šฉํ•œ AI Streaming Chat ๊ตฌํ˜„ (w. React) - (2) FE

์ ์ด 2025. 2. 12. 20:43
๋ฐ˜์‘ํ˜•

SSE ํ™œ์šฉํ•œ AI Streaming Chat ๊ตฌํ˜„ (w. React) - 2ํƒ„: ์›น ์ฑ— ๊ตฌํ˜„

โžก๏ธ 1ํƒ„: ์„œ๋ฒ„ ๊ตฌํ˜„

 

[SpringBoot/Kotlin] SSE ํ™œ์šฉํ•œ AI Streaming Chat ๊ตฌํ˜„ (w. React)

SSE ํ™œ์šฉํ•œ AI Streaming Chat ๊ตฌํ˜„ (w. React) - 1ํƒ„: ์„œ๋ฒ„ ๊ตฌํ˜„๋ฏธ๋ฆฌ๋ณด๊ธฐ์ „์ฒด ์ฝ”๋“œ๋Š” ์•„๋ž˜์—!Spring Boot์™€ SSE๋ฅผ ํ™œ์šฉํ•˜์—ฌ AI Streaming Chat ์„œ๋น„์Šค๋ฅผ ๊ตฌํ˜„ํ•˜์˜€๋‹ค.์‚ฌ์‹ค ์ฑ„ํŒ… ์ŠคํŠธ๋ฆฌ๋ฐ์„ ๊ตฌํ˜„ํ•˜๊ธฐ ์œ„ํ•œ ๋‹ค์–‘ํ•œ ์–ธ

doteloper.tistory.com

๋ฏธ๋ฆฌ๋ณด๊ธฐ

์ด๋ฒˆ ํฌ์ŠคํŒ…์€ ์ €๋ฒˆ ์„œ๋ฒ„ ๊ตฌํ˜„์„ ๋ฐ›๋Š” ์›น ์ฑ„ํŒ…์— ๊ด€๋ จ๋œ ์ฝ”๋“œ์ด๋‹ค.

์‚ฌ์‹ค ์ด๋ฒˆ ํ”„๋กœ์ ํŠธ์˜ ํ”„๋ก ํŠธ ๋‹ด๋‹น์€ “GPT”์˜€๋‹ค.

๋”ฐ๋ผ์„œ ์„ค๋ช…์˜ ํ•œ๊ณ„๊ฐ€ ์žˆ์œผ๋ฏ€๋กœ, ์ค‘์š”ํ•œ ์‹ค์‹œ๊ฐ„ ์‘๋‹ต ์ฒ˜๋ฆฌ๋งŒ ๊ฐ„๋‹จํžˆ ์„ค๋ช…ํ•˜๊ณ  ๋งˆ์น˜๋„๋ก ํ•˜๊ฒ ๋‹ค.

(โ€ผ๏ธโ€ผ๏ธโ€ผ๏ธโ€ผ๏ธโ€ผ๏ธ ์•„๋ž˜ ๋ชจ๋“  ํ”„๋ก ํŠธ ์ฝ”๋“œ๋Š” chat gpt๋กœ๋งŒ ๋งŒ๋“  ์ฝ”๋“œ๋กœ ์‹ค์ œ FE ๊ธฐ์ˆ ๊ณผ ๋ฉ€๋ฆฌ ๋–จ์–ด์ ธ์žˆ์„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค โ€ผ๏ธโ€ผ๏ธโ€ผ๏ธโ€ผ๏ธโ€ผ๏ธ)

๊ธฐ์ˆ  ์Šคํƒ

  • React (Vite)
  • TypeScript
  • tailwindcss

SSE๋ฅผ ์ด์šฉํ•œ ์‹ค์‹œ๊ฐ„ ์‘๋‹ต ์ฒ˜๋ฆฌ

const sendMessage = () => {
  if (!input.trim() || isComposing) return;

  setMessages((prev) => [...prev, { role: "user", text: input }]);
  setInput("");
  setLoading(true);

  const eventSource = new EventSource(`${API_URL}?query=${encodeURIComponent(input)}`);
  let assistantText = "";

  eventSource.onmessage = (event) => {
    assistantText += event.data.replace(/^"|"$/g, "");
    setMessages((prev) => {
      if (prev.length === 0 || prev[prev.length - 1].role !== "assistant") {
        return [...prev, { role: "assistant", text: assistantText }];
      } else {
        return prev.map((msg, index) => index === prev.length - 1 ? { ...msg, text: assistantText } : msg);
      }
    });
  };

  eventSource.onerror = () => {
    eventSource.close();
    setLoading(false);
  };
};
  • `EventSource`๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ๋ฐฑ์—”๋“œ์—์„œ ์˜ค๋Š” ์ŠคํŠธ๋ฆฌ๋ฐ ๋ฐ์ดํ„ฐ๋ฅผ ์ˆ˜์‹ 
  • `onmessage` ์ด๋ฒคํŠธ์—์„œ ์ˆ˜์‹ ํ•œ ๋ฐ์ดํ„ฐ๋ฅผ ๊ธฐ์กด ํ…์ŠคํŠธ์— ์ถ”๊ฐ€ํ•˜๋ฉด์„œ, UI๋ฅผ ์‹ค์‹œ๊ฐ„์œผ๋กœ ์—…๋ฐ์ดํŠธ
  • ํ•œ ๊ธ€์ž์”ฉ ์ถ”๊ฐ€๋˜๋ฏ€๋กœ ์ž์—ฐ์Šค๋Ÿฌ์šด ์ŠคํŠธ๋ฆฌ๋ฐ ํšจ๊ณผ๊ฐ€ ๊ตฌํ˜„
  • `onerror` ์ด๋ฒคํŠธ์—์„œ ์ŠคํŠธ๋ฆผ์„ ์ข…๋ฃŒํ•˜๊ณ  ๋กœ๋”ฉ ์ƒํƒœ๋ฅผ ํ•ด์ œ

์ „์ฒด ์ฝ”๋“œ

https://github.com/jeongum/openai-chat/tree/master/my-sse-chat

 

openai-chat/my-sse-chat at master · jeongum/openai-chat

Contribute to jeongum/openai-chat development by creating an account on GitHub.

github.com

 

๋ฐ˜์‘ํ˜•