튼튼발자 개발 성장기🏋️

코드 1도 모르는 비개발자도 AI 비서 만드는 방법 본문

AI/LLM 서비스 개발

코드 1도 모르는 비개발자도 AI 비서 만드는 방법

시뻘건 튼튼발자 2026. 5. 21. 10:30
반응형
코드 1도 모르는 비개발자가 RAG 챗봇 만들기 — 5시간 분투기

코드 1도 모르는 비개발자가 회사 문서로 답하는 AI 만들기

ChatGPT는 우리 회사 정책을 모른다. 그런데 알게 만들 수 있다면? 이 글은 코드 한 줄 없이 회사 문서를 학습시킨 Discord AI 비서를 만들면서 겪은 모든 과정과 시행착오의 기록이다.

PROLOGUE

"우리 회사 휴가 정책 알려줘"라고 AI한테 묻고 싶었다

나는 개발자다. 언젠가 회사 내부 정책이나 내가 담당하는 서비스의 도메인 지식 또는 서비스 정책을 한 번에 알아낼 수 있는 방법을 생각했다.

"우리 팀 OKR 진행 상황 정리해줘"라고 물으면 AI는 모른다. "지난주 회의록에서 결정된 사항만 추려줘"도 마찬가지. 우리 회사의 모든 지식은 Google Drive에 흩어져 있는데, AI는 그걸 모른다.

그래서 만들기로 했다. 회사 문서를 학습시킨 챗봇을. 이걸 부르는 기술 용어가 RAG (Retrieval-Augmented Generation)다. 이번엔 지난번 Butler 처럼 Langchain과 같은 프레임워크를 사용해서 개발하진 않았다. 그래서 오히려 개발자인 나에겐 쉽지 않은 일이었다. 그치만 실제로 만들어보니 충분히 할 수 있는 일이었다. 그만큼, 함정(?)이 좀 많았다. 그 함정들까지 모두 포함해서 정리한다.

비개발자가 만들 수 있느냐가 아니라, [어떻게 안 막히고 끝까지 가느냐]가 핵심
CHAPTER ONE

RAG가 뭔지 30초 안에 이해하기

딱 시험 보는 학생으로 비유한다.

일반 AI: 책 없이 보는 시험

자기가 외운 것만 답할 수 있다. 모르는 건 그럴듯하게 지어낸다 (이걸 "hallucination"이라고 한다). "우리 회사 출산휴가가 며칠이지?" 같은 질문엔 무용지물이다.

RAG AI: 오픈북 시험

질문이 오면 먼저 책(=회사 문서)에서 관련 페이지를 찾아본 다음, 그 내용을 참고해서 답한다. 정확하고, 출처도 댈 수 있다.

아하 모먼트

RAG = "AI에게 우리 회사 문서를 검색할 수 있는 능력을 주는 것". 그게 전부다. 기술적으로 보면 더 복잡하지만, 본질은 이것이다.

그럼 왜 그냥 ChatGPT에 문서 붙여넣기는 안 되는가

가능하긴 한데, 문서가 1~2개일 때만 그렇다. 문서가 100개, 1,000개가 되면:

  • 매번 모든 문서를 붙여넣을 수 없다 (입력 길이 한계)
  • 관련 없는 문서까지 보내면 AI가 산만해져서 답변 품질이 떨어진다
  • 비용도 폭증한다 (모든 문서 = 모든 토큰 = 모든 청구서)

그래서 RAG는 "질문과 관련된 문서 조각만 찾아서" AI에게 보낸다. 마치 사서가 필요한 책 페이지만 찾아주는 것처럼.

CHAPTER TWO

우리가 만들 것

