Tag

Automation

3 posts

AIAutomation

Text-to-SQL: "매출 지난달이랑 비교해줘" 한 마디로 BigQuery의 데이터 분석 및 리포트 작성

2026.06.04

TL;DR — SQL을 몰라도 된다. 자연어로 분석을 요청하면 AI가 SQL을 자동 생성하고, BigQuery에 직접 쿼리를 날려 결과를 돌려주는 Text-to-SQL 분석 시스템을 구축했다. 핵심은 Claude Projects + BigQuery MCP + 커스텀 Analytics Skill의 조합이다. 데이터가 쌓이면 질문이 생긴다. "이번 달 채널별 매출 비중이 어떻게 돼?" "지난 3개월 평균이랑 비교하면 어때?" "해외 환자 유입이 전년 대비 얼마나 늘었어?" 이 질문들의 공통점은 답이 데이터베이스 안에 있다는 것이다. 그런데 문제가 있었다. 데이터를 꺼내려면 SQL을 알아야 한다. 우리팀은 분석 요청이 생길 때마다 업무 리소스가 발생했다. 요청 → 전달 → 쿼리 작성 → 결과 전달의 사이클이 반복됐고, 특히 복잡한 분석을 요청하는 경우 쿼리 작성 시간이 길어졌다. ​우리팀은 더 게을러지기 위해 이 Claude Project를 만들었다. Text-to-SQL은 자연어(Natural Language) 질의를 SQL로 자동 변환하는 기술이다. NL2SQL이라고도 부른다. 개념 자체는 오래됐지만, LLM(대형 언어 모델)의 등장으로 실용적인 수준에 도달했다. 특히 테이블 스키마와 비즈니스 컨텍스트를 함께 제공하면, 모델이 도메인에 맞는 정확한 SQL을 생성할 수 있게 됐다. 이 방식을 선택한 이유는 단순하다. 별도 UI 개발 없이 구현 가능 기존 BigQuery 인프라를 그대로 활용 비개발자도 즉시 사용 가능 질문의 형태와 깊이에 제한이 없음 세 가지 컴포넌트가 핵심이다. MCP(Model Context Protocol)는 AI 모델이 외부 서비스와 직접 통신할 수 있게 해주는 프로토콜이다. BigQuery MCP를 연결하면 Claude가 SQL을 생성하는 것에 그치지 않고, BigQuery에 직접 쿼리를 실행하고 결과를 받아올 수 있다. 기존 Text-to-SQL의 한계는 "SQL을 생성해주면 사람이 직접 실행해야 한다"는 것이었다. MCP는 이 단계를 없앤다. 자연어 질의부터 분석 결과까지 중간에 사람이 개입하지 않아도 된다. Claude의 Skill은 특정 도메인에 대한 지식과 규칙을 패키징한 컨텍스트 파일이다. tu-analytics Skill에는 다음이 담겨 있다. 스키마 정보 (schema.md) 테이블 구조, 컬럼 설명, 데이터 타입. Claude가 올바른 SQL을 생성하려면 테이블이 어떻게 생겼는지 알아야 한다. 지표 정의 (tables.md / 분석 지침) 단순한 컬럼 설명을 넘어, 비즈니스 지표의 정확한 계산식을 정의한다. 예를 들어 "객단가"는 단순 평균이 아니라 특정 필터 조건을 만족하는 행만 포함해야 한다. 이런 규칙이 없으면 SQL은 실행되지만 결과가 틀린다. 쿼리 패턴 (querypatterns.md) 자주 사용하는 분석 패턴을 템플릿화했다. 영업일 기준 동기 비교, 국내/해외 분리 집계, 누적 영업일 필터링 등 반복되는 로직을 표준화해두면 매번 처음부터 만들지 않아도 된다. 특수 케이스 (specialcases.md) 데이터의 예외 상황과 주의사항. 예를 들어 특정 컬럼에 NULL이 들어오는 경우 어떻게 처리해야 하는지, 중복 제거는 어떤 기준으로 하는지 등이 명시돼 있다. 이 Skill이 없으면 Claude는 일반적인 SQL은 만들 수 있지만, 이 데이터베이스의 맥락에 맞는 정확한 SQL은 만들기 어렵다. Claude Projects는 특정 프로젝트에 대한 지식과 설정을 지속적으로 유지할 수 있는 환경이다. tu-analytics Skill을 프로젝트에 연결해두면, 매번 컨텍스트를 다시 설명하지 않아도 된다. 대화가 길어지거나 세션이 바뀌어도 테이블 구조, 지표 정의, 분석 원칙이 유지된다. 질의 예시: "5월 국내/해외 매출 전월 동기 대비 비교해줘" 내부 처리 과정: 1. Claude가 Skill을 참조해 관련 테이블과 컬럼을 파악 2. "전월 동기"의 의미를 영업일 기준으로 해석 (달력 날짜 기준이 아님) 3. 국내/해외 분리 집계 쿼리 생성 (nationalitytype 기준) 4. BigQuery MCP로 쿼리 실행 5. 결과 반환 → 증감 계산 + 시각화 + 인사이트 해석 핵심은 3번과 4번 사이에 사람이 없다는 것이다. 결과 리포트 예시 "매출이 얼마야?"라는 질문은 단순해 보이지만, 어떤 컬럼을 쓰느냐에 따라 숫자가 달라진다. 고객납부액인지, 매출귀속액인지, 세금 포함/제외인지. 이 정의가 Skill에 명확하게 박혀 있어야 일관된 결과가 나온다. 수식 하나하나를 명문화하는 작업이 시스템 구축보다 더 시간이 걸렸다. "전월 대비"를 단순 날짜 수로 비교하면 안 된다. 영업일 수가 다른 달을 날짜 기준으로 비교하면 오해를 부른다. 이 시스템은 영업일 기준 동기 비교를 표준으로 정의하고, 모든 기간 비교에 이 원칙을 적용한다. 당월 / 전월 동기 / 직전 3개월 평균 / 전년 동기 — 네 가지 비교 기준을 항상 함께 제공하는 것도 규칙으로 정해뒀다. 분석 결과가 매번 다른 형태로 나오면 보는 사람이 불편하다. 스코어카드 → 차트 → 표 요약 → 인사이트의 순서를 표준화하고, 증감 표기 방식(▲/▼, 색상 코드)도 규칙으로 정의했다. 완벽하지 않다. LLM 기반 SQL 생성은 복잡한 조건이 중첩될수록 오류 가능성이 높아진다. 이 때문에 중요한 분석은 결과를 검증하는 습관이 필요하다. 컨텍스트 품질이 전부다. Skill에 정의된 스키마와 지표가 부정확하면 SQL도 틀린다. "쓰레기가 들어가면 쓰레기가 나온다(GIGO)"는 여기서도 그대로 적용된다. 초기 Skill 작성과 지속적인 업데이트가 시스템 품질을 결정한다. MCP는 현재 사용자 단위 인증이다. BigQuery MCP는 개인 인증 기반으로 동작한다. 조직 전체가 동일한 연결을 공유하는 구조가 아니므로, 팀 단위 배포 시에는 이 점을 고려해야 한다. SQL을 모르는 팀원도 분석 질문을 직접 던질 수 있게 됐다. 분석 요청 → 결과 확인의 사이클이 대폭 줄었고, "물어보기 애매해서 참던" 질문들이 올라오기 시작했다. 데이터 민주화라는 말이 있다. 데이터를 다루는 기술이 없어도 데이터에서 인사이트를 얻을 수 있게 되는 것. Text-to-SQL이 그 방향으로 한 걸음 가까워지게 해준 기술이라고 생각한다. 사용 기술: Claude Projects, BigQuery MCP, Model Context Protocol (MCP), Google BigQuery 관련 키워드: Text-to-SQL, NL2SQL, Conversational Analytics, AI Analytics, Data Democratization

