Skip to main content
Summary

멀티모달은 "파일을 넣는다"가 아니라 "맥락을 설계한다"이다

앞 장에서 우리는 브라우저, 서버, Gemini API를 연결해 텍스트 기반 애플리케이션을 만들었다. 이제 무대가 넓어진다. 실무에서 제미나이를 쓰기 시작하면 텍스트 질문보다 먼저 들어오는 요청은 대개 이런 것들이다. "이 계약서 PDF에서 불리한 조항을 찾아줘." "이 화면 캡처를 보고 사용자가 어디서 막혔는지 말해줘." "회의 녹음 파일을 요약하고 액션 아이템을 뽑아줘." "강의 영상을 챕터별로 나눠줘." "이 코드 저장소를 읽고 인증 흐름을 설명해줘."

Gemini의 강점은 이런 입력을 별도의 OCR, 음성 인식, 이미지 캡셔닝 파이프라인으로 억지로 쪼개지 않고 한 요청 안에서 함께 다룰 수 있다는 점이다. Gemini API의 generateContent는 텍스트뿐 아니라 이미지, 오디오, 비디오, PDF 같은 파일을 프롬프트의 일부로 받을 수 있다.1 그러나 여기에는 함정이 있다. 멀티모달 입력은 데모에서는 마법처럼 보이지만, 운영에서는 곧바로 비용, 지연시간, 파일 수명, 권한, 토큰 초과, 환각 문제로 바뀐다.

이 장의 핵심은 단순하다. 파일을 넣을 수 있다고 해서 항상 통째로 넣으면 안 된다. 반대로 RAG나 청킹을 너무 빨리 도입해도 안 된다. 좋은 설계자는 "이 입력은 한 번 쓰고 버릴 것인가, 여러 번 재사용할 것인가, 공개 URL인가, 민감정보인가, 질문이 전체 맥락을 요구하는가"를 먼저 판단한다. 그 판단이 멀티모달 시스템의 품질과 비용을 가른다.

문서·이미지·오디오·비디오와 긴 컨텍스트

네 가지 입력 경로: inline data, Files API, 외부 URL, URL Context

Gemini에 파일을 전달하는 방법은 크게 네 가지로 이해하면 된다. 작은 파일을 요청 본문에 직접 넣는 inline data, 파일을 먼저 업로드하고 URI로 참조하는 Files API, 이미 외부 저장소에 있는 공개 URL·서명 URL·GCS 객체를 입력으로 연결하는 방식, 그리고 웹페이지나 문서 URL을 도구처럼 읽게 하는 URL Context다. 이름이 비슷해서 헷갈리지만 설계 목적이 다르다.

입력 방식어울리는 상황장점주의점
inline data작은 이미지, 짧은 오디오, 작은 PDF를 한 번 분석할 때업로드 단계가 없어 빠르고 단순하다요청 본문이 커지고 재사용성이 낮다
Files API큰 파일, 비디오, 반복 질의가 필요한 문서업로드 후 URI를 여러 요청에서 재사용할 수 있다파일은 장기 저장소가 아니며 수명과 용량 제한이 있다
외부 URL·서명 URL·GCS 등록원본 파일이 이미 클라우드 스토리지에 있을 때서버가 파일을 내려받아 다시 올리는 과정을 줄인다URL 접근권한, 만료시간, 인증 방식을 설계해야 한다
URL Context특정 웹페이지, PDF, 이미지 URL을 근거로 분석할 때웹 콘텐츠를 프롬프트 근거로 바로 활용할 수 있다요청당 URL 수, 단일 URL 크기, 공개 접근성 등 제약이 있다

Google은 Gemini API의 파일 입력 방식을 확장해 공개 HTTPS URL, 서명 URL, Google Cloud Storage 객체 등록을 지원하고, inline 파일 한도도 늘렸다고 발표했다.2 다만 실제 한도는 파일 유형, 모델, API 표면, SDK 버전에 따라 달라질 수 있다. 따라서 코드 안에 MAX_INLINE_MB = 100 같은 값을 박아 넣기보다, 설정 파일이나 환경변수로 관리하고, 배포 전 공식 문서의 "검증일"을 남기는 편이 안전하다.

