본문으로 건너뛰기
취준생을 위한 n8n 실전 워크플로우 레시피 · 22/44시리즈 보기 →

음식 사진을 올리면 AI가 평가하는 슬랙 봇 — Slack Trigger로 대화형 봇 만들기

· 약 9분
Datapopcorn CEO / AI automation educator
이 글과 연결된 글 보기
7편 · 20개 연결
100%
n8n Data Table 처음 써보기 — 트렌드 3개를 합쳐 매일 아침 분석 주제 뽑는 워크플로우n8n으로 정적 웹서버 운영하기 — 수료증 발급부터 진위 확인까지, 멱등 처리로 안전하게벡터DB 없이 실시간으로 사이트를 읽는 챗봇 — 경복대학교 안내 챗봇 해부n8n으로 반복업무 자동화 포트폴리오 만들기구글시트에 쌓인 데이터를 인스타그램 카드뉴스로 — n8n 시트 인스타그램 자동발행음식 사진을 올리면 AI가 평가하는 슬랙 봇 — Slack Trigger로 대화형 봇 만들기서브워크플로우를 Tool로 감싸 AI Agent에게 서버 점검시키기 — 그리고 시스템 프롬프트만으로는 부족한 이유

드래그로 이동 · 휠/두 손가락으로 확대 · 방향키로 글 선택 · 0으로 맞춤. 실선: 본문 링크, 점선: 시리즈·주제의 대표 연결.

표시된 글 7편 목록
전체 글 연결 탐색

슬랙 채널에 음식 사진을 올리면 Slack Trigger가 그 순간을 감지해 이미지를 GPT-4o Vision에 넘기고, 미슐랭 3스타 셰프 페르소나로 3~5문장짜리 평가를 받아 슬랙에 남기는 n8n 워크플로우를 만들 수 있습니다. Webhook처럼 외부 요청을 기다리는 대신 Slack이 보내는 이벤트를 직접 구독하는 트리거를 이번에 처음 다루게 되고, 이 구조는 그대로 제품 사진 QC나 영수증 확인 같은 실무 이미지 리뷰 봇으로 확장됩니다.

이 워크플로우 하나로 보여줄 수 있는 것​

이 워크플로우는 외부에서 요청을 던지는 Webhook 대신 "슬랙 채널에 메시지가 올라왔다"는 이벤트 자체를 트리거로 씁니다. 그 위에 얹힌 역량은 네 가지입니다.

  • 이벤트 기반 트리거 — 외부 요청을 기다리는 대신, 협업 툴에서 발생하는 이벤트를 직접 구독해 워크플로우를 시작하는 경험
  • 인증이 필요한 파일 다운로드 — 슬랙이 넘겨준 파일 URL이 아무나 열 수 있는 링크가 아니라는 것을 이해하고, credential로 인증을 붙여 내려받는 경험
  • Vision 모델에 이미지 바인딩 — 다운로드한 바이너리 데이터를 LLM 노드의 이미지 입력 필드에 정확히 연결하는 경험
  • 페르소나 프롬프트 설계 — 역할, 말투, 금지 사항, few-shot 예시로 캐릭터성 있는 답변을 만드는 경험

이 중 첫 번째와 두 번째가 특히 새롭습니다. Slack Trigger는 이 시리즈에서 처음 쓰는 트리거 방식이고, "받은 파일 URL을 그냥 fetch하면 왜 안 되는가"는 슬랙뿐 아니라 노션·구글드라이브처럼 인증이 걸린 서비스를 다룰 때 계속 마주치는 문제입니다.

배경 — 재미로 시작했지만 뼈대는 진지합니다​

이 워크플로우의 스티키 노트에는 목적이 딱 한 줄로 적혀 있습니다. "음식 사진을 올리면 AI안성재가 음식을 평가한다." 업무 반복 작업을 줄이려는 자동화라기보다, 슬랙에서 바로 써볼 수 있는 재미있는 봇에 가깝습니다. 페르소나 이름은 넷플릭스 요리 경연 프로그램에 출연한 실제 셰프의 이름에서 따왔지만, 실제 인물의 발언이나 평가와는 무관한 패러디 캐릭터라는 점은 짚어두겠습니다.