AIAutomation

회사 캘린더 비서 만들기 (feat. n8n, Gemini API)

2026.05.12

잔디에 말 걸면 알아서 회의실 예약해주는 AI 비서, 직접 만들어봤습니다. 우리 회사는 회의실 예약을 Google Calendar로 관리한다. 문제는 예약할 때마다 캘린더 앱을 열고, 시간대를 확인하고, 이벤트를 직접 만들어야 한다는 것. 팀원들이 회의실 예약하러 캘린더 들어가는 걸 귀찮아하는 걸 보고 생각했다. "잔디에서 그냥 말로 하면 안 되나?" 그래서 만들었다. 잔디 채팅창에 "내일 오후 2시에 대회의실 1시간 잡아줘" 라고 보내면, AI가 알아서 캘린더에 예약을 넣어주는 시스템. \[이미지: n8n 워크플로우 전체 화면\] 사용한 스택은 단순하다. n8n — 워크플로우 자동화 허브 Google Gemini — 자연어 해석 + 도구 호출 판단 Google Calendar API — 실제 예약 실행 잔디(Jandi) — 사용자 인터페이스 (채팅) 흐름은 이렇다. 잔디 메시지 → n8n Webhook → AI Agent (Gemini) → Google Calendar → 잔디 응답 AI Agent가 중심이고, 상황에 따라 4가지 Calendar 도구 중 하나를 선택해서 실행한다. | 도구 | 기능 | | --- | --- | | createconferenceroomevent | 새 회의실 예약 생성 | | updateconferenceroomevent | 기존 예약 수정 | | deleteconferenceroomevent | 예약 삭제 | | searchconferenceroom | 예약 현황 조회 | 잔디는 특정 채널에 메시지가 오면 외부 URL로 POST 요청을 보내는 Incoming/Outgoing Webhook을 지원한다. 잔디 팀 설정에서 Outgoing Webhook을 추가한다. 채널: 봇이 응답할 채널 선택 트리거 단어: 설정하지 않으면 모든 메시지에 반응 (봇 전용 채널 추천) URL: n8n Webhook URL 입력 \[이미지: 잔디 Outgoing Webhook 설정 화면\] 잔디가 보내는 POST body 구조는 다음과 같다. json { "token": "...", "teamName": "tugether", "roomName": "회의실봇", "writerName": "홍길동", "text": "내일 오후 2시에 대회의실 1시간 잡아줘", "writer": "[email protected]" } n8n에서 잔디로 응답을 보낼 때는 잔디의 Incoming Webhook URL을 사용한다. 잔디 채널 설정 → Incoming Webhook 추가 → URL 복사. 나중에 n8n에서 HTTP Request 노드로 이 URL에 POST하면 된다. json { "body": "✅ 예약 완료! 내일 오후 2~3시 대회의실을 예약했습니다." } n8n에서 새 워크플로우를 만들고 Webhook 노드를 추가한다. HTTP Method: POST Path: 원하는 경로 (예: calendar-bot) Response Mode: Respond to Webhook 노드로 분리 팁: Response Mode를 "Last Node"로 하면 AI 처리가 끝날 때까지 HTTP 연결을 물고 있어야 한다. Gemini 응답이 느릴 수 있으니 "Respond to Webhook" 노드를 별도로 두고 먼저 잔디에 응답하는 구조를 추천한다. Webhook URL을 복사해서 잔디 Outgoing Webhook에 붙여넣으면 1단계 연결 완료. n8n에서 Google Calendar 노드를 쓰려면 OAuth2 인증이 필요하다. 1. [Google Cloud Console](https://console.cloud.google.com/?ref=haas.kr) → API & Services → Credentials 2. OAuth 2.0 클라이언트 ID 생성 (웹 애플리케이션) 3. 승인된 리디렉션 URI에 n8n OAuth 콜백 URL 추가 형식: https://[n8n도메인]/rest/oauth2-credential/callback 4. 클라이언트 ID / Secret 복사 n8n → Credentials → Google Calendar OAuth2 API → 위 값 입력 후 연결. AI Agent의 Tool 포트에 Google Calendar 노드 4개를 연결한다. 각 노드 설정: create\conference\room\event Operation: Create Calendar: 회의실 캘린더 선택 나머지 필드: AI가 채워줌 (Expression으로 설정) update\conference\room\event Operation: Update Event ID: AI가 검색해서 넘겨줌 delete\conference\room\event Operation: Delete search\conference\room Operation: Get All 시간 범위: AI가 판단해서 설정 각 노드의 이름을 도구 이름과 동일하게 지정해야 AI Agent가 올바르게 인식한다. 워크플로우의 핵심. AI Agent 노드를 추가하고 설정한다. AI Agent의 Chat Model 포트에 Google Gemini Chat Model 노드를 연결한다. Model: gemini-2.0-flash (속도/비용 균형) API Key: Google AI Studio에서 발급한 Gemini API 키 AI Agent의 System Prompt가 전체 품질을 결정한다. 아래는 실제 사용 중인 프롬프트 구조다. 너는 00(test.ai)의 전문 회의실 예약 비서야. 사용자의 입력({{ $json.body.data }})과 작성자 정보({{ $json.body.writer.email }})를 바탕으로 [예약/수정/삭제] 업무를 수행해. 1. 의도 분류: 사용자의 요청이 '신규 예약', '시간/제목 수정', '예약 취소(삭제)' 중 무엇인지 판단해. 2. 시간 계산: 현재 시간({{ $now }})을 기준으로 시작/종료 시간을 ISO8601 형식으로 변환해. (타임존: Asia/Seoul) - 별도 언급 없으면 종료 시간은 시작 시간 1시간 뒤로 설정. '2시간 동안' 등 기간 언급 시 그에 맞게 계산. 3. 제목 생성: 제목 언급이 없으면 "{{ $json.body.writer.name }}님의 회의"로 자동 생성. 4. 회의실 ID 매핑: (언급 없으면 '10층 대회의실' 기본값) - 10층 대회의실/큰방: '[email protected]' - 10층 소회의실/작은방: '[email protected]' - 8층 대회의실/미팅룸: '[email protected]' 5. 참석자: 본문의 <@이메일 멘션을 추출해 'getmemberemails'로 이메일을 확보하고 작성자({{ $json.body.writer.email }})를 포함해. - 모든 액션 전 'searchroomschedules' 도구를 호출해 해당 날짜의 전체 일정을 가져와. - [예약/수정 시]: 요청한 시간대에 겹치는 일정이 있는지 확인해. (수정의 경우 본인 기존 일정은 제외) - [수정/삭제 시]: 기존 일정 중 사용자가 말한 시간/제목과 일치하는 'Event ID'를 찾아내. 1. 신규 예약: 중복이 없을 때만 'createroomevent' 호출. 2. 일정 수정: 'Event ID'가 확인되고 변경 시간대에 중복이 없을 때 'updateroomevent' 호출. 3. 일정 삭제: 'Event ID'가 확인되면 'deleteroomevent' 호출. 모든 응답은 친절한 한국어로 하며 아래 형식을 지켜줘. ✅ 성공 시: [예약 성공 / 수정 완료 / 삭제 완료] 되었습니다. - 회의실: [회의실 이름] - 일시: [시작시간] ~ [종료시간] - 제목: [회의 제목] - 참석자: [이름/이메일 리스트] ❌ 중복/실패 시: 요청하신 [회의실 이름]은 이미 아래 일정이 잡혀 있어 처리가 불가능합니다. [해당 날짜 전체 일정 리스트] - [시작~종료] : [제목] (이후 "다른 시간으로 도와드릴까요?"라고 마무리) AI Agent의 User Message에는 잔디에서 받은 텍스트를 넘긴다. {{ $json.body.text }} 잔디 Outgoing Webhook이 text 필드에 메시지를 담아서 보내기 때문에 이렇게 받는다. AI Agent 처리가 끝나면 Respond to Webhook 노드로 잔디에 응답을 돌려준다. Response Body: AI Agent의 출력값 {{ $json.output }} 동시에 잔디 Incoming Webhook으로도 별도 POST를 보내면 채널에 메시지가 표시된다. 잔디에서 예약 요청 메시지 보내는 화면 잔디에서 AI 응답이 돌아오는 화면 실제로 이런 요청들이 동작한다. "내일 오후 3시에 소회의실 2시간 잡아줘" → 중복 확인 후 예약 생성 "이번 주 금요일 대회의실 예약 취소해줘" → 검색 후 삭제 "다음 주 월요일 오전에 어느 회의실 비어 있어?" → 가용 시간 조회 "아까 예약한 거 4시로 바꿔줘" → 이벤트 찾아서 업데이트 잔디에서 Webhook이 안 불릴 때 잔디 Outgoing Webhook은 Public URL이 필요하다. 로컬 n8n은 Cloudflare Tunnel이나 ngrok으로 외부 노출이 필요하다. Gemini가 도구를 안 쓸 때 시스템 프롬프트에 "반드시 도구를 사용하라"는 지시를 명시적으로 추가한다. Gemini는 도구 사용 여부를 자체 판단하므로 프롬프트로 유도해야 한다. 날짜 계산이 틀릴 때 시스템 프롬프트에 {{ $now }}를 포함해서 현재 시각을 명시적으로 알려줘야 한다. 이게 없으면 Gemini가 날짜를 잘못 계산하는 경우가 생긴다. 이중 예약 문제 create 전에 반드시 search로 확인하도록 프롬프트에 강제하는 게 핵심이다. Freebusy API를 별도로 연동하면 더 정확하게 처리할 수 있다. n8n + Gemini 조합으로 생각보다 빠르게 실용적인 AI 비서를 만들 수 있었다. 코드 한 줄 없이 노드 연결만으로 자연어 처리부터 Calendar API 호출까지 완성된다는 게 n8n의 강점이다. 비슷한 구조로 HR 문의 봇, 공지사항 자동 발송, 리포트 조회 봇 등으로 확장 가능하다. 잔디 대신 Slack이나 카카오워크를 Webhook으로 연결하면 그대로 이식된다. 전체 워크플로우 JSON은 추후 공유할 예정이다.

AIAutomation

n8n과 노션으로 구축하는 RAG 기반 사내 AI 챗봇 가이드

2026.03.18

자사에서는 업계 특성상 임직원의 입/퇴사가 잦은 편이고, 이로 인한 자세한 온보딩 여력이 되지 않는다. 그로인한 다양한 임직원의 문의는 인사팀의 업무 리소스로 이어지며, 이를 해결하기 위해 사내 AI 봇을 기획하고 만들게 되었다. [Make](https://www.make.com/en?ref=haas.kr) 와 [Zapier](https://zapier.com/?ref=haas.kr) 같이 다양한 애플리케이션 간의 자동화를 no code로 도와주는 서비스 이다. 하지만, 위 툴들과 다르게 [n8n](https://n8n.io/?ref=haas.kr)이 요즘 각광을 받는 이유는 한 가지다. 바로 '셀프호스팅'을 제공한다. 즉, 내가 DevOps 지식만 있다면, 거의 무료로 자동화 시스템을 구축 할 수 있다는 말이다. 처음에 기획 했을때는 RAG가 아닌 단순히 AI Agent 노드로 제작 하였다. 노션에 올라가 있는 데이터가 많지도 않고, 따라서 성능이슈는 없을 것으로 예상했다. AI Agent 독립사용 워크플로우 이미지 1. 노션 페이지의 블록 누락 이슈 이번에 정확히 알게된 사실인데, 노션 데이터를 json으로 받게 되면, 페이지에 그냥 있는 text의 depth와 콜아웃, 토글, 표 등의 블록의 depth가 달라 데이터를 읽어오는 과정에서 이를 모두 고려해야 한다. 그래서 위 이미지를 보면 "데이터베이스 Get - 각각 페이지 ID 추출 - 각 페이지 Get - 각 페이지에서 블록 추출" 하는 로직이 필요하다. 이 과정에서 난 모든 블록을 고려하는 것은 포기하고 표와 텍스트만 가져오기로 했다. 위 문제는 Notion Node를 이용하면 비교적 간단하게 해결이 될 수도 있지만 일일히 설정 해야 하는 것은 변함이 없다. 2. 성능 이슈(속도) 노션에서 데이터를 조회하는 노드가 4개가 있고, 이렇게 하여도 사용자가 채팅을 입력할 때마다 전체 노션 데이터를 실시간으로 조회해야 한다. 데이터가 적을 때는 큰 문제가 없었지만, 페이지가 늘어날수록 응답 지연이 눈에 띄게 증가했다. 매 요청마다 Notion API를 여러 번 호출하는 구조 자체가 근본적인 병목이었다. 위 두 가지 문제를 해결하기 위해 아키텍처를 완전히 재설계했다. 노션 데이터를 매 요청마다 가져오는 대신, 주기적으로 벡터 DB에 임베딩해두고 AI Agent가 이를 Tool로 검색하는 방식으로 전환했다. 전체 플로우 워크플로우는 크게 두 개의 독립적인 플로우로 나뉜다. Schedule Trigger → Get many database pages (DB1) → Get many database pages1 (DB2) → Delete Collection (Qdrant 컬렉션 초기화) → Merge (append) → Filter → Loop Over Items → HTTP Request (Notion 페이지 본문 조회) → Code in JavaScript (텍스트 가공) → Qdrant Vector Store (임베딩 저장) ← Embeddings Google Gemini ← Default Data Loader 주요 포인트: Schedule Trigger로 주기적(예: 매일 새벽)으로 실행된다. 실행 시작 시 Delete Collection으로 기존 벡터 데이터를 전부 삭제 후 재적재한다. 이렇게 하면 노션에서 수정/삭제된 내용이 자동으로 반영된다. 여러 노션 DB를 Merge (append)로 합쳐 단일 파이프라인으로 처리한다. Filter로 불필요한 페이지(미완성, 비공개 등)를 걸러낸다. Loop Over Items로 각 페이지를 순회하며 Notion API(HTTP Request)로 본문을 가져온다. Code in JavaScript로 블록 타입별 텍스트 추출 및 포맷팅을 처리한다. 최종적으로 Qdrant Vector Store에 Google Gemini 임베딩과 함께 저장한다. When chat message received → AI Agent ← Google Gemini Chat Model ← Simple Memory ← Qdrant Vector Store2 (Tool) ← Embeddings Google Gemini1 주요 포인트: 사용자 메시지를 받으면 AI Agent가 동작한다. Qdrant Vector Store를 Tool로 연결하여, Agent가 필요할 때만 벡터 검색을 수행한다. Simple Memory로 대화 히스토리를 유지해 멀티턴 대화가 가능하다. Notion API 호출이 전혀 없으므로 응답 속도가 획기적으로 빨라진다. | 항목 | 개선 전 (AI Agent 단독) | 개선 후 (RAG + Qdrant) | | --- | --- | --- | | 데이터 조회 시점 | 매 요청마다 실시간 | 주기적 사전 적재 | | 응답 속도 | 느림 (Notion API 다중 호출) | 빠름 (벡터 검색) | | 블록 누락 | 있음 | HTTP Request로 직접 조회하여 감소 | | 최신 데이터 반영 | 즉시 | 스케줄 주기에 따라 반영 | | 비용 | Notion API 호출 많음 | 임베딩 비용 발생 (저렴) | Notion Node 대신 HTTP Request를 사용한 이유는 유연성 때문이다. Notion API의 /blocks/{blockid}/children 엔드포인트를 직접 호출하면 블록 타입을 코드로 직접 핸들링할 수 있다. // 블록 타입별 텍스트 추출 예시 const items = $input.all(); const results = []; for (const item of items) { // HTTP Response body 추출 const body = item.json.body ?? item.json; // pageId와 pageTitle은 Loop의 현재 아이템에서 가져오기 // (Loop Over Items가 넘겨준 원본 페이지 정보) const pageId = item.json.pageId ?? item.json.id ?? body?.results?.[0]?.parent?.pageid ?? ""; const pageTitle = item.json.pageTitle ?? item.json.title ?? "Untitled"; const lastEdited = item.json.lasteditedtime ?? ""; // block results 배열 추출 const blocks = body?.results ?? []; // 각 블록에서 텍스트 추출 const textParts = []; for (const block of blocks) { const type = block.type; const blockData = block[type]; if (!blockData) continue; // richtext 배열이 있는 블록 타입들 처리 const richText = blockData.richtext ?? []; const blockText = richText .map(rt = rt.plaintext ?? rt.text?.content ?? "") .join(""); if (blockText.trim()) { // 헤딩 타입은 앞에 마크다운 표시 if (type === "heading1") textParts.push(# ${blockText}); else if (type === "heading2") textParts.push(## ${blockText}); else if (type === "heading3") textParts.push(### ${blockText}); else if (type === "bulletedlistitem") textParts.push(• ${blockText}); else if (type === "numberedlistitem") textParts.push(- ${blockText}); else textParts.push(blockText); } } const bodyText = textParts.join("\n"); results.push({ json: { text: 제목: ${pageTitle}\n\n[본문]\n${bodyText}, metadata: { pageId, pageTitle, chunkIndex: 0, source: "notion", lastEdited, } } }); } return results; 매 실행마다 컬렉션을 삭제 후 재생성하는 방식은 단순하지만 효과적이다. 업데이트/삭제 추적 로직 없이도 항상 노션과 동기화된 상태를 유지할 수 있다. 데이터 양이 많아지면 upsert 방식으로 전환을 고려할 수 있다. n8n + Notion + Qdrant + Google Gemini 조합으로 사내 AI 챗봇을 구축한 결과, 인사팀의 반복 문의 대응 부담을 크게 줄일 수 있었다. 특히 RAG 아키텍처 도입 후 응답 품질과 속도 모두 만족스러운 수준으로 개선되었다. 셀프호스팅 n8n의 가장 큰 장점은 API 키와 데이터가 외부로 나가지 않는다는 점이다. 사내 민감 정보를 다루는 HR 봇에는 이 점이 특히 중요했다.

Posts