최종 결과물은 이렇다:

  1. Google Drive 폴더에 회사 문서를 모아둔다. 워드, PDF, Google Docs, 마크다운 등 다 된다.
  2. 한 번 클릭하면 AI가 모든 문서를 "학습"한다. 정확히는 검색 가능한 형태로 변환해서 벡터DB라는 데이터베이스에 저장한다.
  3. Discord에서 봇한테 질문하면 답해준다. 예: 출산휴가 며칠이야? → "회사 정책에 따르면 90일이며..."
  4. 문서를 수정하면 자동으로 봇도 재학습한다. 정책이 바뀌면 AI 답변도 자동으로 최신화된다.
실제 사용 예시

나는 내가 담당하는 서비스 중에 가장 복잡도가 높은 도메인 지식을 다 넣어놨다. 내가 구글 드라이브보다 봇에게 묻는 빈도가 늘었다. "그거 어디 있더라" 하고 헤매는 시간이 사라졌다.

CHAPTER THREE

필요한 도구 6가지

각자 역할이 분명한 도구 6개를 조합한다. 모두 무료 플랜으로 시작할 수 있다.

n8n

지휘자

모든 도구를 연결해주는 노코드 자동화 플랫폼. 여기서 마우스로 블록을 연결하며 흐름을 만든다.

Google Drive

도서관

원본 문서가 사는 곳. 이미 쓰고 있을 가능성이 높다. 새로 만들 거 없다.

Supabase

AI용 책장

AI가 빠르게 검색할 수 있도록 문서를 "벡터" 형태로 보관하는 데이터베이스. 무료 플랜으로 충분.

Cohere

번역가

문서를 AI가 이해하는 "숫자 표현(벡터)"으로 바꿔주는 임베딩 모델. 한국어 성능이 강해서 선택.

LLM (GPT/Kimi 등)

사서

실제로 답변을 생성하는 똑똑한 AI. OpenAI, Anthropic, Moonshot 등 원하는 거 선택.

Discord

창구

사용자가 봇에게 질문하는 채팅 인터페이스. 슬랙이나 Telegram으로 바꿔도 된다.

왜 이 조합인가

"비개발자가 무료로 시작해서 운영할 수 있는 가장 단순한 조합"을 기준으로 골랐다. 각 도구에 다 대안이 있지만, 처음에는 이 조합을 그대로 따라하는 것을 권한다. 익숙해진 후 본인 필요에 맞게 바꾸면 된다.

예상 비용

월 0원 ~ 만 원 정도로 시작 가능하다. n8n 셀프호스팅(무료), Supabase 무료 플랜, Cohere 무료 크레딧, Discord 무료. LLM API만 사용량에 따라 과금된다.

CHAPTER FOUR

전체 아키텍처 한눈에 보기

도구 6개가 어떻게 연결되는지 그림으로 정리한다. 두 가지 흐름이 동시에 작동한다.

FLOW A · 문서 학습 (인덱싱) Google Drive 원본 문서 n8n 다운로드 → 조각 분할 Cohere 벡터로 변환 Supabase 벡터 저장 FLOW B · 사용자 질문 (검색 + 답변) Discord "?출산휴가 며칠이야?" n8n AI Agent (질문 분석) Supabase 관련 문서 조각 검색 LLM 검색결과 + 질문 → 답변 생성 답변 Discord로 전송 두 흐름은 독립적으로 작동한다. 학습은 한 번 또는 문서 변경 시, 질문은 사용자가 던질 때마다.

핵심을 다시 정리하면:

  • 학습 흐름(빨강): 문서를 가져와 → 작은 조각으로 나누고 → 숫자(벡터)로 바꾼 다음 → DB에 저장
  • 질문 흐름(초록): 질문이 오면 → 비슷한 의미의 문서 조각을 찾고 → 그걸 LLM에게 보여주며 답변 요청
CHAPTER FIVE

워크플로우 ①: 문서 학습시키기

처음 만들 워크플로우다. Google Drive의 문서를 Supabase에 학습시키는 일을 한다.

사전 준비 — Supabase에 "책장" 만들기

문서 벡터를 저장할 데이터베이스 테이블을 만들어야 한다. Supabase 대시보드에서 SQL Editor를 열고 다음을 실행한다. 이게 코딩이라고 생각할 수도 있는데, 음... 그냥 Copy & Paste라고 생각하자.

