4강

AI Agent 활용 기초

이 강에서 만들 것

지금까지 배운 요소들을 통합해 실무에서 그대로 쓸 수 있는 사내 자동화 시스템 을 구축합니다. 예제 시나리오: 사내 비품 신청 자동 처리.

  • ① 임직원이 웹 폼 으로 비품 신청
  • ② 신청 데이터를 n8n 내부 DB 에 저장
  • ③ AI Agent 가 DB 를 조회해 부서별로 다른 답변 생성
  • ④ Gmail 로 담당자에게 자동 이메일 발송
💡
이 흐름 하나만 마스터하면 사내 티켓 시스템 · 문의 접수 · 예약 신청 · 재고 신청 등 회사 안의 반복 신청 프로세스 대부분을 자동화할 수 있습니다.

폼 트리거 선택

새 워크플로우에서 첫 노드로 n8n Form 트리거를 선택합니다. 노드 검색창에 form 만 쳐도 상단에 나옵니다.

n8n Form — n8n 이 직접 웹폼을 호스팅해줌
On form submission — 폼 제출 시 워크플로우 실행
💡
외부 폼 서비스(구글 폼·타입폼 등)를 안 써도 됩니다. n8n 이 공용 URL 로 폼 페이지를 자동 호스팅합니다.

폼 필드 세팅

Form Title, Description, Form Elements 를 세팅합니다. 각 Form Element 는 사용자에게 보일 입력 항목.

제목·설명·필드(이름, 부서, 비품명, 수량, 사유)를 정의
  • Form Title — 폼 상단 큰 제목 (예: 비품신청목록)
  • Form Description — 안내 문구 (예: 수량이 많은 경우 별도 승인이 필요할 수 있습니다.)
  • Element Type — Text Input / Textarea / Dropdown / Date 등 필드 유형 선택
  • Label — 각 입력창 위 표시명 (사용자 화면에 그대로 노출)

이 실습에서는 정확히 5개 필드를 만듭니다. 왼쪽은 폼에 표시할 Label(한글), 오른쪽은 뒤에서 만들 n8n Data Table 컬럼명(영문) 입니다. 폼과 DB의 이름이 헷갈리지 않도록 지금부터 두 세트를 짝지어 두세요.

📌 폼 필드 5개 (n8n 폼 노드에 입력할 값)
Label (폼 표시)DB 컬럼명Element Type필수
이름NameText Input필수
부서DepartmentText Input필수
신청 비품RequestedItemText Input필수
수량QuantityNumber필수
신청 사유ReasonTextarea선택
💡
폼 Label 은 한글로 사용자 친화적으로, DB 컬럼명은 영문 CamelCase로 통일합니다. 이렇게 하면 뒤에서 DB 매핑 표현식 ({{ $json.Name }} 등) 을 쓸 때 오타·인코딩 이슈가 없습니다.

폼 실행 — Test URL 로 확인

노드 저장 후 Test URL 을 새 창에 열면 실제 폼 페이지가 뜹니다.

폼 노드에서 Test URL 복사
실제 사용자에게 보이는 폼 UI
제출하면 폼 트리거 output 에 JSON 으로 데이터가 들어옴

내부 DB 만들기 — Data Tables

n8n 은 자체 내장 Data Tables 를 제공합니다. 외부 MySQL/Postgres 없이 n8n 안에서 바로 테이블을 만들고 조회·저장할 수 있어요.

상단 Data Tables 탭 → Create Data Table.

① 좌측 사이드바 Overview → Data Tables 탭
② Create Data Table 클릭
③ 테이블 이름 입력 (예: bipum_requests)

DB 컬럼 설계

폼에서 받는 필드에 맞춰 컬럼을 만듭니다.

테이블 진입 후 컬럼 추가 시작
컬럼명 + 타입 (String / Number / Date 등)
필요한 컬럼을 하나씩 추가
created_at (저장된 시각) 컬럼 추가 — 나중에 정렬·필터에 유용
💡
실무 팁: id (자동증가) · created_at (자동) 두 개는 어떤 테이블에도 기본으로 넣는 게 좋습니다.

DB 노드 배치 + 매핑

폼 트리거 뒤에 Data Table 노드를 추가하고 앞서 만든 테이블을 선택합니다.

노드 검색 → n8n Data Table
만든 테이블 선택

액션을 Insert Row 로 두고, 폼에서 받은 값을 각 컬럼에 매핑합니다.

각 컬럼 = 폼 데이터 표현식 매핑 (예: 이름 컬럼 ← {{ $json.이름 }})

실행 → DB 저장 확인

폼을 다시 제출하면 DB 노드가 실행되어 데이터가 테이블에 들어갑니다.

폼 제출 → 워크플로우 실행 → DB Insert 성공
Data Tables 로 돌아가 실제 행이 들어왔는지 확인