운영 판단은 다음처럼 하면 쉽다. 사용자가 방금 업로드한 2MB 화면 캡처를 한 번 분석한다면 inline data가 가장 간단하다. 300MB 회의 영상에 대해 요약, 화자별 발언, 위험 발언 탐지, 후속 질문을 여러 번 할 예정이라면 Files API나 클라우드 스토리지 연동이 낫다. 공개된 제품 매뉴얼 URL 세 개를 비교해야 한다면 URL Context가 편하다. 사내 비공개 계약서라면 공개 URL Context로 넘기지 말고, 권한이 통제된 저장소와 서버 측 업로드 흐름을 설계해야 한다.

여기서 특히 중요한 차이가 있다. URL Context는 "웹에 있는 자료를 읽어 근거로 삼는 도구"에 가깝다. 반면 외부 URL 파일 입력은 "파일 바이트를 모델 입력으로 가져오는 경로"에 가깝다. 둘 다 URL을 쓰지만, URL Context는 검색·분석 도구의 성격이 강하고 응답 메타데이터에서 어떤 URL을 가져왔는지 확인할 수 있다.3

작은 파일은 inline으로 시작한다

처음 실험할 때는 inline data가 가장 쉽다. 파일을 바이트로 읽어 Part에 넣고, 그 옆에 텍스트 지시를 붙이면 된다. 예를 들어 제품 화면 캡처에서 UX 문제를 찾는 코드는 다음처럼 시작할 수 있다.

import os
from pathlib import Path
from google import genai
from google.genai import types

client = genai.Client(api_key=os.environ["GEMINI_API_KEY"])
image_bytes = Path("checkout_error.png").read_bytes()

response = client.models.generate_content(
    model="gemini-2.5-flash",
    contents=[
        types.Part.from_text(
            text=(
                "이 화면은 결제 실패 후 사용자에게 보이는 오류 화면이다. "
                "사용자가 다음 행동을 이해하기 어려운 지점을 찾아라. "
                "출력은 JSON으로 하고 fields는 summary, issues, suggested_copy로 구성하라."
            )
        ),
        types.Part.from_bytes(data=image_bytes, mime_type="image/png"),
    ],
    config=types.GenerateContentConfig(
        response_mime_type="application/json",
        temperature=0.2,
    ),
)

print(response.text)

이 코드는 짧지만 중요한 원칙을 담고 있다. 파일만 던지는 것이 아니라 "이 화면이 어떤 업무 상황에서 쓰이는지"를 텍스트로 설명한다. 모델은 이미지를 볼 수 있지만, 그 이미지가 결제 오류 화면인지, QA 테스트 화면인지, 디자인 시안인지 스스로 확정할 수 없다. 멀티모달 프롬프트에서 텍스트 지시는 렌즈다. 같은 이미지를 보더라도 렌즈가 "마케팅 문구 평가"인지 "접근성 점검"인지 "버그 재현"인지에 따라 답이 완전히 달라진다.

inline 방식은 단순하지만 요청 크기와 지연시간이 커질 수 있다. 서버리스 함수에서 큰 파일을 base64로 넣으면 메모리 사용량이 예상보다 빨리 증가한다. 또한 같은 파일에 대해 사용자가 후속 질문을 여러 번 하는 서비스라면 매번 같은 바이트를 보내는 셈이므로 비용과 네트워크 시간이 낭비된다. 이때는 Files API로 전환한다.

큰 파일과 반복 질의는 Files API로 보낸다

Files API는 파일을 먼저 업로드한 뒤, 응답으로 받은 파일 URI를 generateContent 요청에 참조하는 방식이다. 공식 문서에서 Files API는 이미지, 오디오, 비디오, PDF 등 다양한 멀티모달 MIME 타입을 모델 입력으로 사용할 수 있는 경로로 설명된다.4 파일은 API가 장기간 보관하는 지식 베이스가 아니라, 모델 호출을 위한 임시 입력 자산이다. 따라서 파일 수명, 프로젝트 단위 저장 용량, 파일당 크기 제한은 항상 공식 문서의 최신 값을 확인해야 한다.

다음은 PDF 계약서를 업로드하고 활성화 상태를 확인한 뒤 분석하는 흐름이다.

import os
import time
from google import genai
from google.genai import types

client = genai.Client(api_key=os.environ["GEMINI_API_KEY"])

uploaded = client.files.upload(
    file="sample_contract.pdf",
    config=types.UploadFileConfig(
        mime_type="application/pdf",
        display_name="sample_contract.pdf",
    ),
)

