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

명함 사진을 드라이브에 올리면 구글시트에 자동 정리 — n8n + Mistral OCR 명함 입력 자동화

· 약 9분
Datapopcorn CEO / AI automation educator
이 글과 연결된 글 보기
7편 · 20개 연결
100%
n8n Data Table 처음 써보기 — 트렌드 3개를 합쳐 매일 아침 분석 주제 뽑는 워크플로우n8n으로 정적 웹서버 운영하기 — 수료증 발급부터 진위 확인까지, 멱등 처리로 안전하게벡터DB 없이 실시간으로 사이트를 읽는 챗봇 — 경복대학교 안내 챗봇 해부명함 사진을 드라이브에 올리면 구글시트에 자동 정리 — n8n + Mistral OCR 명함 입력 자동화학교코드만 바꾸면 재사용 — n8n 워크플로우를 일회성 스크립트에서 템플릿으로 만드는 법구글시트에 쌓인 데이터를 인스타그램 카드뉴스로 — n8n 시트 인스타그램 자동발행서브워크플로우를 Tool로 감싸 AI Agent에게 서버 점검시키기 — 그리고 시스템 프롬프트만으로는 부족한 이유

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

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

구글 드라이브의 특정 폴더에 명함 사진을 올리기만 하면, 몇 분 안에 Mistral OCR이 글자를 읽고 AI가 이름·직함·이메일·전화번호로 나눠 구글시트에 한 행씩 추가한 뒤 슬랙으로 "명함 N개 등록 완료"를 보내줍니다. 이 흐름을 만들어보면 OCR과 AI 구조화 출력이 왜 별개의 단계인지 몸으로 이해하게 되고, 그 이해는 영수증·설문지·운송장 같은 다른 문서 자동화에 그대로 옮겨집니다.

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

재고 알림 편이 "숫자가 조건을 넘으면 알려줘" 유형의 기본기였다면, 이 편은 "비정형 입력을 정형 데이터로 바꿔라" 유형입니다. 사진 한 장이 들어와서 시트의 행 하나가 되어 나가는 동안 네 가지 역량이 드러납니다.

  • 파일 트리거 — 폴더에 파일이 생기는 것을 감지해 워크플로우를 시작하는 경험
  • 외부 API 호출 — 공식 노드가 없거나 부족할 때 HTTP Request 노드와 credential로 직접 API를 부르는 경험
  • AI 구조화 출력 — LLM의 답을 자유 문장이 아니라 정해진 JSON 스키마로 강제하는 경험(Structured Output Parser)
  • 시트 적재와 알림 — 구조화된 결과를 시트 컬럼에 맞춰 넣고 완료를 알리는 경험

이 중 세 번째가 핵심입니다. OCR은 명함의 글자를 읽어 줄 뿐, 어느 글자가 이름이고 어느 글자가 회사명인지는 모릅니다. 그 판단을 AI에게 맡기되 "반드시 이 필드들로만 답하라"고 묶어두는 게 Structured Output Parser의 역할이고, 이걸 써본 사람과 안 써본 사람은 AI 자동화를 설계하는 방식 자체가 다릅니다.

배경 — 행사 뒤에 쌓이는 명함 뭉치​

행사나 밋업을 다녀오면 명함이 한 뭉치 생깁니다. 나중에 연락하려면 어딘가에 정리해야 하는데, 명함 앱은 내 시트로 안 나오고 손으로 치자니 한 장에 1분씩 걸립니다. 이 워크플로우는 그 문제를 "드라이브 폴더에 사진만 던져 넣으면 시트에 정리되는" 흐름으로 푼 것입니다. 원본 캔버스의 트리거 노드 이름이 "New Invoice Detection"인 걸 보면 처음엔 영수증·인보이스용으로 잡았다가 명함으로 좁힌 흔적이 있는데, 그만큼 입력 문서만 바꾸면 되는 구조라는 뜻이기도 합니다.