테스트용 더미 데이터 대량 삽입

AI 기능을 테스트하려면 행이 여러 개 있어야 합니다. Code 노드로 20건의 신청 데이터를 한 번에 삽입.

Code 노드로 여러 신청 데이터 배열 반환 → Data Table Insert 로 흘려보냄

아래 코드를 Code 노드 안에 그대로 붙여넣고 실행하면, 20건이 한 번에 DB로 흘러들어갑니다. 5개 부서·7종 비품·다양한 사유로 구성되어 있어 뒤에서 부서별 분기 실험에 그대로 씁니다.

더미 데이터 생성 (Code 노드, 20건) javascript
return [
  { json: { 이름: "김민수", 부서: "개발팀",   "신청 비품": "모니터",    수량: 2, "신청 사유": "신규 입사자 듀얼 모니터 구성",              submittedAt: "2026-08-01T09:15:22+09:00" } },
  { json: { 이름: "이서연", 부서: "마케팅팀", "신청 비품": "무선마우스", 수량: 3, "신청 사유": "기존 마우스 노후화로 교체 필요",            submittedAt: "2026-08-01T10:32:11+09:00" } },
  { json: { 이름: "박준호", 부서: "영업팀",   "신청 비품": "노트북",    수량: 1, "신청 사유": "외근 및 고객 미팅용 장비 필요",          submittedAt: "2026-08-01T14:20:05+09:00" } },
  { json: { 이름: "최지우", 부서: "개발팀",   "신청 비품": "키보드",    수량: 2, "신청 사유": "기존 키보드 고장",                        submittedAt: "2026-08-02T09:42:31+09:00" } },
  { json: { 이름: "정현우", 부서: "디자인팀", "신청 비품": "모니터",    수량: 2, "신청 사유": "디자인 작업용 고해상도 모니터 필요",     submittedAt: "2026-08-02T11:18:43+09:00" } },
  { json: { 이름: "한유진", 부서: "인사팀",   "신청 비품": "무선마우스", 수량: 1, "신청 사유": "기존 마우스 클릭 불량",                    submittedAt: "2026-08-02T15:05:12+09:00" } },
  { json: { 이름: "강민재", 부서: "개발팀",   "신청 비품": "노트북",    수량: 3, "신청 사유": "신규 개발자 입사 예정",                    submittedAt: "2026-08-03T09:03:55+09:00" } },
  { json: { 이름: "윤서진", 부서: "마케팅팀", "신청 비품": "웹캠",      수량: 2, "신청 사유": "온라인 회의 및 콘텐츠 촬영용",            submittedAt: "2026-08-03T10:45:27+09:00" } },
  { json: { 이름: "송지훈", 부서: "영업팀",   "신청 비품": "무선마우스", 수량: 5, "신청 사유": "영업팀 공용 장비 부족",                    submittedAt: "2026-08-03T13:28:19+09:00" } },
  { json: { 이름: "김하늘", 부서: "디자인팀", "신청 비품": "키보드",    수량: 1, "신청 사유": "디자인 작업 단축키 사용을 위한 교체",     submittedAt: "2026-08-04T09:52:42+09:00" } },
  { json: { 이름: "오세훈", 부서: "개발팀",   "신청 비품": "무선마우스", 수량: 4, "신청 사유": "신규 프로젝트 팀 장비 세팅",              submittedAt: "2026-08-04T11:11:06+09:00" } },
  { json: { 이름: "임수빈", 부서: "인사팀",   "신청 비품": "헤드셋",    수량: 2, "신청 사유": "온라인 면접 진행용",                      submittedAt: "2026-08-04T14:33:50+09:00" } },
  { json: { 이름: "조현민", 부서: "영업팀",   "신청 비품": "노트북",    수량: 2, "신청 사유": "신규 영업직원 지급",                      submittedAt: "2026-08-05T08:58:31+09:00" } },
  { json: { 이름: "신예은", 부서: "마케팅팀", "신청 비품": "모니터",    수량: 1, "신청 사유": "광고 데이터 분석 업무용 보조 모니터 필요", submittedAt: "2026-08-05T10:25:46+09:00" } },
  { json: { 이름: "백승우", 부서: "개발팀",   "신청 비품": "헤드셋",    수량: 6, "신청 사유": "개발팀 온라인 회의 장비 일괄 교체",       submittedAt: "2026-08-05T16:02:13+09:00" } },
  { json: { 이름: "장다은", 부서: "디자인팀", "신청 비품": "태블릿",    수량: 2, "신청 사유": "일러스트 및 시안 작업용",                  submittedAt: "2026-08-06T09:35:17+09:00" } },
  { json: { 이름: "문재현", 부서: "마케팅팀", "신청 비품": "웹캠",      수량: 3, "신청 사유": "SNS 영상 및 라이브 방송 촬영용",          submittedAt: "2026-08-06T11:47:22+09:00" } },
  { json: { 이름: "서유나", 부서: "인사팀",   "신청 비품": "키보드",    수량: 1, "신청 사유": "기존 키보드 일부 키 입력 불량",            submittedAt: "2026-08-07T10:13:07+09:00" } },
  { json: { 이름: "권도윤", 부서: "개발팀",   "신청 비품": "모니터",    수량: 5, "신청 사유": "신규 프로젝트 개발환경 구축",            submittedAt: "2026-08-07T14:26:38+09:00" } },
  { json: { 이름: "홍지수", 부서: "영업팀",   "신청 비품": "헤드셋",    수량: 4, "신청 사유": "고객 전화 상담용 장비 교체",              submittedAt: "2026-08-08T16:52:37.829+09:00" } }
];
💡
이 데이터는 5개 부서(개발/마케팅/영업/인사/디자인) × 7종 비품 × 8일치 신청으로 구성되어 있어, 뒤 강에서 부서별 분기·집계·AI 판단을 실험하기에 딱 좋습니다.