# 영상·오디오·대형 문서는 처리 시간이 걸릴 수 있다.
while getattr(uploaded.state, "name", uploaded.state) == "PROCESSING":
    time.sleep(2)
    uploaded = client.files.get(name=uploaded.name)

if getattr(uploaded.state, "name", uploaded.state) == "FAILED":
    raise RuntimeError("파일 처리에 실패했습니다. 파일 손상, MIME 타입, 크기 제한을 확인하세요.")

prompt = """
너는 기업 법무팀을 돕는 계약 검토 보조자다.
다음 PDF 계약서를 읽고 위험 조항, 누락 정보, 법무팀 확인 질문을 JSON으로 정리하라.
문서에 없는 내용은 추정하지 말고 '확인 필요'라고 써라.
"""

response = client.models.generate_content(
    model="gemini-2.5-pro",
    contents=[
        prompt,
        types.Part.from_uri(file_uri=uploaded.uri, mime_type=uploaded.mime_type),
    ],
    config=types.GenerateContentConfig(
        response_mime_type="application/json",
        temperature=0.1,
    ),
)

print(response.text)

계약서 분석에는 gemini-2.5-pro처럼 추론 품질이 높은 모델이 어울린다. 하지만 모든 PDF 분석을 Pro로 처리할 필요는 없다. 약관에서 조항 제목만 추출하거나 영수증에서 금액·날짜만 뽑는 작업은 Flash 계열로도 충분할 수 있다. 실무에서는 샘플 50~100개를 뽑아 Pro와 Flash의 정확도, 지연시간, 비용을 비교한 뒤 라우팅 규칙을 만든다. 예를 들어 "20페이지 이하의 정형 문서 추출은 Flash, 비정형 계약 리스크 분석은 Pro"처럼 나누는 식이다.

Files API를 쓸 때 반드시 저장해야 하는 메타데이터도 있다. 파일 이름만 저장하면 안 된다. 파일 URI, MIME 타입, 업로드 시각, 만료 시각, 원본 저장소 경로, 원본 해시를 함께 저장해야 한다. 그래야 "이 URI가 만료되었는지", "같은 파일을 다시 업로드할 필요가 있는지", "사용자가 삭제 요청을 했을 때 원본과 임시 파일을 어떻게 처리할지"를 판단할 수 있다.

URL Context는 웹 자료 분석에 강하지만 만능 브라우저는 아니다

URL Context는 Gemini가 프롬프트에 포함된 URL의 콘텐츠를 가져와 답변에 활용하도록 하는 기능이다. 웹페이지, PDF, 이미지, JSON, CSV 같은 자료를 분석할 때 유용하다. 예를 들어 경쟁사 제품 문서 세 개를 비교하거나, 공개 보고서의 핵심 수치를 요약하거나, 특정 기술 문서 URL을 기준으로 설정 절차를 생성할 수 있다.

import os
from google import genai
from google.genai import types

client = genai.Client(api_key=os.environ["GEMINI_API_KEY"])

response = client.models.generate_content(
    model="gemini-2.5-flash",
    contents=(
        "다음 두 제품 문서 URL을 비교해 공통 기능, 차이점, "
        "마이그레이션 시 주의점을 표로 정리하라.n"
        "URL 1: https://example.com/product-a-docsn"
        "URL 2: https://example.com/product-b-docs"
    ),
    config=types.GenerateContentConfig(
        tools=[types.Tool(url_context=types.UrlContext())],
        temperature=0.2,
    ),
)

print(response.text)
print(response.candidates[0].url_context_metadata)

URL Context에는 운영상 중요한 제약이 있다. 공식 문서는 요청당 처리 가능한 URL 수, 지원 콘텐츠 유형, 접근 가능한 URL 조건, 가져온 콘텐츠가 토큰 사용량에 포함된다는 점을 설명한다.3 페이월, 로그인 뒤에만 보이는 페이지, 접근 차단된 리소스, 동적으로 렌더링되는 일부 콘텐츠는 기대대로 읽히지 않을 수 있다. URL을 넣었다고 해서 브라우저 자동화 도구처럼 모든 버튼을 누르고 로그인하고 화면을 탐색하는 것은 아니다.

