Kai 72 2
게시물 메뉴

AI 채팅창, LLM만 붙이면 끝인 줄 알았습니다

이번에 AI를 붙여서 간단한 채팅 기능을 만들 일이 있었습니다.

처음에는 진짜 별거 없을 줄 알았습니다.

채팅창 하나 만들고, GPT나 Claude 같은 AI에 질문을 보내서 받은 답변을 보여주면 끝 아닌가?

막상 만들어보니 아니더군요 ㅎㅎ

우리가 GPT나 Claude에서 너무 당연하게 쓰고 있는 채팅창 안에도 생각보다 여러 가지 기술이 들어가 있었습니다.

이번에 직접 만들면서 알게 된 내용을 최대한 쉽게 정리해봅니다.

먼저 전체 구조부터

AI 채팅은 아주 단순하게 보면 이렇습니다.

사용자 → 채팅창 → 우리 서버 → AI → 답변

여기서 개발할 때는 몇 가지 용어가 등장합니다.

LLM은 GPT나 Claude처럼 사람의 말을 이해하고 답변을 만들어주는 AI 모델을 말합니다.

Backend는 사용자가 보는 화면 뒤에서 실제 작업을 처리하는 서버입니다.

Frontend는 반대로 사용자가 직접 보고 사용하는 채팅 화면입니다.

일단 이 세 가지만 알면 전체 구조는 어렵지 않습니다.

사용자 → Frontend → Backend → LLM

사용자가 채팅창에 질문을 입력하면 우리 서버가 그 질문을 AI에게 전달하고, AI가 만든 답변을 다시 채팅창에 보여주는 구조입니다.

채팅 화면은 assistant-ui로 만들었습니다

채팅창 화면을 처음부터 직접 만들지는 않았습니다.

assistant-ui라는 AI 채팅 화면을 만들기 위한 라이브러리를 사용했습니다.

쉽게 말하면 GPT 같은 채팅창을 만들 때 필요한 기본 부품들을 미리 만들어 놓은 도구입니다.

메시지 입력창, 내가 보낸 메시지, AI가 보낸 메시지, 답변 생성 중 표시 같은 것들을 처음부터 하나씩 만들 필요가 없습니다.

assistant-ui 기반으로 만들어달라고 AI에게 시키니 화면 자체는 생각보다 금방 나왔습니다.

여기까지는 쉬웠습니다.

AI 연결은 OpenLLMs를 사용했습니다

이제 채팅창에서 실제 AI에게 질문을 보내야 합니다.

보통은 OpenAI나 Anthropic 같은 회사에서 제공하는 API를 연결합니다.

API는 어렵게 생각할 필요 없이 프로그램끼리 서로 요청하고 답을 주고받는 통로 정도로 생각하면 됩니다.

이번에는 제가 만들어둔 OpenLLMs를 사용했습니다.

제가 결제해서 쓰고 있는 Claude나 Codex 같은 구독을 OpenAI 호환 방식으로 호출할 수 있게 만든 도구입니다.

그래서 구조는 이렇게 만들었습니다.

사용자 → assistant-ui → 우리 서버 → OpenLLMs → AI

질문도 잘 넘어갔고 답변도 잘 돌아왔습니다.

여기까지 보고는 거의 끝났다고 생각했습니다.

그런데 실제로 채팅을 해보니 뭔가 이상했습니다.

답변은 오는데 GPT 같지가 않았습니다

AI가 답변을 만드는 데 10초가 걸린다고 해보겠습니다.

처음 만든 방식에서는 10초 동안 화면에 아무것도 안 나옵니다.

그리고 답변이 전부 완성되면 갑자기 긴 글이 한꺼번에 나타납니다.

질문 → 기다림... → 기다림... → 답변 전체 등장

기능적으로는 정상입니다.

그런데 우리가 평소 사용하는 GPT나 Claude는 이렇지 않습니다.

질문을 하면 AI가 답변을 만드는 동시에 내용이 조금씩 화면에 올라옵니다.

여기서 스트리밍이라는 게 필요했습니다.

스트리밍이 뭘까?

스트리밍이라고 하면 어렵게 들리는데 개념은 간단합니다.

답변이 전부 완성될 때까지 기다리지 않고 만들어지는 대로 바로바로 보내주는 방식입니다.

유튜브를 생각하면 조금 쉽습니다.

영상 파일 전체를 다운로드한 다음 재생하는 게 아니라 데이터를 받으면서 바로 영상을 재생합니다.

AI 채팅도 비슷합니다.

AI가 긴 답변을 전부 만든 다음 보내는 게 아니라 생성되는 내용을 작은 조각으로 계속 보내줍니다.

이 작은 데이터 조각을 개발할 때 흔히 chunk라고 부릅니다.

AI → 조각 → 조각 → 조각 → 조각 → 완료

채팅창에서는 조각이 들어올 때마다 기존 답변 뒤에 계속 붙입니다.

그래서 우리 눈에는 AI가 실시간으로 글을 쓰고 있는 것처럼 보입니다.

이제 한 가지 문제가 남습니다.

AI에서 받은 이 조각들을 우리 서버에서 사용자의 채팅창까지 어떻게 계속 보내줄까?

여기서 SSE와 WebSocket이라는 기술이 등장합니다.