저장된 데이터 조회

새 워크플로우(또는 브랜치)에서 Data Table 노드의 액션을 Get Rows 로 두면 저장된 데이터를 불러올 수 있습니다.

액션: Get Rows → 조건 없이 전체 조회
조회 결과 — 여러 행이 items 로 반환됨

Aggregate 로 데이터 묶기

여러 행을 배열 하나로 묶어 다음 AI 노드에 통째로 넘기기 위해 Aggregate 노드를 씁니다.

Get Rows → Aggregate 노드 연결
여러 아이템이 하나의 배열로 병합됨

역할별로 다른 답변 — OpenAI 노드 3개 (총무 / 구매담당자 / 경영지원팀장)

같은 비품 신청 데이터를 놓고도 총무 · 구매담당자 · 경영지원팀장 은 관심사가 완전히 다릅니다. 총무는 전체 현황 요약, 구매담당자는 구매 계획 표, 경영지원팀장은 경영 관점 판단. 각 역할별로 OpenAI 노드 3개를 병렬 배치해서 같은 입력 → 3가지 관점의 결과 를 동시에 뽑습니다.

Aggregate → OpenAI(총무) / OpenAI(구매담당자) / OpenAI(경영지원팀장)
각 노드마다 System(역할별 지침) + User(비품 목록) 세팅

① 총무 — 신청 현황을 훑고 요약·이상치 를 짚어주는 역할. System / User 프롬프트를 그대로 붙여넣고, Output 은 자유 텍스트로 둡니다 (Output Schema 없음).

📋 총무 · System Message
너는 회사의 총무 담당자입니다.

입력으로 전달된 사내 비품 신청 데이터를 분석해서 다음 내용을 알려주세요.

1. 가장 많이 신청된 비품 TOP 3
2. 신청 수량이 가장 많은 부서
3. 반복적으로 등장하는 신청 사유
4. 수량이 많아 확인이 필요해 보이는 신청
5. 전체 데이터를 5줄 이내로 요약
📋 총무 · User Message
아래 데이터를 분석해주세요.

{{ JSON.stringify($input.all().map(item => item.json)) }}
💡
총무 노드는 자유 서술형 요약 이 목적이라 Output Schema 를 걸지 않습니다. 아래 두 역할(구매담당자·경영지원팀장) 도 같은 이유로 자유 텍스트를 유지 — 번호 리스트 + 마지막 5줄 요약 이라는 포맷은 System 프롬프트만으로도 충분히 재현됩니다.

② 구매담당자 — 다음 달 구매 계획 을 짜는 역할. 어떤 비품을 · 얼마만큼 · 어떤 우선순위로 사둘지 판단합니다. 총무와 마찬가지로 자유 서술형이라 Output Schema 없이 프롬프트만 사용.

📋 구매담당자 · System Message
너는 회사의 구매 담당자입니다.

입력으로 전달된 사내 비품 신청 데이터를 분석해서 다음 내용을 알려주세요.

1. 다음 달에 미리 구매해두면 좋을 비품
2. 비품별 예상 필요 수량
3. 반복적으로 신청되는 비품
4. 대량 구매가 필요해 보이는 품목
5. 구매 우선순위
6. 각 추천의 근거

마지막에는 구매 계획을 5줄 이내로 요약해주세요.
📋 구매담당자 · User Message
아래 사내 비품 신청 데이터를 분석해주세요.

{{ JSON.stringify($input.all().map(item => item.json)) }}

③ 경영지원팀장 — 회사 전체 관점 에서 부서별 신청 패턴을 검토하고 개선점 을 뽑는 역할. 마지막 줄 요약은 경영진이 빠르게 읽을 수 있도록 5줄 이내로 강제.