실무에서는 URL Context 응답을 그대로 믿기보다 메타데이터를 확인해야 한다. 어떤 URL이 성공적으로 검색되었는지, 실패했는지, 안전성 검사에 걸렸는지 기록하라. 제품 문서 비교 서비스라면 응답 본문과 함께 url_context_metadata를 로그에 남겨야 나중에 "모델이 어느 문서를 실제로 읽고 답했는가"를 추적할 수 있다.

PDF 계약서: 조항 추출보다 "위험의 구조"를 묻는다

PDF는 멀티모달 처리에서 가장 자주 등장하는 입력이다. PDF는 단순 텍스트 파일처럼 보이지만 실제로는 텍스트, 표, 이미지, 도장, 주석, 스캔 페이지가 뒤섞인 컨테이너다. Gemini는 PDF의 텍스트뿐 아니라 페이지의 시각적 요소까지 함께 이해할 수 있으므로 표, 차트, 서명란, 레이아웃을 활용하는 작업에 강하다.1

계약서 분석 프롬프트는 "요약해줘"에서 멈추면 안 된다. 좋은 프롬프트는 독자의 의사결정을 돕는 구조로 출력한다.

너는 SaaS 구매 계약을 검토하는 조달·법무 보조자다.
문서 전체를 읽고 다음 관점으로 분석하라.

1. 비용과 갱신: 자동 갱신, 가격 인상, 환불 제한
2. 데이터와 보안: 고객 데이터 처리, 하위 처리자, 침해 통지
3. 책임과 면책: 손해배상 한도, 간접손해 제외, 일방 면책
4. 해지와 이전: 해지권, 데이터 반환, 전환 지원

출력은 JSON으로 한다.
각 위험 항목에는 조항 위치, 위험 설명, 협상 제안, 확인 질문을 포함한다.
문서에서 확인되지 않는 내용은 절대 지어내지 말고 "문서상 확인 불가"로 표시한다.

여기서 "문서상 확인 불가"라는 지시가 중요하다. 멀티모달 모델은 표나 이미지 속 내용을 읽을 수 있지만, 흐릿한 스캔본이나 잘린 페이지에서는 오독할 수 있다. 계약 업무에서는 그럴듯한 답변보다 불확실성 표시가 더 중요하다. 따라서 출력 스키마에 confidence, evidence, needs_human_review 같은 필드를 넣는 것이 좋다.

긴 계약서가 들어오면 통째로 넣을지 나눌지 판단해야 한다. 단일 계약서 전체에서 상호 참조를 찾아야 한다면 긴 컨텍스트가 유리하다. 예를 들어 3조의 정의 조항, 12조의 책임 제한, 부속서의 보안 조항이 서로 연결되어 있다면 청크로 쪼갤수록 맥락이 약해진다. 반대로 수천 건의 계약서 중 사용자의 질문과 관련된 몇 건만 찾아 답하는 서비스라면 RAG 구조가 더 적합하다.

스크린샷과 이미지: 보이는 것과 판단한 것을 분리한다

이미지 분석에서는 모델이 "무엇을 봤는지"와 "무엇을 추론했는지"를 분리해야 한다. 화면 캡처를 보고 "사용자가 혼란스러워한다"고 말하는 것은 추론이다. 실제로 보이는 것은 "오류 메시지가 빨간색으로 표시되어 있고, 재시도 버튼이 접힌 영역 아래에 있으며, 결제 수단 변경 링크가 작다" 같은 관찰이다.

UX 진단 프롬프트는 다음처럼 구성하면 안정적이다.

이 이미지는 모바일 앱의 회원가입 화면이다.
먼저 이미지에서 직접 관찰 가능한 요소만 나열하라.
그다음 관찰을 근거로 사용성 문제를 추론하라.
마지막으로 개선안을 제안하라.

출력 형식:
{
  "observations": ["이미지에서 직접 보이는 사실"],
  "inferences": ["관찰을 근거로 한 해석"],
  "risks": ["전환율 또는 접근성 측면의 위험"],
  "recommendations": ["구체적 개선안"]
}

이 분리만으로도 환각을 상당히 줄일 수 있다. 모델이 이미지에 없는 버튼을 언급하거나, 보이지 않는 사용자의 의도를 단정하면 observations 단계에서 드러난다. 운영 시스템에서는 이미지 분석 결과를 바로 자동 수정으로 연결하지 말고, 관찰과 추론을 분리한 뒤 사람이 검토할 수 있게 만드는 것이 안전하다.