시리즈 안에서 사진 습관 트래커 편은 사진 한 장을 Webhook으로 받아 Vision 모델에 넘기는 흐름을 다뤘고, 회의록 요약 편은 녹음 파일을 입력으로 받았습니다. 이번 편은 그 이미지 입력에 슬랙이라는 창구가 하나 더해진 경우이고, 창구가 바뀌면서 트리거 방식 자체가 달라집니다. 설치나 기본 노드 설명은 n8n으로 반복업무 자동화 포트폴리오 만들기에 정리돼 있으니, 이 글에서는 이 워크플로우만 다룹니다.

Vision 모델은 OpenAI 유료 API 키가 있어야 호출할 수 있습니다. 사용량 기준 과금이라 사진 몇 장으로 테스트해보는 정도는 소액이지만, 무료 티어만으로는 이 워크플로우를 끝까지 돌려볼 수 없습니다.

구조 — Trigger / Task / Target​

  • Trigger — 지정한 슬랙 채널에 메시지가 게시되는 이벤트
  • Task — 첨부 파일의 URL로 이미지를 인증 다운로드 → 페르소나 프롬프트와 함께 Vision 모델에 전달 → 3~5문장 평가 생성
  • Target — 슬랙 채널로 평가 메시지 전송

캔버스에는 이 흐름과 별개로 Webhook 노드 하나와 슬랙 전송 노드가 하나 더 떠 있습니다. 본 흐름과는 연결돼 있지 않은데, 아래 "원본 캔버스에 남아 있는 흔적"에서 왜 흥미로운지 다룹니다.

만들기​

1. Slack Trigger — 메시지 이벤트 구독하기​

Slack Trigger 노드에서 Trigger로 Message를 고르고, 감시할 채널을 하나 지정합니다. 이 노드는 Webhook 노드처럼 "누가 POST를 보내면 시작"하는 게 아니라, 슬랙 쪽에서 발생하는 이벤트(채널에 메시지가 올라옴)를 n8n이 직접 구독하는 방식입니다. credential을 연결한 뒤 노드가 안내하는 절차를 따라가면 슬랙 앱에 이벤트 구독을 연결할 수 있습니다.

이 방식은 채널의 메시지 전체를 구독한다는 점도 알아둬야 합니다. 이 워크플로우는 이미지가 첨부된 메시지만 걸러내는 조건 노드가 없어서, 사진 없이 텍스트만 올라와도 트리거는 똑같이 실행됩니다. 뒤 단계에서 이게 어떤 문제를 만드는지는 한계 항목에서 다시 짚습니다.

2. HTTP Request — Slack이 준 파일 URL로 이미지 내려받기​

Slack Trigger가 넘겨주는 데이터 안에는 첨부 파일의 비공개 다운로드 URL이 files[0].url_private로 들어 있습니다. 이 URL은 브라우저에서 그냥 열리지 않습니다. 슬랙 봇 토큰으로 인증한 요청만 파일을 내려받을 수 있기 때문입니다.

HTTP Request 노드는 이렇게 설정돼 있습니다.

  • URL: {{ $json.files[0].url_private }}
  • Authentication: Predefined Credential Type → Slack API
  • Response Format: File, Output Property Name: image

Authentication을 Predefined Credential Type으로 두고 Slack API credential을 고르면, 헤더에 인증 토큰을 직접 적을 필요 없이 n8n이 알아서 붙여줍니다. Response Format을 File로 두고 Output Property Name을 image로 지정했기 때문에, 이 노드를 통과한 아이템은 image라는 이름의 바이너리 데이터를 갖게 됩니다. 이 이름은 다음 단계에서 그대로 다시 나옵니다.

3. Basic LLM Chain — 이미지와 페르소나 프롬프트 묶기​

Basic LLM Chain 노드에 Chat Model로 OpenAI(chatgpt-4o-latest)를 연결합니다. Prompt Type은 Define으로 두고 Text에는 아래에서 다룰 페르소나 프롬프트를 넣습니다. 여기서 중요한 설정은 Messages 쪽입니다. 메시지 타입을 이미지 바이너리로 두고, Binary Image Data Key에 앞 단계에서 지정한 이름과 똑같이 image를 적습니다.