📋 경영지원팀장 · System Message
너는 회사의 경영지원팀장입니다.

입력으로 전달된 사내 비품 신청 데이터를 분석해서 다음 내용을 알려주세요.

1. 부서별 총 신청 수량
2. 비품 신청이 가장 많은 부서
3. 부서별 주요 신청 비품
4. 반복적으로 발생하는 신청 사유
5. 비품 신청이 과도하거나 비효율적으로 보이는 부분
6. 회사 전체 관점에서 개선이 필요한 점

마지막에는 경영진이 빠르게 이해할 수 있도록
핵심 내용을 5줄 이내로 요약해주세요.
📋 경영지원팀장 · User Message
아래 사내 비품 신청 데이터를 분석해주세요.

{{ JSON.stringify($input.all().map(item => item.json)) }}
💡
여기까지가 수동 라우팅 방식. 역할이 늘어날 때마다 노드를 추가해야 해서 유지보수가 힘듭니다. 다음 단계에서 이걸 AI Agent 하나로 대체합니다.

구조화된 출력 확인

System 프롬프트에 JSON 스키마를 넣어두면 OpenAI 노드 결과도 구조화된 JSON 으로 나옵니다.

output 필드가 자유 텍스트가 아닌 JSON 문자열로 반환

AI Agent 로 통합 — 스스로 판단하게

수동으로 배치했던 노드들을 AI Agent 하나로 대체합니다. AI Agent 가 스스로 DB 를 조회하고, 부서를 판단하고, 답변까지 생성.

AI Agent 노드 하나로 대체 (Chat Model + Memory + Tool 로 Data Table 연결)
Data Table 을 Tool 로 연결 · Operation: Get / Return All 켜기 → AI 가 필요 시 스스로 호출
💡
AI Agent 의 진짜 힘은 Tool 연결. 다음 강에서 Gmail Tool 을 붙여 실제 이메일까지 자동 발송.

Gmail Tool 연동

AI Agent 하위 Tool 슬롯에 Gmail 노드를 붙이면, AI 가 필요할 때 스스로 이메일을 보낼 수 있습니다.

AI Agent Tool 슬롯 → Gmail 노드 추가
Gmail 노드의 To/Subject/Body 를 AI 자동 설정 모드로
각 필드에 'let AI define' 활성화
💡
각 필드를 AI 자동 정의 모드로 두면, AI Agent 가 상황에 맞게 수신자·제목·본문을 실시간으로 생성합니다. 매번 하드코딩할 필요 없음.

AI Agent 프롬프트 — 부서별 이메일 분기

AI Agent 의 System Message 에 어떤 조건에서 어떤 이메일을 보낼지 지침을 넣습니다.

부서별 이메일 규칙을 System Message 에 명시
AI Agent System Message (샘플) text
당신은 사내 비품 신청 처리 담당자입니다.

Data Table 에서 신청 목록을 조회하고,
부서별로 다음 규칙에 따라 이메일을 발송하세요.

[영업팀]
- 담당자: sales-lead@company.com
- 제목: [비품승인요청] 영업팀
- 본문: 신청자 이름과 비품/수량을 표로 정리, 승인 요청 문구 포함

[개발팀]
- 담당자: dev-ops@company.com
- 제목: [비품승인요청] 개발팀
- 본문: 기술 관련 비품은 우선 처리 요청 문구 포함

[마케팅팀]
- 담당자: marketing@company.com
- 제목: [비품승인요청] 마케팅팀
- 본문: 캠페인 일정과 무관한 물품인지 확인 요청

각 부서별로 이메일을 <b>한 번씩만</b> 발송하고,
같은 부서 신청은 하나의 이메일에 <b>표로 묶어서</b> 포함하세요.

실행 — AI 가 스스로 Tool 선택

AI Agent 를 실행하면, 지시받은 대로 Data Table 을 조회 → 부서별로 그룹화 → 각 부서에 맞는 Gmail Tool 을 자동 호출 하는 과정이 로그로 보입니다.

AI 가 스스로 Tool 을 골라 실행 → 부서별 이메일 3통 발송
💡
이게 AI Agent 의 진짜 가치입니다. 우리는 규칙만 자연어로 알려주고, 실제 실행 순서·데이터 정리·Tool 선택은 AI 가 알아서. 부서가 20개로 늘어도 노드 추가 없이 프롬프트만 갱신.
  • 확장 아이디어: 슬랙 Tool 추가 → 승인자에게 알림
  • 확장 아이디어: 카카오 알림톡 Tool → 신청자에게 접수 확인
  • 확장 아이디어: Google Calendar Tool → 배송 예정일 일정 자동 등록