이미지 품질도 중요하다. 너무 작은 해상도, 흐릿한 글자, 잘린 화면, 과도한 압축은 모델의 판단을 흔든다. 업로드 전에 이미지 방향을 바로잡고, 개인정보가 들어간 영역을 마스킹하고, 필요한 화면만 잘라 보내는 전처리가 품질과 비용을 동시에 개선한다.

회의 녹음: 전사, 요약, 액션 아이템을 한 번에 요구하지 않는다

오디오는 텍스트와 다르게 시간축이 있다. 회의 녹음을 분석할 때 "요약해줘"라고만 요청하면 중요한 결정 사항과 잡담이 섞일 수 있다. 좋은 설계는 단계적이다. 첫 요청에서는 구간별 요약을 만들고, 두 번째 요청에서는 결정 사항과 액션 아이템을 추출하며, 세 번째 요청에서는 특정 부서나 담당자 기준으로 재구성한다.

짧은 녹음이라면 inline으로 보낼 수 있지만, 긴 회의 파일은 Files API가 낫다. 프롬프트는 다음처럼 시간 정보를 요구한다.

이 오디오는 제품 주간 회의 녹음이다.
다음 순서로 분석하라.

1. 5분 단위로 주제 변화를 요약한다.
2. 명시적으로 결정된 사항만 decisions에 넣는다.
3. 담당자, 마감일, 작업 내용을 알 수 있는 항목만 action_items에 넣는다.
4. 담당자나 마감일이 불명확하면 null로 둔다.
5. 확실하지 않은 발언은 uncertain_notes에 넣는다.

출력은 JSON으로 한다.

오디오 분석에서 자주 생기는 문제는 화자 구분이다. 모델이 화자를 완벽하게 구분한다고 가정하면 안 된다. 회의실 마이크 하나로 여러 명이 동시에 말하면 화자 추정이 흔들린다. 따라서 "김지훈이 말했다"처럼 사람 이름을 확정해야 하는 업무에서는 참석자 목록, 발언 순서, 회의록 초안 같은 보조 정보를 함께 제공하고, 그래도 불확실하면 speaker_unknown_1처럼 표시하도록 지시한다.

강의 영상과 데모 영상: 타임스탬프가 품질이다

영상은 이미지와 오디오가 시간축 위에서 결합된 입력이다. 강의 영상에서는 슬라이드 내용과 강사의 말이 서로 보완된다. 제품 데모 영상에서는 화면 변화, 클릭 흐름, 내레이션이 함께 의미를 만든다. Gemini는 이런 교차 모달 분석에 강하지만, 영상은 토큰과 처리 시간이 빨리 커지는 입력이다. 긴 영상은 한 번에 넣을 수 있더라도 항상 좋은 선택은 아니다.

강의 영상 분석 프롬프트는 타임스탬프를 중심으로 설계한다.

이 영상은 60분짜리 기술 강의다.
강의 내용을 학습 콘텐츠로 재구성하라.

출력:
{
  "chapters": [
    {
      "start_time": "00:00",
      "end_time": "05:30",
      "title": "구간 제목",
      "summary": "핵심 설명",
      "key_terms": ["용어"],
      "quiz_questions": ["복습 질문"]
    }
  ],
  "important_slides": [
    {
      "time": "12:40",
      "visible_content": "슬라이드에서 보이는 내용",
      "why_important": "중요한 이유"
    }
  ]
}

제품 데모 영상이라면 목적이 다르다. "기능 목록을 뽑아줘"보다 "사용자가 어떤 순서로 어떤 UI를 조작하는지, 실패 지점이 있는지, 문서화할 단계는 무엇인지"를 물어야 한다. QA 자동화에 쓰려면 타임스탬프, 화면 요소, 사용자 행동, 기대 결과, 실제 결과를 분리해 출력하게 한다.

영상 파일은 크기 제한에 걸리기 쉽다. 파일 하나가 제한을 넘으면 시간 기준으로 자르고, 각 조각에 약간의 오버랩을 둔다. 예를 들어 30분 영상을 10분 단위로 자를 때 30초씩 겹치게 하면 장면이 경계에서 끊기는 문제를 줄일 수 있다. 단, 조각별 분석 결과를 합칠 때 중복 구간이 두 번 집계되지 않도록 후처리해야 한다.