문서 OCR은 Mistral OCR을 썼습니다. 표와 여러 단으로 된 문서를 마크다운으로 잘 뽑아주고, 페이지 단위 과금입니다. 공식 가격은 2026년 8월 기준 1,000페이지당 4달러라 명함 50장이면 20센트 정도입니다. OCR 결과를 구조화하는 모델은 Gemini, 구조화가 스키마에서 벗어났을 때 고쳐 쓰는 auto-fix에는 OpenAI 모델(gpt-4.1-mini)이 붙어 있습니다. 둘 다 무료·저가 티어로 충분히 도는 모델이라 취준생이 따라 만들어도 비용 부담이 거의 없습니다.

구조 — Trigger / Task / Target​

  • Trigger — 드라이브의 Scanning 폴더에 새 파일이 생기면(1분마다 폴링)
  • Task — Mistral OCR로 텍스트 추출 → 페이지별로 나눔 → AI가 name, title, company, email, phone… 스키마로 구조화
  • Target — 구글시트에 행 추가 → 슬랙에 완료 알림

만들기​

1. Google Drive Trigger — 폴더 하나만 감시​

드라이브에 Scanning 같은 이름으로 폴더를 만들고, Google Drive Trigger 노드에서 Trigger On을 Changes Involving a Specific Folder, Watch For를 File Created로 둡니다. Poll Times는 Every Minute입니다. 드라이브 트리거는 실시간 웹훅이 아니라 주기적으로 폴더를 들여다보는 방식이라, 사진을 올리고 최대 1분 정도 기다려야 합니다.

2. HTTP Request — Mistral OCR 부르기​

Mistral OCR은 HTTP Request 노드로 직접 호출했습니다.

  • Method: POST
  • URL: https://api.mistral.ai/v1/ocr
  • Authentication: Predefined Credential Type → Mistral Cloud API
  • Body(JSON):
{
"model": "mistral-ocr-latest",
"document": {
"type": "image_url",
"image_url": "https://drive.usercontent.google.com/download?id={{ $json.id }}&export=view&authuser=0"
}
}

API 키는 Body나 Header에 직접 적지 않고 n8n의 credential에 넣습니다. Predefined Credential Type으로 Mistral Cloud API를 고르면 인증 헤더를 n8n이 알아서 붙여줍니다.

image_url에는 트리거가 넘겨준 파일 ID로 드라이브 다운로드 URL을 조립해 넣었습니다. 이 방식이 동작하려면 Mistral 서버가 로그인 없이 그 URL을 열 수 있어야 합니다. 즉 감시 폴더가 "링크가 있는 모든 사용자에게 공개" 상태여야 합니다. 명함 정도는 괜찮지만 민감한 문서라면 이 방식 대신 Google Drive 노드로 파일을 내려받아 바이너리를 직접 올리는 쪽으로 바꿔야 합니다.

3. Split Out — pages를 페이지별 아이템으로​

Mistral OCR 응답은 pages 배열 안에 페이지마다 markdown 필드를 담아 돌려줍니다. Split Out 노드에서 Field To Split Out을 pages로 두면 페이지 하나가 아이템 하나가 되고, 다음 노드에서 {{ $json.markdown }}으로 바로 텍스트에 접근할 수 있습니다. 명함은 보통 1페이지라 큰 의미는 없지만, 여러 장을 스캔한 PDF를 넣으면 이 노드 덕분에 장마다 따로 처리됩니다.

4. AI Agent + Structured Output Parser — 글자를 필드로​

AI Agent 노드의 Prompt를 Define으로 두고 Text에 {{ $json.markdown }}을 넣습니다. Chat Model에는 Google Gemini를 연결하고, Require Specific Output Format을 켠 뒤 Structured Output Parser를 붙입니다.

파서의 JSON Example에는 원하는 형태를 예시로 적어둡니다. 원본에는 이렇게 들어 있습니다.