-- pgvector 확장 켜기 (벡터 검색을 가능하게 해주는 기능)
create extension if not exists vector;

-- 벡터를 저장할 테이블
create table documents (
  id        bigserial primary key,
  content   text,
  metadata  jsonb,
  embedding vector(1024)  -- Cohere multilingual은 1024 차원
);

-- 유사도 검색용 함수 (n8n이 이걸 호출함)
create or replace function match_documents (
  query_embedding vector(1024),
  match_count int default null,
  filter jsonb default '{}'
) returns table (id bigint, content text, metadata jsonb, similarity float)
language plpgsql as $$
#variable_conflict use_column
begin
  return query
  select id, content, metadata, 1 - (documents.embedding <=> query_embedding) as similarity
  from documents
  where metadata @> filter
  order by documents.embedding <=> query_embedding
  limit match_count;
end; $$;
함정 #1

벡터 차원 숫자가 임베딩 모델과 정확히 일치해야 한다. Cohere multilingual은 1024, OpenAI text-embedding-3-small은 1536, OpenAI text-embedding-3-large는 3072다. 다르면 나중에 "expected 1536 dimensions, not 1024" 에러가 난다 (내가 겪었다).

n8n에서 워크플로우 만들기

여기부턴 마우스 작업이다. n8n 화면에서 노드(블록) 8개를 연결한다:

  1. Manual Trigger

    "테스트 실행" 버튼을 만들어주는 시작점.

  2. Google Drive → Search Files

    모든 문서를 검색. Options → Fields에서 mimeType을 꼭 추가한다 (기본은 id, name만 줘서 다음 단계로 넘어갈 수 없다.).

  3. Filter Supported Mime Types

    이미지/동영상 같은 임베딩 불가능한 파일을 걸러낸다. PDF, 워드, 구글 문서 등만 통과시킨다.

  4. Loop Over Items

    큰 파일에서 메모리 문제 안 나게 파일을 한 번에 하나씩 처리한다.

  5. Google Drive → Download

    실제 파일 내용 다운로드. Google Docs/Slides는 자동으로 text로 변환.

  6. Supabase Vector Store (Insert 모드)

    벡터로 변환된 문서를 DB에 저장. 옆에 3개의 보조 노드를 붙인다: 데이터 로더, 텍스트 분할기, Cohere 임베딩.

  7. Recursive Character Text Splitter

    긴 문서를 1,000자 정도의 작은 청크로 자른다. 청크가 너무 크면 검색 정밀도 떨어지고, 너무 작으면 맥락 손실.

  8. Cohere Embeddings (embed-multilingual-v3.0)

    각 청크를 1024차원 벡터로 변환.

이게 끝이다. Manual Trigger 버튼을 누르면 Drive의 모든 문서가 Supabase로 학습된다. 문서 100개면 5~10분 정도 걸린다.

메타데이터 꿀팁

Default Data Loader 설정 시 file_id, file_name, web_view_link을 metadata로 함께 저장한다. 나중에 답변할 때 "이 정보는 ◯◯ 문서에서 가져왔다" 같은 출처 표시가 가능해지고, 문서가 수정됐을 때 정확히 그것만 골라 삭제할 수도 있다.

· · ·
CHAPTER SIX

워크플로우 ②: Discord 챗봇 연결하기

이제 사용자가 질문을 던지면 학습된 문서로 답하는 봇을 만든다. 솔직히 가장 험난했던 부분이다. Discord 봇 설정이 함정 투성이라 차근차근 짚는다.

1단계 — Discord 봇 만들기

Discord Developer Portal에서 새 앱 생성. Bot 메뉴에서 토큰 발급. 이때 반드시 켜야 하는 게 있다:

3개의 Privileged Intent

Bot 메뉴 → Privileged Gateway Intents에서 세 개 모두 ON:

  • Presence Intent
  • Server Members Intent
  • Message Content Intent