코드 저장소: 전체를 넣기 전에 쓰레기를 버린다

긴 컨텍스트가 커지면서 "코드베이스 전체를 넣고 분석한다"는 접근이 가능해졌다. Gemini의 긴 컨텍스트 문서는 큰 텍스트, 오디오, 비디오, 코드 같은 입력을 한 번에 넣는 새로운 사용 사례를 설명한다.5 하지만 코드 저장소를 통째로 넣는다는 말은 node_modules, 빌드 산출물, 잠금 파일, 이미지 에셋, 테스트 스냅샷까지 모두 넣는다는 뜻이 아니다. 좋은 코드 분석은 "전체"보다 "관련 전체"를 만드는 일이다. 먼저 제외 규칙을 만든다.

분석에서 제외할 항목:
- node_modules, .next, dist, build, coverage
- 이미지·폰트·바이너리 파일
- 자동 생성된 API 클라이언트
- lock 파일이 질문과 무관한 경우
- 비밀키, 인증서, 운영 설정 파일

그다음 파일을 의존성 순서로 정리한다. 타입 정의, 설정, 공통 유틸리티, 핵심 도메인 로직, API 라우트, UI 컴포넌트 순서가 좋다. 모델은 긴 컨텍스트를 처리할 수 있지만, 사람이 읽기 쉬운 구조로 넣을수록 답변 품질이 올라간다.

코드 저장소 분석 프롬프트는 구체적인 질문을 담아야 한다.

아래는 결제 서비스 저장소의 주요 파일이다.
목표는 "환불 요청이 승인된 뒤에도 주문 상태가 PAID로 남는 버그"의 원인을 찾는 것이다.

분석 순서:
1. refund 요청이 들어오는 엔드포인트를 찾는다.
2. 주문 상태를 변경하는 함수들을 추적한다.
3. 트랜잭션 경계와 이벤트 발행 순서를 확인한다.
4. 테스트가 빠진 경로를 지적한다.
5. 수정안을 파일별 패치 계획으로 제시한다.

확실하지 않은 부분은 추정하지 말고 추가로 필요한 파일명을 요청하라.

이런 프롬프트는 "이 코드 어때?"보다 훨씬 강하다. 모델에게 수사 목표와 추적 경로를 주기 때문이다. 코드 분석에서 긴 컨텍스트가 특히 강한 지점은 파일 간 패턴이다. 인증 체크가 일부 라우트에만 빠졌는지, 같은 도메인 이벤트가 서비스마다 다르게 처리되는지, 마이그레이션 후 타입 정의와 SQL 스키마가 어긋났는지 같은 문제는 한두 파일만 봐서는 찾기 어렵다.

토큰 카운팅: 긴 컨텍스트의 속도계를 보라

긴 컨텍스트를 쓰면 "들어간다"와 "좋다"를 혼동하기 쉽다. 100만 토큰 컨텍스트에 들어간다고 해서 매번 100만 토큰을 보내도 된다는 뜻은 아니다. 토큰은 비용이며 지연시간이며, 모델이 주의를 나눠야 하는 정보량이다. 따라서 큰 파일을 다룰 때는 먼저 토큰을 세어야 한다.

from google import genai
from google.genai import types
import os

client = genai.Client(api_key=os.environ["GEMINI_API_KEY"])

file = client.files.upload(
    file="annual_report.pdf",
    config=types.UploadFileConfig(mime_type="application/pdf"),
)

prompt = "이 연차보고서에서 매출 성장, 주요 리스크, 투자 계획을 요약하라."

count = client.models.count_tokens(
    model="gemini-2.5-flash",
    contents=[
        prompt,
        types.Part.from_uri(file_uri=file.uri, mime_type=file.mime_type),
    ],
)

print(count.total_tokens)

토큰 수를 보면 설계 판단이 가능해진다. 예를 들어 600페이지 보고서 하나가 컨텍스트 예산 안에 들어가고 질문이 문서 전체의 상호 관계를 요구한다면 통째로 넣는 것이 단순하고 강력하다. 반대로 5만 개 고객지원 티켓 중 사용자의 질문과 관련된 몇십 개만 필요하다면 모든 티켓을 매번 넣는 것은 낭비다. 그때는 임베딩과 RAG로 전환한다.

