Gemini API에 조용히 새로운 표준이 생겼습니다. generateContent는 이제 구식입니다. Google은 모든 새 프로젝트에 Interactions API를 권장하고, 6월 8일엔 기존 스키마를 완전 제거합니다.
핵심 요약 → Interactions API: Gemini 3.5 Flash와 함께 베타 출시, 모든 신규 프로젝트 기본 권장 → 핵심 기능: previous_interaction_id — 대화 히스토리를 클라이언트가 아닌 Google 서버가 관리 → OpenAI Responses API의 서버사이드 히스토리 패턴과 동일한 개념 → 브레이킹 체인지: outputs 배열 → steps 배열 (5월 26일 기본값, 6월 8일 레거시 완전 제거) → 상태 저장: 유료 플랜 55일, 무료 플랜 1일 보관 → background=true 지원 — 비동기 장시간 에이전트 실행 → 현재 미지원: Computer Use, 비디오 메타데이터, 명시적 캐싱, Remote MCP → generateContent는 계속 지원되지만 신규 기능은 Interactions API에만 추가
generateContent vs Interactions API — 무엇이 다른가
# 두 API의 근본적 차이
generateContent (기존):
클라이언트 → 매 요청마다 전체 대화 히스토리 전송
→ 개발자가 히스토리 배열 직접 관리
→ 대화가 길어질수록 입력 토큰 폭발
→ 서버는 무상태(stateless) — 이전 요청을 모름
Interactions API (신규):
클라이언트 → 첫 요청 → 서버가 interaction_id 반환
→ 이후 요청에 previous_interaction_id만 전달
→ 서버가 히스토리 저장·관리·추론 컨텍스트 유지
→ 개발자 코드에서 히스토리 배열 사라짐
OpenAI Responses API와의 비교:
OpenAI: previous_response_id → 이전 응답 참조
Google: previous_interaction_id → 이전 인터랙션 참조
→ 개념 동일, API 형태만 다름
1. 핵심 개념 — Interaction이란 무엇인가
# Interaction: 하나의 완전한 대화 턴 (세션 레코드)
# 각 Interaction은 다음을 포함:
# - 사용자 입력
# - 모델의 추론 과정 (Thought)
# - 도구 호출 및 결과
# - 최종 응답
# 모든 이것이 "steps" 배열로 구조화됨
# 예시: 검색 도구를 사용하는 Interaction의 steps
steps = [
{"type": "user_input", "content": "최신 Gemini 가격은?"},
{"type": "thought", "content": "검색이 필요한 질문..."}, # 추론 과정
{"type": "google_search_call", "query": "Gemini API pricing 2026"},
{"type": "google_search_result","content": "검색 결과..."},
{"type": "model_output", "content": "Gemini 3.5 Flash는 $1.50/1M..."},
]
# ← 이게 6월 8일 이후 유일한 응답 형태
# 이전: response.outputs[0].text (flat 배열)
# 이후: interaction.steps[-1].content[0].text (typed 배열)
2. 실전 코드 — generateContent에서 Interactions API로
import google.generativeai as genai
# SDK 버전 확인 필수: google-genai >= 2.0.0
# pip install -U google-genai
client = genai.Client(api_key="YOUR_API_KEY")
# ══════════════════════════════════════
# [기존] generateContent — 히스토리 직접 관리
# ══════════════════════════════════════
history = [] # 개발자가 직접 관리
def chat_old(user_message: str) -> str:
history.append({"role": "user", "content": user_message})
response = client.models.generate_content(
model="gemini-3.5-flash",
contents=history, # 매번 전체 히스토리 전송
# → 대화 10턴 = 10턴치 토큰 매번 재전송
)
history.append({
"role": "model",
"content": response.text
})
return response.text
# 문제:
# - 히스토리 관리 코드 직접 작성
# - Thought Preservation 수동 처리 필요
# - 대화 길어질수록 입력 토큰 선형 증가
# ══════════════════════════════════════
# [신규] Interactions API — 서버사이드 히스토리
# ══════════════════════════════════════
previous_id = None # 이게 전부
def chat_new(user_message: str) -> str:
global previous_id
interaction = client.interactions.create(
model="gemini-3.5-flash",
contents=user_message, # 현재 메시지만 전송
previous_interaction_id=previous_id, # 이전 컨텍스트 참조
config={
"thinking_level": "medium",
"tools": [{"google_search": {}}]
}
# store=True (기본값) → 서버가 55일 보관
)
previous_id = interaction.id # 다음 턴을 위해 저장
# 응답 읽기 (새 steps 스키마)
for step in interaction.steps:
if step.type == "model_output":
return step.content[0].text
# 장점:
# - 히스토리 관리 코드 = 0줄
# - Thought Preservation 자동 처리
# - 서버가 추론 컨텍스트 유지 → 더 일관된 멀티턴 응답
3. 브레이킹 체인지 — 6월 8일 데드라인
Gemini Interactions API를 사용 중이라면 6월 8일에 코드가 깨집니다. Google은 응답 객체의 outputs 배열을 폐기하고 타입이 있는 steps 배열로 교체하며, 레거시 스키마는 그 날짜에 영구 제거됩니다. 연장 공지는 없습니다.
# ══════════════════════════════════════════════
# 브레이킹 체인지 1: outputs → steps 스키마 변경
# ══════════════════════════════════════════════
# ❌ 레거시 (6월 8일 완전 삭제)
text = interaction.outputs[0].text
# → June 8 이후 AttributeError
# ✅ 신규 스키마
for step in interaction.steps:
if step.type == "model_output":
text = step.content[0].text
break
# steps type 목록:
# "user_input" - 사용자 입력
# "thought" - 모델 추론 과정 (Thought Preservation)
# "model_output" - 최종 응답
# "function_call" - 커스텀 함수 호출
# "function_result" - 함수 실행 결과
# "google_search_call" - 검색 도구 호출
# "google_search_result"- 검색 결과
# ══════════════════════════════════════════════
# 브레이킹 체인지 2: response_format 변경
# ══════════════════════════════════════════════
# ❌ 레거시
interaction = client.interactions.create(
model="gemini-3.5-flash",
contents="JSON으로 분석해줘",
response_mime_type="application/json", # 삭제됨
)
# ✅ 신규 (polymorphic response_format)
interaction = client.interactions.create(
model="gemini-3.5-flash",
contents="JSON으로 분석해줘",
config={
"response_format": {
"type": "json_schema",
"json_schema": {
"name": "analysis_result",
"schema": {
"type": "object",
"properties": {
"summary": {"type": "string"},
"score": {"type": "number"}
}
}
}
}
}
)
# ══════════════════════════════════════════════
# 마이그레이션 헤더로 전환 시점 제어
# ══════════════════════════════════════════════
import httpx
# 5월 26일 이전: 옵트인으로 새 스키마 테스트
headers = {"Api-Revision": "2026-05-20"} # 신규 스키마
# headers = {"Api-Revision": "2026-05-07"} # 레거시 (6월 8일까지만)
# 5월 26일 이후: 기본값이 신규 스키마로 전환
# 6월 8일: 레거시 완전 제거, 헤더 옵트아웃 불가
4. 함수 호출 — steps 기반 새 패턴
# 커스텀 함수 호출 (Function Calling) — 새 스키마
def get_weather(city: str) -> dict:
# 실제 날씨 API 호출
return {"city": city, "temperature": 22, "condition": "맑음"}
interaction = client.interactions.create(
model="gemini-3.5-flash",
contents="서울 날씨 알려줘",
config={
"tools": [{
"function_declarations": [{
"name": "get_weather",
"description": "도시의 현재 날씨를 가져옵니다",
"parameters": {
"type": "object",
"properties": {
"city": {"type": "string", "description": "도시명"}
},
"required": ["city"]
}
}]
}]
}
)
# 함수 호출이 필요한 경우 steps에서 탐지
for step in interaction.steps:
if step.type == "function_call":
func_name = step.name # "get_weather"
func_args = step.args # {"city": "서울"}
# 함수 실행
result = get_weather(**func_args)
# 결과를 다음 Interaction으로 전달
interaction = client.interactions.create(
model="gemini-3.5-flash",
contents={
"function_response": {
"name": func_name,
"response": result
}
},
previous_interaction_id=interaction.id
)
break
5. background=true — 비동기 장시간 에이전트 패턴
# 장시간 실행 에이전트 — background 모드
# 시나리오: 대용량 코드베이스 분석 (수 분 소요)
interaction = client.interactions.create(
model="gemini-3.5-flash",
contents="이 저장소의 보안 취약점 전체 분석해줘",
config={
"thinking_level": "high",
"background": True, # 비동기 실행
# background=True는 store=True 필수 (기본값)
},
files=["gs://my-bucket/entire-codebase.zip"]
)
# 즉시 반환 — 아직 완료 안 됨
print(interaction.status) # "running"
print(interaction.id) # "int_xxxxxxxxx" — 나중에 결과 조회용
# 폴링 대신 Webhook 사용 (2026.05 신규 출시)
# → 완료 시 설정한 엔드포인트로 POST 콜백
# → 수 분 걸리는 에이전트도 서버리스 환경에서 처리 가능
# 결과 조회
completed = client.interactions.get(interaction.id)
if completed.status == "completed":
for step in completed.steps:
if step.type == "model_output":
print(step.content[0].text)
6. 서버사이드 히스토리의 실제 비용 이점
# 10턴 대화 기준 토큰 비용 비교
# 가정: 턴당 평균 2,000 입력 토큰 + 500 출력 토큰
# [generateContent — 클라이언트 히스토리]
# 1턴: 2,000 입력
# 2턴: 4,000 입력 (1턴 히스토리 포함)
# 3턴: 6,000 입력 ...
# 10턴: 20,000 입력
# 총 입력 토큰: 2000+4000+...+20000 = 110,000 토큰
# [Interactions API — 서버사이드 히스토리]
# 1턴: 2,000 입력
# 2턴: 2,000 입력 (previous_interaction_id만 참조)
# ...
# 10턴: 2,000 입력
# 총 입력 토큰: 20,000 토큰 (단, Thought Preservation 토큰 별도)
# 순수 입력 토큰 절감: 최대 82%
# 실제 절감: Thought Preservation 토큰 누적 감안 시 40~60% 절감
# 비용 계산 ($1.50/1M input)
traditional_cost = 110_000 / 1_000_000 * 1.50 # $0.165
interactions_cost = 20_000 / 1_000_000 * 1.50 # $0.030
# → 10턴 기준 82% 절감 (Thought 토큰 제외 순수 계산)
7. 현재 Interactions API 미지원 기능 — 전환 전 확인 필수
# generateContent에서 지원, Interactions API 미지원 (2026.05 기준)
❌ Computer Use
→ gemini-3-flash-preview 유지 필요
❌ 비디오 메타데이터 (video_metadata)
→ 클리핑 구간, 커스텀 프레임레이트 설정 불가
❌ 명시적 캐싱 (Explicit Caching)
→ previous_interaction_id를 통한 암시적 캐싱은 지원
❌ Remote MCP
→ "Coming Soon" 공식 예고
✅ 지원 기능
→ Google Search 그라운딩
→ URL Context
→ 코드 실행 (Code Execution)
→ 커스텀 함수 호출 (Function Calling)
→ 구조화 출력 (Structured Outputs)
→ 파일 검색 (File Search — 멀티모달 포함)
→ 스트리밍
→ 백그라운드 실행 + Webhook
마이그레이션 체크리스트 — 6월 8일 전 완료
# [BLOCKS] 미처리 시 코드 깨짐 항목
checklist = {
"1. SDK 업데이트": {
"action": "pip install -U google-genai # >= 2.0.0",
"why": "구 SDK는 Interactions API 미지원"
},
"2. 메서드 교체": {
"before": "client.models.generate_content()",
"after": "client.interactions.create()",
},
"3. 응답 읽기 수정": {
"before": "response.text",
"after": "interaction.steps[-1].content[0].text",
},
"4. 히스토리 관리 교체": {
"before": "client.chats.create() / 수동 history 배열",
"after": "previous_interaction_id=interaction.id",
},
"5. response_format 수정": {
"before": "response_mime_type='application/json'",
"after": "config={'response_format': {'type': 'json_schema', ...}}",
},
"6. 함수 호출 로직 수정": {
"before": "response.candidates[0].content.parts 탐색",
"after": "step.type == 'function_call' 탐색",
},
}
# [TUNE] 성능·비용 최적화 항목
tuning = {
"thinking_level 명시 설정": "기본값 medium — 워크로드에 맞게 low/medium/high 선택",
"store=False 검토": "히스토리 불필요한 단건 요청은 store=False로 비용 절감",
"background=True 활용": "수 분 걸리는 태스크는 비동기로 전환 + Webhook 연동",
}
# 자동 마이그레이션 도구 (Gemini CLI 사용 시)
# /gemini-interactions-api migrate my-app
# → 기계적 코드 변환 자동 처리
결론
✅ Interactions API로 전환해야 하는 이유
- 서버사이드 히스토리 = 멀티턴 입력 토큰 최대 82% 절감
- Thought Preservation 자동 처리 — 직접 구현 불필요
- 신규 기능(Webhook, 비동기 실행, mid-flight steering)은 Interactions API에만 추가
- 6월 8일 이후 레거시 스키마 완전 제거 — 미리 마이그레이션이 유일한 선택
✅ OpenAI Responses API와의 비교
- 개념 동일: 서버사이드 히스토리 → 클라이언트 상태 관리 불필요
- Google 차별점: background=true 비동기 실행, Webhook 네이티브 지원
- 아직 베타 — GA 전까지는 프로덕션 크리티컬 경로에 단독 의존 주의
❌ 아직 주의해야 할 것
- Computer Use 미지원 — gemini-3-flash-preview 병행 유지 필요
- 베타 상태 — 추가 브레이킹 체인지 가능성 존재 ("actively developing")
- store=True 기본값 — 민감 데이터 저장 정책 검토 필요 (55일 보관)
- Remote MCP 미지원 — 내부망 MCP 연결은 별도 방법 필요
관련 글
'Gemini' 카테고리의 다른 글
| Gemini Omni vs Veo 3.1 — Google이 비디오 모델을 두 개 운영하는 이유 (0) | 2026.05.28 |
|---|---|
| Gemini 3.5 Flash + Interactions API로 MCP 에이전트 만들기 — 완전 실전 가이드 (0) | 2026.05.28 |
| Gemini 3.5 Flash 출시 9일 — 실제 사용자들은 뭐라고 했나 (0) | 2026.05.28 |
| Gemini 3.5 Flash 가격 3배 인상의 전략적 의미 — Google이 Flash를 프리미엄으로 올린 이유 (0) | 2026.05.28 |
| 텍스트·이미지·오디오로 영상을 만든다 — Gemini Omni 완전 분석 (0) | 2026.05.27 |