SSE와 WebSocket은 뭐가 다를까?

둘 다 서버와 채팅창 사이에서 데이터를 실시간으로 주고받을 때 사용하는 기술입니다.

처음에는 저도 둘 다 비슷해 보여서 뭘 써야 하나 싶었습니다.

직접 구현하면서 이해한 차이는 의외로 단순했습니다.

SSE는 서버가 계속 보내주는 방식

SSE는 Server-Sent Events의 약자입니다.

이름 그대로 서버가 사용자 화면으로 데이터를 계속 보내주는 방식입니다.

AI 채팅에 대입하면 쉽습니다.

사용자 → 질문 → 서버

서버 → 답변 조각 → 사용자

서버 → 답변 조각 → 사용자

서버 → 답변 조각 → 사용자

서버 → 완료 → 사용자

사용자는 질문을 보내고 기다립니다.

AI가 답변을 만들기 시작하면 서버가 그 내용을 받는 즉시 채팅창으로 계속 보내줍니다.

일반적인 AI 채팅과 꽤 잘 맞는 구조입니다.

WebSocket은 서로 계속 대화하는 방식

WebSocket은 조금 다릅니다.

서버와 사용자 사이에 통신 연결을 계속 열어두고 양쪽에서 자유롭게 메시지를 주고받을 수 있습니다.

Client ↔ Server

예를 들어 단순히 질문에 답하는 AI가 아니라 직접 여러 가지 일을 하는 AI 에이전트를 만든다고 해보겠습니다.

AI : 인터넷에서 자료를 찾고 있습니다.

AI : 파일을 찾았습니다.

AI : 이 작업을 실행해도 될까요?

사용자 : 실행해.

AI : 실행하고 있습니다.

사용자 : 잠깐, 멈춰.

AI : 작업을 중단했습니다.

이렇게 AI가 일을 하는 도중에도 사용자가 승인하거나 중단하거나 새로운 명령을 계속 보내야 한다면 양방향 통신이 중요해집니다.

이런 상황에서는 WebSocket이 편해집니다.

둘의 차이를 간단히 정리하면

구분SSEWebSocket
쉽게 말하면서버가 계속 보내줌서로 계속 주고받음
통신 방향서버 → 사용자 중심사용자 ↔ 서버
구현비교적 단순상대적으로 복잡
AI 답변 실시간 표시잘 맞음가능
작업 중 승인이나 중단다른 요청과 함께 처리처리하기 편함
일반적인 AI 채팅잘 맞음굳이 필요 없을 수도
복잡한 AI 에이전트상황에 따라 가능유리한 경우가 많음

그래서 저는 SSE로 만들었습니다

이번에 제가 만들던 건 비교적 단순한 채팅이었습니다.

사용자가 질문한다 → AI가 답변한다 → 만들어지는 답변을 바로 보여준다.

이게 전부였습니다.

AI가 일을 하다가 저한테 승인을 요청할 일도 없고, 작업 중간에 제가 멈추거나 새로운 명령을 보낼 필요도 없었습니다.

그래서 굳이 WebSocket까지 사용할 필요가 없다고 판단했습니다.

SSE로 구현했습니다.

실제로는 이렇게 움직입니다

전체 구조를 다시 보면 이렇습니다.

사용자 → 채팅창 → 우리 서버 → OpenLLMs → AI

여기까지가 질문을 보내는 과정입니다.

AI가 답변을 시작하면 반대로 돌아옵니다.

AI → 우리 서버 → SSE → 채팅창 → 사용자

중요한 건 AI의 답변이 모두 끝날 때까지 기다리지 않는다는 겁니다.

AI → 답변 조각 → 서버 → SSE → 화면

AI → 답변 조각 → 서버 → SSE → 화면

AI → 답변 조각 → 서버 → SSE → 화면

이걸 답변이 끝날 때까지 반복합니다.

그래서 사용자 눈에는 GPT나 Claude처럼 답변이 실시간으로 올라오는 것처럼 보이게 됩니다.

직접 만들어보고 나니

처음에는 정말 단순하게 생각했습니다.

채팅창 + AI 연결 = AI 채팅

크게 보면 틀린 말은 아닙니다.

그런데 직접 만들어보니 그 사이에서 데이터가 어떻게 오가는지도 꽤 중요한 부분이었습니다.

특히 GPT나 Claude에서 매일 아무 생각 없이 보고 있던 실시간 답변이 그냥 나오는 게 아니더군요.

이번에 직접 붙여보고 나니 기준은 오히려 간단해졌습니다.

일반적인 AI 채팅처럼 질문하고 답변을 실시간으로 보여주는 정도라면 SSE부터 생각해볼 수 있습니다.

반대로 AI가 여러 작업을 수행하고, 그 중간에 사용자가 승인하고 멈추고 다시 명령하는 일이 많아진다면 WebSocket을 고려할 수 있습니다.

어느 기술이 더 좋다기보다 내가 만들려는 기능에 필요한 만큼 선택하면 되는 것 같습니다.

저는 이번에는 SSE면 충분했습니다.

조금 더 자세한 내용은 블로그에 정리했습니다.

https://goodtek.xyz/blog/ai-caetingcangeul-jigjeob-mandeuleoboni-jinjja-jungyohan-geon-seuteurimingieossda/