상황권장 전략이유
단일 문서 전체를 읽고 상호 참조를 찾아야 한다긴 컨텍스트에 통째로 입력조항·표·부록 간 관계를 잃지 않는다
긴 영상이나 녹음을 구간별로 요약한다시간 기준 청크 + 오버랩토큰과 지연시간을 제어한다
많은 문서 중 관련 문서만 찾아 답한다RAG매 요청마다 전체 문서를 넣지 않는다
같은 큰 문서를 놓고 여러 번 대화한다Files API 재사용 또는 컨텍스트 캐싱 검토반복 업로드와 반복 입력 비용을 줄인다
정확한 수치 추출이 중요하다구조화 출력 + 근거 필드 + 검증 로직그럴듯한 요약보다 재현성이 중요하다

긴 컨텍스트는 RAG의 대체재가 아니라 선택지다. 질문이 "이 계약서 전체에서 상충되는 조항을 찾아라"라면 긴 컨텍스트가 좋다. 질문이 "우리 회사의 모든 계약서 중 이 고객에게 적용되는 SLA를 찾아라"라면 RAG가 좋다. 질문의 범위가 하나의 큰 문서인가, 많은 문서의 검색인가를 먼저 구분하라.

멀티모달 환각은 "근거 없는 자신감"으로 나타난다

텍스트 환각은 틀린 문장을 만들어내는 형태로 나타난다. 멀티모달 환각은 조금 더 교묘하다. 이미지에 없는 버튼을 봤다고 하거나, 표의 숫자를 잘못 읽거나, 영상에서 말하지 않은 내용을 자막처럼 요약하거나, 흐릿한 도장을 특정 회사명으로 단정한다. 사용자는 "모델이 직접 봤다"고 생각하기 때문에 텍스트 환각보다 더 쉽게 믿는다.

이를 줄이려면 출력 형식을 설계해야 한다. 모든 분석 결과에 근거와 불확실성을 넣는다.

{
  "claim": "확인한 내용",
  "evidence_type": "text|image|audio|video|layout",
  "evidence_location": "페이지 4, 00:12:33, 화면 오른쪽 상단 등",
  "confidence": "high|medium|low",
  "needs_human_review": true
}

또한 모델에게 "보이는 사실"과 "해석"을 분리하게 한다. 문서에서는 원문 위치나 페이지를 요구하고, 영상에서는 타임스탬프를 요구하며, 오디오에서는 불확실한 화자를 분리한다. 숫자나 날짜가 중요한 업무에서는 모델 출력만 믿지 말고 별도 파서, 정규식, 데이터베이스 검증, 사람 검토를 연결한다.

운영 리스크: 파일 수명, 권한, 비용, 품질

멀티모달 시스템에서 장애는 모델보다 파일 주변에서 더 자주 난다. 파일 URI가 만료되어 후속 질문이 실패한다. 사용자가 업로드한 PDF가 너무 커서 413 오류가 난다. URL Context가 접근할 수 없는 비공개 페이지를 읽으려다 빈 답을 만든다. 영상 코덱이 지원되지 않아 처리 상태가 FAILED가 된다. 회의 녹음에 개인정보가 들어 있는데 로그에 원문 파일명이 남는다.

따라서 운영 체크리스트를 코드와 함께 만들어야 한다.

리스크증상대응
파일 만료어제 업로드한 파일 URI로 후속 질문 실패원본 저장소를 따로 두고 만료 시 재업로드한다
크기 제한 초과400 또는 413 계열 오류inline 대신 Files API, 청크 분할, 압축, 해상도 조정을 사용한다
URL 접근 실패URL Context 결과가 비어 있거나 오래된 내용URL 검색 메타데이터를 확인하고 실패 시 직접 업로드 경로를 제공한다
미디어 품질 저하표·자막·작은 글씨 오독해상도, 방향, 자르기, 노이즈 제거를 전처리한다
비용 폭증긴 파일 반복 질의로 입력 토큰 급증토큰 카운팅, 캐싱, RAG 전환, 모델 라우팅을 적용한다
민감정보 노출로그·파일명·프롬프트에 개인정보 저장마스킹, 접근권한, 보존기간, 감사 로그를 설계한다