{
"data": {
"name": "홍길동",
"title": "수석 데이터 분석가",
"company": "데이터팝콘 주식회사",
"department": "비즈니스 인텔리전스팀",
"email": "[email protected]",
"phone": "010-1234-5678",
"address": "서울특별시 강남구 데이터로 1234",
"website": "https://www.example.com",
"note": "컨설팅 프로젝트 및 강연 가능"
}
}

파서의 Auto-Fix Format을 켜고 그 아래에 OpenAI Chat Model(gpt-4.1-mini)을 하나 더 연결했습니다. Gemini가 스키마를 살짝 벗어난 답을 내면 이 모델이 한 번 더 고쳐 쓰는 안전장치입니다.

시스템 프롬프트는 캔버스의 스티키 노트에 한글판과 영문판이 같이 적혀 있는데, 한글판을 옮기면 다음과 같습니다. AI Agent의 System Message에 그대로 넣으면 됩니다.

당신은 명함 데이터 파서입니다.

당신의 임무는 JSON 형식의 명함 데이터에서 구조화된 정보를 추출하고 이해하는 것입니다.

해당 JSON에는 다음과 같은 필드들이 포함될 수 있습니다:
• name (이름)
• title (직책 또는 직위)
• company (회사 또는 조직명)
• department (부서명, 선택사항)
• email, phone, mobile, fax (연락처 정보)
• address (도로명, 도시, 시/도, 우편번호, 국가 등으로 구성된 주소 정보)
• website (개인 또는 회사 공식 웹사이트)
• note (기타 추가 정보)

이 데이터를 실제 종이 명함에 인쇄된 정보처럼 해석하세요.
별도의 지시가 없는 한 원래의 표기 및 형식을 유지해야 하며,
선택 필드가 비어 있는 경우 값을 추정하거나 임의로 생성하지 마십시오.

별도 요청이 없는 한 구조화된 데이터 또는 유효성 검증 메시지로만 응답하십시오.

마지막 두 줄이 중요합니다. "비어 있으면 지어내지 말라"를 명시하지 않으면 모델이 없는 부서명이나 그럴듯한 주소를 채워 넣는 경우가 생깁니다. 시트에 들어간 뒤에는 어느 값이 진짜였는지 구분할 방법이 없으니, 이 지시는 프롬프트에 꼭 남겨두세요.

5. Split Out → Google Sheets — 컬럼에 맞춰 행 추가​

AI Agent의 결과는 output 필드 아래에 들어옵니다. Split Out 노드로 output을 풀어준 뒤 Google Sheets 노드(Operation: Append)에서 Mapping Column Mode를 Map Automatically로 두면, 시트 헤더와 이름이 같은 필드가 알아서 들어갑니다.

원본 시트의 헤더는 name, title, email, phone 네 개뿐입니다. 스키마에는 company, address 같은 필드가 더 있지만, 자동 매핑은 시트에 헤더가 있는 컬럼만 채우고 나머지는 버립니다. 회사명도 남기고 싶으면 시트에 company 헤더를 추가하기만 하면 됩니다. 반대로 말하면, 시트 헤더가 곧 "무엇을 남길지"를 정하는 설정값입니다.

6. Slack — 몇 장 들어갔는지 알리기​

Send a message 노드의 Text에 명함이 {{ $items().length }} 개 등록이 완료되었습니다.를 넣었습니다. $items().length는 이 노드로 들어온 아이템 수, 즉 시트에 추가된 행 수입니다. 명함 5장을 한 번에 올리면 "5개 등록 완료" 한 통이 옵니다.

원본은 이렇게 돼 있지만, $items()는 예전 문법입니다. 지금 새로 만든다면 {{ $input.all().length }}를 쓰세요. 뜻은 같고, n8n 문서에서 현재 권장하는 표기입니다.

원본 캔버스에 남아 있는 흔적 — 두 가지 OCR 방식​