한 개라도 빠지면 봇이 Discord에 로그인 자체를 못 한다. 여기서 2시간을 날렸다. 일반적으로 "Message Content만 켜면 된다"고 안내되어 있는데, 우리가 쓸 n8n 커뮤니티 노드는 코드 내부에서 Presence도 요구한다.

2단계 — 봇을 서버에 초대 (Scope 두 개 필수)

OAuth2 → URL Generator에서:

  • Scopes: bot + applications.commands (둘 다 체크)
  • Bot Permissions: 채널 보기, 메시지 보내기, 메시지 기록 보기 (이 정도면 충분)

생성된 URL을 브라우저로 열어 본인 서버에 초대.

함정 #2

applications.commands scope을 빼먹으면 봇은 서버에 들어오는데 메시지를 못 받는 묘한 상태가 된다. "credentials error"라는 모호한 에러만 계속 뜬다. Scope 두 개 다 체크 필수.

3단계 — n8n에서 워크플로우 만들기

n8n에 Discord 커뮤니티 노드(n8n-nodes-discord)를 설치해야 한다. Docker 환경에서는 peer dependency 이슈가 있어서 Dockerfile에 다음을 추가하는 게 안전하다:

RUN cd /home/node/.n8n/nodes && \
    npm install n8n-nodes-discord @discordjs/rest discord.js

워크플로우 구조:

[Discord Trigger]  ← 사용자 메시지 수신 ("?" prefix로 시작하는 것만)
       ↓
[AI Agent]  ← 여기가 두뇌
   ├── OpenAI/Kimi Chat Model     (실제 답변 생성)
   ├── Window Buffer Memory        (대화 맥락 기억)
   └── Supabase Vector Store as Tool
            └── Cohere Embeddings  (질문도 같은 모델로 임베딩!)
       ↓
[Discord Webhook]  ← 답변을 채널로 전송

4단계 — 트리거 prefix 설정

채널의 모든 메시지에 봇이 반응하면 시끄럽고 비용도 폭증한다. Discord Trigger의 Value 필드에 ?를 넣어서, 사용자가 ?로 시작하는 메시지만 봇이 반응하도록 한다.

다만 이렇게 하면 봇에게 전달되는 메시지 앞에 ?가 그대로 붙는다. AI Agent의 Text 입력을 다음과 같이 바꾸면 깔끔하게 떼고 전달된다:

{{ $json.content.replace(/^\?\s*/, '').trim() }}

5단계 — 답변 전송은 Webhook으로

함정 #3

커뮤니티 노드의 메시지 수신은 잘 되는데 송신이 안 된다. 노드가 "성공"이라고 표시해놓고 실제론 메시지를 안 보낸다. (n8n 노드 자체의 버그) 그래서 송신만 Discord 공식 Webhook으로 분리한다:

① Discord 채널 설정 → 연동 → 웹후크 만들기 → URL 복사. ② n8n 기본 Discord 노드에 그 URL 붙여넣으면 끝. 안정적이고 빠르다.

· · ·
CHAPTER SEVEN

워크플로우 ③: 자동 동기화

여기까지 완성하면 RAG 챗봇이 작동한다. 그런데 한 가지 문제가 남는다.

문서가 수정되었는데, AI는 옛날 버전으로 답한다.

인덱싱은 일종의 스냅샷이라 원본이 바뀌면 동기화가 깨진다. 사용자가 옛 정보를 받고 잘못된 의사결정을 할 수도 있다. 운영에서는 치명적이다.

해결 패턴: 삭제 후 재삽입

다행히 문서를 처음 학습시킬 때 메타데이터에 file_id를 저장해뒀다. 이 ID로 정확히 그 문서의 모든 청크만 골라서 삭제하고, 새 버전을 다시 임베딩하면 된다.

[Google Drive Trigger]   ← 폴더의 파일 수정 감지 (1~3분 주기 폴링)
       ↓
