요즘 들어 LLM 기반 챗봇이나 생성형 앱을 만들다 보면, 어느 순간 이런 고민이 찾아옵니다.
“모델이 매번 모든 문서를 기억하지 못하는데, 어떻게 나만의 데이터를 참조하게 만들 수 있을까?”
바로 이 문제를 해결하는 대표적인 방법이 RAG(Retrieval-Augmented Generation)입니다. 말은 복잡해 보여도, 핵심은 간단해요. 모델이 문서를 “외우는” 대신, 그때그때 필요한 정보를 찾아서 함께 전달해주는 방식이죠. 마치 친구에게 질문할 때, “잠깐만, 이거 전에 봤던 문서 있어” 하며 파일을 꺼내 보여주는 것처럼요.
이번 글에서는 Flutter 앱을 Supabase 백엔드와 연결해, OpenAI API를 사용하는 기본적인 환경을 전제로 RAG를 어떻게 적용할 수 있을지 소개하려고 합니다. 특히 초보 개발자도 구현해볼 수 있는 수준의 구조부터, 실제로 토큰을 아끼며 효율을 높이는 실전 전략까지, 단계별로 하나씩 풀어보겠습니다.
RAG, 기억을 못하는 모델에게 책장을 만들어주는 기술
RAG는 Retrieval-Augmented Generation의 줄임말이에요. 이름만 보면 뭔가 복잡한 인공지능 기술 같지만, 사실 개념 자체는 꽤 직관적이에요. 예를 들어 설명해볼게요.
우리가 ChatGPT 같은 LLM을 사용할 때, 모델은 기본적으로 세상에 대해 광범위하게 훈련돼 있어요. 하지만 “우리 회사 내부 문서”나 “특정 사용자의 이력” 같은 정보는 알지 못해요. 이럴 땐 어떻게 해야 할까요?
가장 쉬운 방법은 질문을 보낼 때, 함께 참고할 문서나 데이터도 같이 보내주는 것이에요. 예를 들어, “이 고객이 지난주에 뭐라고 했는지 기억해?”라는 질문을 할 때, 고객의 이전 대화 내용을 같이 첨부해주는 거죠. 이렇게 모델이 몰라도 되는 데이터를 매번 찾아서 곁들여 주는 기술이 바로 RAG예요.
좀 더 구체적으로 말하면, RAG는 두 단계로 작동해요.
- Retrieval(검색): 먼저 DB나 벡터 DB에서 관련 문서를 찾아요. “이 질문에 도움이 될 만한 정보는 뭐지?“라고 검색하는 단계죠.
- Augmented Generation(보강 생성): 찾은 문서를 프롬프트에 끼워 넣고, 그걸 바탕으로 답변을 생성해요. 말 그대로 “보강해서 답변을 생성”하는 겁니다.
이런 구조는 LLM의 단점을 보완해줘요. 모델이 모든 걸 다 외우지 않아도 되고, 중요한 데이터는 나중에라도 업데이트할 수 있으니까요.
초보자 입장에서는 모델을 똑똑하게 만드는 것보다, 모델이 ‘참고할 수 있는 지식’을 잘 꺼내주는 게 훨씬 현실적인 방법이라는 점을 기억하면 좋아요. RAG는, 말하자면 GPT 같은 모델에게 책장을 만들어주고, 거기서 필요한 책을 꺼내 읽게 해주는 기술이라고 볼 수 있어요.
Flutter 앱에서 Supabase와 OpenAI로 RAG 구현하기
이제 RAG의 개념을 알았다면, 실제 Flutter 프로젝트에서 어떻게 구현할 수 있는지를 살펴볼 차례예요. 특히 Supabase와 OpenAI를 함께 활용하면 가볍고도 강력한 RAG 구조를 만들 수 있습니다.
먼저 전체 흐름을 간단히 요약하면 다음과 같아요:
(1) 사용자가 질문
(2) Supabase에서 관련 데이터 검색 (벡터 검색)
(3) 해당 결과와 질문을 함께 OpenAI에 전달
(4) 모델이 답변 생성
(5) Flutter 앱에 응답 표시
이제 하나씩 차근차근 살펴볼게요.
1. Supabase에 벡터 검색 준비하기
RAG의 핵심은 정보 검색이죠. 이를 위해 Supabase에 벡터 기반 검색 환경을 세팅합니다. 일반적으로는 pgvector 확장을 활성화하고, 검색 대상이 될 문서나 데이터를 임베딩 벡터로 변환해 저장해둡니다. 이 임베딩은 OpenAI Embedding API로 생성할 수 있어요.
예를 들어, 앱에 제품 매뉴얼 데이터를 넣고 싶다면
- 각 문단을 나눠 텍스트 벡터화
- content와 embedding을 포함하는 테이블에 저장
- 쿼리 시에는 사용자의 질문도 벡터화한 뒤, 유사도 높은 문서들을 가져오면 됩니다.
2. Edge Function으로 검색 + OpenAI 호출 통합하기
Supabase의 Edge Function을 활용하면, 검색과 생성 단계를 하나의 API로 묶을 수 있어요. 사용자가 앱에서 질문을 입력하면 이 Edge Function을 호출하고, 그 안에서 다음 과정을 처리합니다:
- 질문 → 벡터로 변환
- 벡터 기반으로 관련 문서 검색 (예: 상위 5개)
- 이 문서들과 원 질문을 함께 OpenAI에 프롬프트로 전달
- 생성된 응답을 다시 Flutter 앱으로 전달
이렇게 하면 프런트엔드는 매우 간단해지고, 데이터는 Supabase에서 안전하게 관리할 수 있어요.
3. Flutter에서 질문하고 답변받기
이제 Flutter 앱에서는 Edge Function을 호출하기만 하면 됩니다. 예를 들면 dio나 http 패키지를 사용해서 사용자의 질문을 POST 방식으로 전송하고, 응답으로 생성된 답변을 받아 화면에 표시하면 되죠.
사용자는 자연스럽게 앱을 사용하고, 내부적으로는 RAG 로직이 효율적으로 작동하는 구조가 됩니다.
실무에서 고려해야 할 데이터 최적화와 압축 전략
실제 RAG를 앱에 적용하다 보면, 단순한 기능 구현을 넘어서 성능, 비용, 데이터 규모라는 현실적인 문제에 부딪히게 돼요. 특히 OpenAI API를 사용할 때는 토큰 비용이 누적되기 때문에, 수많은 문서 데이터를 효율적으로 다루는 전략이 필요합니다.
1. 불필요한 정보 제거, “압축”의 시작
우선 가장 기본은 정보의 절제예요. 긴 텍스트일수록 임베딩 시 많은 토큰을 차지하고, 모델에 전달할 때도 비용이 올라가죠. 그래서 먼저 해야 할 일은,
- 문장에서 불필요한 접속사, 수식어, 예외 케이스 설명 등을 제거
- 데이터 수집 단계에서부터 요약 전처리를 진행
즉, RAG에서 중요한 것은 “많이”가 아니라 “핵심만” 전달하는 거예요.
2. 요약과 chunking 전략
데이터가 많을 때는 요약(summary)과 chunking(문서 쪼개기)을 적절히 병행해야 합니다.
- 요약은 사람이 쓰듯 자연스럽게: 문서를 단순히 줄이기보단 요약 목적의 별도 프롬프트를 통해, 정보의 핵심을 뽑아내야 합니다.
- Chunking은 의미 단위로: 너무 짧게 자르면 맥락이 사라지고, 너무 길면 검색 정확도가 떨어져요. 보통 200~500자 단위로, 문단 기준으로 쪼개는 게 좋습니다.
Supabase에 저장할 때도 chunk된 텍스트마다 별도의 row로 나누어, 벡터화해서 저장하는 것이 일반적입니다.
3. 캐싱 및 우선순위 저장 전략
모든 질문마다 OpenAI를 부르면 속도도 느리고 비용도 큽니다. 그래서 **자주 묻는 질문(FAQ)**이나 과거 사용자 로그 분석을 통해 파악된 빈출 쿼리에 대해서는:
- 미리 답변을 생성해 Supabase에 캐시 저장
- 앱에서는 검색 결과가 있으면 우선 캐시된 응답을 제공하고, 없을 때만 OpenAI 호출
이 방식은 특히 스케일이 커졌을 때 매우 효과적입니다.
4. 시간 정보를 고려한 DB 설계
또 하나 실무에서 중요한 포인트는, 데이터를 저장하고 불러올 때 시간 순서를 보장하는 구조예요. 예를 들어 사용자가 한국에서 앱을 쓰다가 미국으로 갔을 때, 작성한 글의 순서가 뒤죽박죽되면 안 되겠죠.
그래서 Supabase에 기록할 때는 항상:
- DB 서버 기준 UTC 시간으로 고정 저장
- Flutter에서는 사용자 디바이스의 TimeZone을 인식해 출력 시 변환
이렇게 하면 언제 어디서든 시간 순서가 뒤틀리지 않게 됩니다. 특히 벡터 검색 시 최신 데이터를 우선순위로 부여하는 로직과 함께 쓰면 더 강력해요.
이처럼 실무에서 RAG를 제대로 활용하려면, 단순히 검색과 생성만으로는 부족하고 데이터 구조, 요약 로직, 캐싱 전략, 시간 정합성 유지 등 다양한 관점을 함께 고려해야 합니다. Flutter + Supabase + OpenAI 조합은 이를 빠르고 유연하게 구현할 수 있는 훌륭한 조합이지만, 장기적으로는 이런 실무적 고민을 함께 풀어가는 설계가 중요해요.
마치며...
RAG는 단순한 기술 도입이 아니라, “앱이 어떤 질문을 받아 어떤 문맥을 바탕으로 어떤 답을 줄 것인가”에 대한 사고의 전환이에요. 단순히 OpenAI API에 물어보는 것만으로는 실용적인 성능이나 비용 측면에서 지속 가능하지 않기 때문에, 자신의 앱 맥락에 맞춘 지식 관리 전략이 반드시 필요하죠.
Flutter + Supabase + OpenAI는 그 자체로도 강력한 조합이지만, 여기에 RAG 기반 설계, 요약·압축 전략, 시간 정합성과 벡터 DB 설계, 그리고 적절한 캐싱이 더해지면, MVP 수준을 넘어 실사용 가능한 서비스로 확장할 수 있습니다.
결국 핵심은 “무엇을 줄 것인가”보다 “어떻게 줄 것인가”입니다. RAG는 LLM을 효과적으로 통제할 수 있는 실용적 도구이자 전략이며, 그 가능성은 앞으로 더 커질 거예요. 지금 시작하는 이 한 걸음이, 나중에 당신의 앱을 지식 기반 AI 서비스로 만들어줄 수 있을 거예요.
'IT' 카테고리의 다른 글
바이브 코딩으로 앱 개발 하다가 겪은 병목들 (0) | 2025.06.13 |
---|---|
시간대 혼동 없는 DB 설계 (2) | 2025.06.11 |
플러터, 텍스트 에디터가 필요하다면? (2) | 2025.06.10 |
Cursor 최근 업데이트에 대해 알아보자 (2) | 2025.06.09 |
앱 서비스에 LLM 도입하기 (4) | 2025.06.02 |