원본 워크플로우를 열어보면 본 흐름과 연결되지 않은 노드가 하나 떠 있습니다. n8n에 내장된 Mistral AI 노드(Extract Text)를 이미지 URL로 테스트해본 흔적입니다. 즉 이 워크플로우를 만들 때 HTTP Request로 직접 부르는 방법과 내장 노드를 쓰는 방법을 둘 다 시도했고, 본 흐름은 HTTP Request 쪽으로 확정했습니다.

지금 따라 만든다면 내장 Mistral AI 노드가 더 간단합니다. Body를 직접 조립할 필요가 없고 credential만 고르면 됩니다. 그래도 HTTP Request 버전을 먼저 만들어보길 권하는 이유는, 내장 노드가 없는 API를 만났을 때 어차피 이 방법을 써야 하기 때문입니다. HTTP Request + Predefined Credential 조합을 한 번 손에 익혀두면 "공식 노드가 없어서 못 한다"는 말이 사라집니다.

한계​

  • 폴더 공개 필요 — 위에서 말한 대로 드라이브 다운로드 URL을 OCR에 넘기는 방식이라 폴더가 공개 상태여야 합니다. 명함이 아닌 계약서·영수증에 적용할 때는 바이너리 업로드 방식으로 바꿔야 합니다.
  • 중복 등록 — 같은 명함을 두 번 올리면 시트에 두 번 들어갑니다. Append 대신 Append or Update를 쓰고 email을 매칭 컬럼으로 지정하면 해결됩니다.
  • 명함 앞뒤면 — 앞면과 뒷면을 따로 찍으면 두 사람으로 등록됩니다. 실무에서는 한 장에 앞뒤를 같이 찍거나, 뒷면은 올리지 않는 규칙이 필요합니다.
  • 폴링 지연 — 1분 폴링이라 즉시성은 없습니다. 명함 정리에는 충분하지만, 초 단위 반응이 필요한 문서 흐름이면 웹훅 기반 업로드 폼으로 바꿔야 합니다.

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

이 워크플로우는 "OCR → 구조화 → 적재" 3단 구조만 유지하면 입력 문서를 바꾸는 것만으로 다른 프로젝트가 됩니다. 원본 스티키 노트에 만든 사람이 적어 둔 응용 목록이 그대로 힌트입니다.

  • 세금계산서 OCR → 회계 시트 연동
  • 수기 설문지 OCR → 구글시트 정리
  • 택배 운송장 스캔 → 발송 내역 자동 기록
  • 계약서 분류 및 요약

포트폴리오에는 명함보다 이 중 하나를 골라 스키마를 직접 설계한 버전이 더 좋습니다. 명함은 필드가 뻔하지만, 설문지나 운송장은 "무엇을 필드로 뽑을지"를 스스로 정해야 하고 그 판단이 곧 포트폴리오의 내용이 됩니다.

면접에서 "OCR 결과를 왜 바로 시트에 안 넣고 AI를 한 번 더 거쳤나요?"라는 질문이 오면, OCR은 글자의 위치와 내용만 알 뿐 의미를 모른다는 점, 그리고 Structured Output Parser로 출력 형식을 강제하지 않으면 시트 컬럼에 맞춰 넣을 수 없다는 점을 말하면 됩니다. 여기에 "없는 값을 지어내지 말라는 지시를 프롬프트에 넣은 이유"까지 덧붙이면 데이터 품질을 생각한 사람으로 보입니다.

다음 액션​

명함은 사진 한 장이 행 하나가 되는 단순한 경우입니다. 다음으로는 음성 파일 하나가 회의록 문서와 노션 페이지가 되는 편으로 넘어갑니다. 입력이 이미지에서 오디오로, 출력이 시트 한 행에서 문서 전체로 커지지만 "비정형 입력 → AI 처리 → 정형 출력"이라는 뼈대는 이 편과 같습니다.