이 두 노드가 바이너리 키 이름으로 연결된다는 게 핵심입니다. HTTP Request의 Output Property Name과 Basic LLM Chain의 Binary Image Data Key 이름이 다르면, 이미지가 전달되지 않고 텍스트 프롬프트만 모델에 들어갑니다. 이름을 맞추는 이 한 단계를 놓치면 "왜 사진을 안 보고 아무 말이나 하지?"라는 결과를 보게 됩니다.

프롬프트는 역할·제약사항·말투·필수 어휘·few-shot 예시 순서로 짜여 있습니다. 전체가 아니라 구조를 보여주는 부분만 발췌합니다.

# 역할 부여
당신은 대한민국 유일의 미슐랭 3스타 셰프이자 요리 경연 프로그램의 심사위원 [안성재]입니다.
사용자가 입력하는 텍스트 또는 업로드하는 음식 사진을 실제 심사장에 나온 요리처럼 대하고,
아래 페르소나에 맞춰 냉철하게 평가하세요.

# 제약 사항 (필수)
- 오직 대사만: 실제 사람이 말하듯이 대사만 출력하세요.
- 사진이 아니라 음식만 평가하세요.
- 길이: 3~5문장으로 짧고 임팩트 있게 끝내세요.

# 핵심 태도 및 말투
1. 정중한 냉철함: 말투는 나긋나긋하고 신사적인 "해요"체를 유지하되,
내용은 매우 직설적이고 분석적이어야 합니다.
2. 말버릇: 말 끝을 "~거든요", "~하는 거예요", "~거지"로 맺으세요.
3. 필수 어휘: 이븐(even)하게, 타이트(tight)하게, 뉘앙스, 킥(Kick), 터치, 인텐션(의도), 테크닉

# 대화 예시 (Few-shot)
User: (스테이크 사진 업로드) 이거 어때?
Assistant: 음... 겉면의 색깔은 좋은데, 지금 단면을 보면 고기가 이븐(even)하게 익지
않았어요. 안쪽은 거의 생고기거든요. 열 전달이 고루 안 된 거예요.
제 기준에는... 잘 못 구워졌습니다.

이 프롬프트에서 눈여겨볼 부분은 "제약 사항"입니다. 답변 길이를 3~5문장으로 못 박고, "오직 대사만" 출력하라고 명시했습니다. 이 두 줄이 없으면 모델이 서두나 부연 설명을 붙여서 캐릭터가 흐려집니다. 필수 어휘 목록도 마찬가지입니다. "이븐하게", "인텐션" 같은 단어를 지정해두면 매번 다른 사진에도 같은 캐릭터가 말하는 것처럼 느껴집니다. 페르소나 봇을 만들 때는 성격 묘사보다 이런 구체적인 규칙이 훨씬 잘 먹힙니다.

4. Slack — 평가 결과 보내기​

Basic LLM Chain의 출력을 Send a message 노드로 받아 지정한 채널로 전송합니다. 다만 이 워크플로우에서 결과가 도착하는 채널은 트리거가 감지한 채널과 다르게 고정돼 있고, 스레드 답글 설정도 없습니다. 즉 사진을 올린 자리에 답글이 달리는 게 아니라, 정해둔 다른 채널에 새 메시지로 올라갑니다.

같은 스레드에 답글로 달고 싶다면 Send a message 노드의 채널을 트리거와 같은 곳으로 맞추고, Additional Fields의 Thread Timestamp에 트리거 노드의 타임스탬프를 넣으면 됩니다. 이 시점의 $json은 Basic LLM Chain을 거쳐 온 값이라 트리거 데이터가 남아 있지 않으니, {{ $('Slack Trigger').item.json.ts }}처럼 노드 이름으로 직접 앞 단계를 참조해야 합니다. 원본 워크플로우는 이 설정을 쓰지 않았지만, 실제 서비스로 확장한다면 이 한 단계가 사용자 경험을 크게 바꿉니다.

원본 캔버스에 남아 있는 흔적 — 연결 안 된 Webhook​

캔버스를 열어보면 본 흐름과 전혀 이어지지 않은 노드 쌍이 하나 더 있습니다. Webhook 노드가 고정된 텍스트("하이") 하나를 슬랙 메시지로 보내는 노드에 바로 연결돼 있고, 이 둘은 Slack Trigger로 시작하는 평가 흐름과 완전히 분리돼 있습니다. 슬랙 노드로 메시지를 보내는 것 자체가 되는지 먼저 확인해본 테스트 흔적으로 보입니다.