[Filter Supported Mime Types]
       ↓
[HTTP Request: DELETE]   ← 옛 청크 모두 삭제
   (URL에 metadata->>file_id=eq.{{ 파일ID }} 쿼리)
       ↓
[Download Updated File]  ← 새 버전 다운로드
       ↓
[Supabase Vector Store (Re-Insert)]  ← 다시 임베딩 + 저장
   ├── Cohere Embeddings
   ├── Data Loader
   └── Text Splitter
함정 #4

n8n의 Supabase 노드는 JSONB 필터(metadata->>file_id)를 제대로 못 다룬다. 그냥 두면 "DELETE requires WHERE clause" 에러로 막힌다. 이 노드만 HTTP Request로 대체해서 Supabase REST API를 직접 호출하는 게 가장 안정적이다.

이 워크플로우를 Active 상태로 켜두면, Drive에서 누군가 문서를 수정한 순간부터 1~3분 이내에 자동으로 봇의 지식이 갱신된다.

비슷하게:

  • 새 파일 자동 학습: 트리거 이벤트를 fileCreated로 바꾼 워크플로우 하나 더 복제
  • 삭제된 파일 정리: Schedule Trigger로 일주일에 한 번 cleanup 워크플로우 실행 (Drive에 없는 file_id는 Supabase에서 제거)
CHAPTER EIGHT

내가 빠진 함정들

5시간 분투기 중 가장 오래 헤맨 부분들. 같은 경로를 따라간다면 다 만나게 될 것이다. 미리 알아두면 시간을 아낄 수 있다.

임베딩 모델 차원 ↔ DB 차원 불일치

"expected 1536 dimensions, not 1024" 에러. Cohere 쓰면서 OpenAI 차원으로 테이블 만들면 발생. 모델 바꾸면 SQL 테이블도 같이 바꿔야 한다.

Privileged Intent 한 개만 켜기

일반 가이드들은 "Message Content만 켜면 된다"고 적혀있지만, n8n-nodes-discord는 내부적으로 Presence와 Server Members도 요구한다. 세 개 다 켜는 게 정답.

OAuth scope에 applications.commands 누락

봇을 초대할 때 bot scope만 체크하면 "credentials error"가 나는데 원인이 안 보인다. applications.commands까지 체크해야 슬래시 커맨드 등록이 성공.

Docker 볼륨이 Dockerfile 설치 내용을 가림

커뮤니티 노드를 Dockerfile에 박아 넣어도 컨테이너 실행 시 기존 볼륨이 그 경로를 덮어버린다. 처음 환경에서는 OK, 기존 볼륨에선 컨테이너 안에서 직접 npm install 해야 한다.

추론 모델에 temperature 설정

OpenAI o3, Kimi K2.5 같은 추론 모델은 temperature=1로 고정. 0.3 같은 값 넣으면 에러. 그냥 temperature 옵션 자체를 제거하면 해결.

Kimi thinking 모드 + AI Agent Tool 호출 비호환

"reasoning_content is missing" 에러. Kimi의 추론 모드는 멀티턴 tool 호출 시 추론 흔적을 보존해야 하는데 n8n이 지원 안 함. non-thinking 모델로 바꾸거나 OpenAI/Anthropic으로 교체.

그래서, 비개발자가 RAG를 만들 수 있느냐

그렇다. 만들 수 있다.

"코드 1도 모른다"는 조건도, 정확히 말하면 "코드를 작성하지는 않는다"는 뜻이지 "로그를 못 읽는다"거나 "에러를 무서워한다"는 의미는 아닐 것이다. 에러 메시지를 읽고, 한 번에 하나씩 바꿔보고, 막히면 검색해보는 능력 — 그건 개발자만의 것이 아니다.

이 글이 누군가의 첫 번째 RAG 구축을 5시간이 아니라 2시간으로 줄여줄 수 있다면, 그것으로 만족한다. AI시대에 같이 노 저어가자!

반응형