특히 Files API를 장기 저장소처럼 쓰면 안 된다. 파일이 일정 기간 후 만료되는 구조라면 원본은 별도의 영속 저장소에 보관하고, Gemini에는 분석 시점에 필요한 사본이나 URI만 제공해야 한다. 반대로 민감정보가 많은 조직에서는 원본 저장소와 Gemini 입력 경로 모두에 접근 제어, 암호화, 삭제 정책을 적용해야 한다.

실전 설계: 고객지원 멀티모달 분석 파이프라인

이제 하나의 예로 정리해 보자. 고객지원팀이 다음 입력을 한 번에 받는다고 가정한다. 사용자가 작성한 문의 텍스트, 오류 화면 스크린샷, 앱 로그 일부, 상담 녹음 파일이다. 목표는 문의 유형을 분류하고, 원인을 추정하고, 상담원이 보낼 답변 초안을 만드는 것이다.

좋은 파이프라인은 모든 것을 한 요청에 밀어 넣지 않는다. 먼저 입력을 분류한다. 문의 텍스트와 짧은 로그는 inline 텍스트로 넣는다. 스크린샷은 작은 파일이면 inline 이미지로 넣는다. 상담 녹음이 길면 Files API로 업로드한다. 그런 다음 첫 요청에서 구조화된 사건 카드만 만든다.

다음 고객지원 자료를 분석해 incident_card JSON을 생성하라.
자료에는 고객 문의 텍스트, 오류 화면 이미지, 앱 로그, 상담 녹음이 포함된다.

출력:
{
  "issue_type": "billing|login|bug|how_to|other",
  "customer_impact": "고객이 겪는 문제",
  "visible_evidence": ["이미지·로그·녹음에서 확인되는 사실"],
  "likely_cause": "가능성 높은 원인. 불확실하면 null",
  "missing_info": ["추가로 물어볼 정보"],
  "agent_reply_draft": "상담원 답변 초안",
  "escalation_required": true
}

두 번째 요청에서는 incident_card와 내부 정책 문서 또는 FAQ를 연결한다. 이 부분은 도구 호출과 그라운딩, RAG로 확장된다. 중요한 것은 멀티모달 입력 분석과 지식 검색을 무리하게 한 단계에 합치지 않는 것이다. 먼저 "무슨 일이 일어났는지"를 정리하고, 그다음 "우리 회사 정책상 어떻게 답해야 하는지"를 찾는 편이 안정적이다.

긴 컨텍스트 설계 원칙

이 장에서 배운 내용을 하나의 문장으로 줄이면 이렇다. Gemini의 멀티모달 능력은 입력을 많이 넣는 능력이 아니라, 다양한 증거를 같은 맥락에서 추론하는 능력이다. 이 능력을 실무 시스템으로 바꾸려면 파일 입력 경로, 토큰 예산, 출력 스키마, 검증 흐름을 함께 설계해야 한다.

작은 파일은 inline으로 빠르게 시작하라. 큰 파일과 반복 질의는 Files API나 클라우드 저장소 연동을 사용하라. 공개 웹 자료 분석에는 URL Context를 쓰되 접근 실패와 크기 제한을 로그로 확인하라. PDF, 이미지, 오디오, 영상, 코드 저장소마다 프롬프트의 목적과 출력 형식을 다르게 설계하라. 긴 문서를 통째로 넣을지, 청크로 나눌지, RAG로 전환할지는 질문의 범위와 반복성을 기준으로 판단하라.

다음 장에서는 이 멀티모달 입력 위에 도구 호출을 얹는다. 파일을 읽는 모델에서 한 걸음 더 나아가, 검색하고, 함수를 호출하고, 코드를 실행하고, 외부 시스템과 연결되는 Gemini 애플리케이션을 설계할 것이다.

참고

1Document understanding - Gemini API

2Increased file size limits and expanded inputs support in Gemini API

3URL context - Gemini API

4File input methods - Gemini API

5Long context - Gemini API

책 목록

제미나이 공략집

전체 목차 보기

이 노트는 보도·비평·교육·연구 목적으로 작성되었습니다. 저작권 문의가 있으시면 에서 알려주세요.

틸노트는 사용자가 AI를 활용해 노트를 쉽게 작성하거나 편집할 수 있는 서비스입니다.

AI 스퀘어 단톡방에 함께해요

AI 활용과 틸노트 사용 팁을 나누는 오픈채팅방입니다. 입장 비밀번호는 tilnote입니다.

참여하기