이 흔적이 유용한 이유는 Slack Trigger와 Webhook의 차이를 같은 캔버스 안에서 비교할 수 있게 해주기 때문입니다. Webhook은 누구든 그 URL로 POST 요청을 보내면 실행되고, 데이터 형태도 우리가 정합니다. Slack Trigger는 슬랙이라는 특정 서비스의 이벤트에 묶여 있고, 데이터 형태도 슬랙이 정한 이벤트 페이로드 그대로 들어옵니다. 외부 서비스가 없는 나만의 트리거를 만들고 싶다면 Webhook이 맞고, 이미 쓰고 있는 협업 툴의 움직임에 반응하고 싶다면 Slack Trigger 같은 이벤트 기반 트리거가 맞습니다.

한계​

  • 이미지 없는 메시지도 트리거를 실행시킵니다 — Slack Trigger 뒤에 첨부 파일 유무를 거르는 조건 노드가 없어서, 텍스트만 올라와도 워크플로우가 시작됩니다. files[0]이 없는 상태로 HTTP Request가 실행되면 에러가 납니다. IF 노드로 {{ $json.files }}가 비어 있는지 먼저 걸러주는 게 안전합니다.
  • 답글이 아니라 새 메시지입니다 — 위에서 다룬 대로 평가 결과는 트리거 채널이 아닌 별도 채널에 새 메시지로 도착합니다. 대화형 봇처럼 쓰려면 같은 채널·같은 스레드로 맞추는 작업이 필요합니다.
  • 봇 자신의 메시지도 이벤트로 잡힐 수 있습니다 — 채널 메시지 전체를 구독하는 구조라, 만약 평가 결과를 트리거와 같은 채널로 보내도록 바꾸면 봇이 보낸 메시지가 다시 트리거를 실행시킬 위험이 생깁니다. 채널을 분리해뒀던 것도 이 문제를 피하려는 설계였을 가능성이 있습니다.
  • 운영 데이터는 확인하지 못했습니다 — 이 워크플로우는 archived 상태로 export돼 있어 실행 이력을 확인할 수 없었습니다. 음식이 아닌 사진을 올렸을 때 모델이 실제로 어떻게 반응하는지는 확인된 바 없어, 여기서는 캔버스에 남은 설계만 다뤘습니다.

포트폴리오·면접 포인트​

포트폴리오에는 이 봇을 그대로 넣기보다 "같은 뼈대로 무엇을 만들 수 있는가"를 보여주는 게 낫습니다. Slack Trigger로 이벤트를 잡고, 인증된 URL로 이미지를 받고, Vision 모델에 역할과 판단 기준을 프롬프트로 못 박는 이 구조는 제품 사진이 규격에 맞는지 확인하는 QC 봇, 영수증 사진을 올리면 항목을 읽어주는 봇으로 그대로 옮겨집니다. 바뀌는 건 페르소나 프롬프트의 내용뿐입니다.

면접에서 "이 봇 왜 만들었어요?"라는 질문에는 "음식 평가는 재미로 만들었지만, Slack 이벤트 트리거로 이미지를 받아 인증 다운로드하고 Vision 모델에 역할을 지정해 답을 강제하는 구조는 실무 이미지 리뷰 자동화에 그대로 쓸 수 있습니다"라고 답할 수 있습니다. 여기에 "지금 버전은 답글이 아니라 새 메시지로 가는데, Thread Timestamp를 추가하면 대화형으로 바꿀 수 있다"는 한계 인식까지 덧붙이면, 노드를 따라 만든 사람이 아니라 구조를 이해한 사람으로 보입니다.

다음 액션​

다음은 입력이 이미지에서 텍스트로 돌아갑니다. 받은 메일을 AI가 분류해 Supabase에 쌓는 편인데, Slack Trigger가 이벤트를 구독했던 것처럼 이번에는 Gmail의 새 메일 이벤트를 구독하는 트리거를 씁니다. 이벤트 기반 트리거에 한 번 익숙해지면, 어떤 서비스든 "무슨 일이 생기면 시작한다"는 뼈대는 똑같다는 게 보이기 시작합니다.