
Gemini
기초세팅
- API 키 발급
- 라이브러리 설치
poetry add google-generativeai
poetry add google-genai
- 환경변수 설정
.env 파일에 GEMINI_API_KEY= ~ 추가
라이브러리 비교
| 비교 항목 | google-generativeai (구버전) | google-genai (신버전/추천) |
|---|
| 개발 상태 | 유지보수 모드 (Maintenance) | 활성 개발 (Active) |
| 목적 | Gemini Developer API 전용 | Gemini API 및 Vertex AI 통합 지원 |
| 호환성 | Google AI Studio 키만 사용 가능 | Google AI Studio 키 & Google Cloud Vertex AI 모두 지원 |
| 장점 | 레퍼런스(예제)가 많음 | 구조가 더 직관적이며 최신 기능 우선 적용 |
| 설치 명령어 | poetry add google-generativeai | poetry add google-genai |

google-generativeai
- Google의 Gemini 및 PaLM 모델과 같은 생성형 AI 모델을
- Python 애플리케이션에서 쉽게 사용할 수 있도록 제공되는 공식 클라이언트 라이브러리(SDK)
- Django 프로젝트에서 이 라이브러리를 사용하면
- 텍스트 생성, 채팅, 이미지 분석 등의 AI 기능을 API 형태로 서비스 가능
특징
- 간편한 설정
- API 키 하나로 빠르게 인증하고 모델을 호출 가능
- 다양한 모델 지원
- gemini-pro, gemini-1.5-flash 등 최신 모델을 선택하여 사용할 수 있음
- 다양한 기능
- 단순 텍스트 생성(generate_content)뿐만 아니라
- 멀티턴 채팅(start_chat), 임베딩 생성, 멀티모달(이미지+텍스트) 입력을 지원함
google-genai
- 차세대 통합 Python SDK
- Google AI Studio(무료/개발자용) / Vertex AI(기업/클라우드용) 통합
특징
- 통합된 인터페이스
- Google AI Studio의 Gemini API와 Google Cloud Vertex AI를
- 최신 기능 우선 지원
- Gemini 1.5 Pro/Flash, Imagen 3(이미지 생성) 등 최신 모델과 기능을 가장 먼저 지원
- 향상된 개발자 경험
- 최신 Python 트렌드를 반영하여 타입 힌트(Type Hinting)와 비동기(Async) 처리가 더 강력해짐
- Pydantic 지원
- 데이터 검증 및 구조화된 출력을 위해 Pydantic 모델을 직접 지원하여, JSON 응답 처리가 편리
google-genai 참고자료
| 자료 구분 | 자료명 | 링크 (URL) | 설명 |
|---|
| 공식 문서 | Python SDK Documentation | 바로가기 | google-genai 라이브러리의 클래스와 메서드 기술 명세서입니다. |
| 코드 예제 | Gemini API Cookbook | 바로가기 | 구글에서 공식 제공하는 주제별(RAG, 챗봇 등) 코드 예제 모음입니다. |
| 소스 코드 | GitHub (googleapis/python-genai) | 바로가기 | google-genai의 실제 소스 코드 저장소입니다. |
| Playground | Google AI Studio | 바로가기 | 웹에서 프롬프트를 테스트하고 코드를 생성할 수 있는 도구입니다. |
- Python SDK Documentation
- Client, types 등 코드에서 사용하는 모든 객체의 정의가 있음
- Gemini API Cookbook
- guides 폴더나 examples 폴더를 보면
.ipynb 파일들이 있음
- 이 파일들을
Colab에서 바로 실행해보며 학습하기 좋음
- GitHub Source
- Google AI Studio
- 로그인 후 우측 상단의 <> Get code 버튼을 누르면,
- 현재 설정한 프롬프트를 Python 코드로 즉시 변환해줌 (초기 개발 시 매우 유용)
예시 코드
from google import genai
from google.genai import types
from pydantic import BaseModel
from adrf.views import APIView
from rest_framework.response import Response
from rest_framework import status
from django.conf import settings
class MovieRecommendation(BaseModel):
"""
- AI가 줄글로 답하는 대신, 정의한 포맷(제목, 연도, 이유)에 맞춰서만 답하도록 강제
- API 개발 시 파싱 에러를 획기적으로 줄여주는 핵심 기능
"""
title: str
year: int
reason: str
class AsyncGeminiView(APIView):
async def post(self, request):
genre = request.data.get('genre', 'SF')
client = genai.Client(api_key=settings.GOOGLE_API_KEY)
"""
- google-genai 라이브러리의 핵심 진입점
- API 키를 사용하여 인증하고, 이후 모든 모델 호출을 이 client 객체를 통해 수행
"""
try:
response = await client.aio.models.generate_content(
"""
- `.aio` 속성을 통해 비동기 메서드에 접근
- `await` 키워드는 반드시 필요
"""
model='gemini-1.5-flash',
contents=f"{genre} 장르의 영화 하나만 추천해줘.",
config=types.GenerateContentConfig(
response_mime_type="application/json",
response_schema=MovieRecommendation,
"""
`google-genai`의 가장 강력한 기능 중 하나입니다.
Pydantic 클래스를 그대로 넘기면, AI가 내부적으로 스키마를 이해하고 정확히 그 형태의 JSON을 생성
"""
temperature=0.5
)
)
recommendation: MovieRecommendation = response.parsed
"""
- 기존에는 `json.loads(response.text)`를 해야 했지만,
이제는 `.parsed`를 통해 이미 Python 객체로 변환된 데이터를 바로 사용 가능
"""
return Response(
{
"title": recommendation.title,
"year": recommendation.year,
"reason": recommendation.reason
},
status=status.HTTP_200_OK
)
except Exception as e:
return Response({"error": str(e)}, status=500)
genai 로드맵
기초 (Setup & Basic)
- Client 초기화
- API Key vs Vertex AI 인증 차이 이해
- generate_content
- 텍스트 생성, 기본 파라미터(temperature, max_output_tokens) 설정
- types 모듈
- 설정 객체(GenerateContentConfig)를 통한 안전한 코딩
- Chat Session
- chats.create()를 이용한 대화 기록 관리 (Memory)
- System Instructions
- Multimodal
- 텍스트 + 이미지/비디오/오디오 파일을 동시에 입력으로 넣기
고급 (Advanced) - Django 적용 시 중요
- Structured Output (JSON)
- pydantic과 연동하여 AI 응답을 강제로 JSON 형식으로 받기 (API 개발 필수)
- Async (비동기)
- client.aio를 사용하여 Django의 비동기 뷰(adjang)와 연동하여 성능 최적화
- Function Calling
- AI가 외부 함수나 API를 호출하도록 만드는 도구 연결
genai 기초
client
- Client 객체는 Google의 AI 모델과 소통하는 창구(Gateway)
- 이 창구를 열 때 어떤 "신분증(인증)"을 사용하느냐에 따라 두 가지 모드로 나뉨

- Django 개발 시 팁
- 로컬 개발(Local)에서는 편리한 API Key 방식을 주로 사용하고,
- 실제 서비스 배포(Production) 시 보안 규정에 따라 Vertex AI로 전환하는 경우가 많음
- google-genai는 코드 한 줄만 바꾸면 이 전환이 가능함
generate_content
- 모델에게 작업을 시키는 가장 기본 명령어
- 결과를 조절하기 위해 파라미터(설정값)를 함께 보냄
Temperature (온도)
- 범위: 0.0 ~ 2.0 (모델마다 상이, 보통 0~1 사용)
- 의미: "창의성 vs 정확성" 조절
- 0.0: 항상 가장 확률이 높은 단어만 선택 (논리적, 사실적 답변, 코딩)
- 1.0: 다양한 단어를 선택 (창의적, 시적인 답변, 브레인스토밍)
Max Output Tokens (최대 출력 토큰)
- 의미: 답변의 길이 제한
- 용도: 비용 절약 및 응답 시간 단축 / 너무 짧게 설정하면 문장이 중간에 잘릴 수 있음
types 모듈
- Python은 타입을 엄격하게 체크하지 않지만,
- google-genai는 types 모듈을 통해 설정값을 객체로 관리할 것을 권장함
Dictionary vs Config Object
- 딕셔너리
- ex.
{"temperature": 0.5} : 오타가 나도 실행 전까지 모름
- 객체
- ex.
types.GenerateContentConfig(temperature=0.5)
- 에디터에서 자동완성을 지원하고, 오타 시 바로 빨간 줄로 경고를 줌
1차 예시
from google import genai
from google.genai import types
import os
API_KEY = os.environ.get("GOOGLE_API_KEY")
def basic_generation_example():
client = genai.Client(api_key=API_KEY)
"""
1. Client 초기화 (API Key 방식)
- Vertex AI 사용 시: client = genai.Client(vertexai=True, project='...', location='...')
- 가장 간단한 'API Key' 인증 방식을 사용
- Client 인스턴스를 생성하면 내부적으로 HTTP 세션이 연결되어 통신 준비를 마침
"""
try:
safe_config = types.GenerateContentConfig(
"""
2. 안전한 설정을 위한 Config 객체 생성 (types 모듈 활용)
- 딕셔너리 { 'temperature': 0.7 } 대신 types 모듈의 클래스를 사용
- 이 방식 사용시 'temprature' 같은 오타를 냈을 때, 코드 실행 전에 IDE가 에러를 잡아줌
- 유지보수하기 훨씬 안전한 방식
"""
temperature=0.7,
max_output_tokens=300,
""""3줄 요약" 요청에 충분하지만 과도하게 길어지는 것을 방지하는 안전장치"""
candidate_count=1
)
response = client.models.generate_content(
"""
3. 콘텐츠 생성 요청 (generate_content)
- 실제 AI 모델에게 연산을 요청하는 시점
- 'gemini-1.5-flash'는 속도가 빠르고 비용이 저렴해 API 서비스용으로 적합한 모델
"""
model='gemini-1.5-flash',
contents='Django 프레임워크의 장점을 3줄로 요약해줘.',
config=safe_config
)
print(response.text)
except Exception as e:
print(f"에러 발생: {e}")
genai 중급
| 기능 (Feature) | 설명 및 역할 | 주요 메서드/파라미터 | 비고 |
|---|
| Chat Session | 이전 대화 내용을 기억하여 문맥(Context)을 유지합니다. | client.chats.create()
chat.send_message() | Django 같은 웹에서는 DB에 대화 기록을 저장하고, 매 요청마다 이 기록을 불러와서 주입해야 합니다. |
| System Instructions | 대화 시작 전 AI에게 역할(Persona)이나 규칙을 부여합니다. | config=types.GenerateContentConfig(system_instruction=...) | 프롬프트에 매번 "넌 의사야"라고 쓰는 것보다 훨씬 강력하고 일관성 있게 작동합니다. |
| Multimodal | 텍스트 외에 이미지, 오디오, 비디오를 함께 입력으로 보냅니다. | types.Part.from_bytes()
types.Part.from_uri() | 텍스트와 미디어 파일을 리스트 [...] 형태로 묶어서 전달합니다. |
Chat Session (대화 세션과 메모리)
- 기본적으로 LLM API(HTTP 요청)는 방금 전의 질문을 기억하지 못함
- 챗봇처럼 대화하기 위해서는 개발자가 매번 지금까지의 모든 대화 내용(History)을 질문과 함께 줘야함
- 모델이 한번에 기억할 수 있는 정보의 양을 Context Window(컨텍스트 윈도우) 라고 함
역할(Role)
- 대화 내용을 재구성할 때는 '누가 말했는지'를 구분하는 Role이 중요
- user: 사용자(사람)의 질문이나 입력
- ex.
{"role": "user", "parts": ["안녕?"]}
- model : AI가 생성했던 답변
- ex.
{"role": "model", "parts": ["반갑습니다!"]}
- system : (설정 시) AI의 행동 지침입니다. 대화 흐름보다 우선순위가 높음(별도 config로 전달)
Django 개발 시 적용
- 웹 서버는 요청이 끝나면 메모리가 초기화되므로, client.chats 객체도 사라짐
- 따라서 DB에 대화 로그를 저장(user/model 쌍)해두고,
- 사용자가 새 메시지를 보낼 때마다 DB에서 기록을 불러와 history 리스트를 재조립하여
System Instructions(시스템 지시사항 / 페르소나)
- AI에게 "너는 지금부터 의사야"라고 말하는 것을 페르소나 부여라고 함
User Prompt에 포함
- 첫 질문에 "너는 의사야. (질문)..." 라고 적음
단점
- 대화가 길어지면(Context가 쌓이면) AI가 앞부분의 지시를 까먹거나 중요도를 낮게 평가할 수 있음
System Instruction 사용
- 아예 대화와 분리된 별도의 시스템 영역에 지시를 입력함
장점
- 대화가 아무리 길어져도 이 규칙은 변하지 않는 절대적인 법처럼 작동
- 모델 내부적으로 시스템 지시사항에 더 높은 가중치(Attention)를 두도록 설계되어 있음
Multimodal (멀티모달)
- Gemini는 네이티브 멀티모달(Native Multimodal) 모델
- 모델이 텍스트와 이미지를 동일한 언어(수학적 벡터)로 이해한다는 뜻
- 이미지 입력
- 사진을 픽셀 단위로 쪼개서 숫자 배열(벡터)로 바꿈
- 텍스트 입력
- 글자를 토큰으로 쪼개서 숫자 배열(벡터)로 바꿈
- 통합 처리
- 모델 입장에선 글자나 그림이나 똑같은 '정보 조각(Token)'일 뿐임
- 그래서 텍스트와 이미지가 섞여 있어도 자연스럽게 연관성을 파악함
2차 예시
from google import genai
from google.genai import types
from rest_framework.views import APIView
from rest_framework.response import Response
from django.conf import settings
from .models import ChatHistory
class AdvancedChatView(APIView):
def post(self, request):
user_msg = request.data.get('message')
image_file = request.FILES.get('image')
user_id = request.user.id
sys_instruction = "당신은 사용자의 식단을 분석해주는 영양사입니다. 칼로리보다는 영양 균형에 집중하세요."
"""
1. 시스템 지시사항 (System Instruction)
- 대화가 아무리 길어져도 변하지 않는 '절대 규칙' 설정
- "영양사"라는 역할을 지정해 놓으면 "이거 맛있어?"라는 질문에 맛보다는 영양사에 맞게 대답해줌
"""
client = genai.Client(api_key=settings.GOOGLE_API_KEY)
formatted_history = []
"""
2. 대화 세션 복원 (Context Window 구성)
- DB에서 과거 기록을 불러와 'history' 리스트로 재구성 (Stateless 극복)
- HTTP 요청은 독립적이므로, 이전 대화를 기억시키기 위해
- DB에서 꺼낸 데이터를 types.Content 객체 리스트로 만들어 주입
"""
past_chats = ChatHistory.objects.filter(user_id=user_id).order_by('created_at')
for chat in past_chats:
formatted_history.append(types.Content(role=chat.role, parts=[types.Part.from_text(chat.message)]))
current_parts = [types.Part.from_text(text=user_msg)]
"""
3. [이론 3] 멀티모달 입력 처리 (Tokenization 준비)
- 이미지 파일을 단순 파일이 아닌 'Part'라는 데이터 조각으로 만듬
- 모델은 이 Part들을 받아서 텍스트 Part와 이미지 Part를 합쳐 하나의 문맥으로 이해함
"""
if image_file:
image_part = types.Part.from_bytes(
data=image_file.read(),
mime_type=image_file.content_type
)
current_parts.append(image_part)
chat_session = client.chats.create(
model='gemini-1.5-flash',
config=types.GenerateContentConfig(
system_instruction=sys_instruction,
temperature=0.7
),
history=formatted_history
)
response = chat_session.send_message(current_parts)
return Response({"reply": response.text})
genai 고급
| 기능 (Feature) | 설명 및 역할 | 주요 메서드/파라미터 | 비고 |
|---|
| Structured Output | AI의 응답을 개발자가 정의한 JSON 스키마(Pydantic)로 강제합니다. | config={'response_mime_type': 'application/json', 'response_schema': MyClass} | API 응답을 파싱할 때 에러가 발생할 확률을 0%에 가깝게 줄여줍니다. |
| Async (Asynchronous) | AI가 응답을 생성하는 동안 서버가 멈추지 않고 다른 요청을 처리합니다. | client.aio.models.generate_content(...)
await 키워드 | Django의 async def 뷰와 결합하면 동시 접속 처리에 매우 유리합니다. |
| Function Calling | AI가 실시간 데이터(날씨, 주식)를 조회하거나 외부 동작을 수행하도록 도구를 쥐여줍니다. | tools=[my_function]
config={'tools': ...} | AI는 직접 코드를 실행하지 않고, "이 함수를 이 파라미터로 실행해줘"라고 요청만 합니다. |
Structured Output (구조화된 출력)
- AI의 응답을 Pydantic 모델에 맞춰 JSON으로 강제함
from rest_framework.views import APIView
from rest_framework.response import Response
import google.generativeai as genai
from pydantic import BaseModel
class Recipe(BaseModel):
"""
1. 응답받을 데이터 구조 정의
- AI가 생성해야 할 JSON 데이터의 '설계도'
- Pydantic을 사용해 필드명과 타입을 명시
- AI는 이 구조를 벗어난 응답(예: calories를 문자열로 줌)을 하지 않도록 강제됨
"""
dish_name: str
ingredients: list[str]
calories: int
class RecipeView(APIView):
def post(self, request):
user_input = request.data.get('query', '김치찌개 레시피 알려줘')
model = genai.GenerativeModel('gemini-1.5-flash')
response = model.generate_content(
user_input,
generation_config=genai.GenerationConfig(
"""
- Gemini에게 "이 대화의 응답은 반드시 Recipe 클래스 형태의 JSON이어야 한다"고 지시
- 'response_mime_type'을 'application/json'으로 설정해야 JSON 모드가 활성화됨
"""
response_mime_type="application/json",
response_schema=Recipe
)
)
return Response(response.text)
"""
- 별도의 JSON 파싱(json.loads) 과정 없이도, AI가 완벽한 JSON 문자열을 반환했으므로
- 프론트엔드에 그대로 전달해도 안전 (필요시 json.loads()로 dict 변환 가능)
"""
Async (비동기 처리)
from adrf.views import APIView
from rest_framework.response import Response
import google.generativeai as genai
import os
class AsyncChatView(APIView):
async def post(self, request):
user_message = request.data.get('message')
api_key = os.getenv("GOOGLE_API_KEY")
genai.configure(api_key=api_key)
model = genai.GenerativeModel('gemini-1.5-flash')
"""일반적인 설정과 동일하지만, 비동기 뷰 내부에서 호출됨"""
response = await model.generate_content_async(user_message)
"""
- 핵심 부분, 동기 방식인 `generate_content` 대신 `_async` 접미사가 붙은 메서드를 사용함
- `await` 키워드를 만나면 Python은 구글 서버로부터 응답이 올 때까지 기다리지 않고,
- 즉시 제어권을 이벤트 루프에 반환하여 다른 사용자의 요청을 처리하러 감
- AI 응답 속도가 2~3초 걸리더라도 서버 전체가 멈추지 않아 동시 접속 처리에 필수적
"""
return Response({"reply": response.text})
Function Calling (함수 호출)
- AI가 외부 함수를 사용할 수 있도록 도구를 연결하는 예시
from rest_framework.views import APIView
from rest_framework.response import Response
import google.generativeai as genai
from google.generativeai.types import FunctionDeclaration, Tool
def get_current_weather(location: str):
"""특정 지역의 날씨를 조회합니다."""
return {"location": location, "temperature": "25C", "condition": "Sunny"}
class AgentView(APIView):
def post(self, request):
user_query = request.data.get('query', '서울 날씨 어때?')
weather_tool = genai.protolib.Tool(
function_declarations=[
FunctionDeclaration(
"""
- 파이썬 함수를 AI가 이해할 수 있는 스키마 형태로 변환하는 과정
- 함수 이름, 설명, 필요한 인자(location)를 정의
"""
name="get_current_weather",
description="Get the weather for a location",
parameters={
"type": "OBJECT",
"properties": {
"location": {"type": "STRING", "description": "The city and state"}
},
"required": ["location"]
}
)
]
)
model = genai.GenerativeModel(
'gemini-1.5-flash',
tools=[weather_tool]
"""
- 모델 초기화 시 "너는 weather_tool이라는 도구를 사용할 수 있어"라고 알려줌
"""
)
chat = model.start_chat(enable_automatic_function_calling=True)
"""
`enable_automatic_function_calling=True`: 사용자가 "서울 날씨 어때?"라고 물으면,
- SDK가 자동으로 아래의 과정을 자동으로 처리해줌
- (1) 모델이 함수 호출 요청 -> (2) 로컬 함수 실행 -> (3) 결과를 다시 모델에 전달 -> (4) 최종 답변 생성
"""
response = chat.send_message(user_query)
return Response({"answer": response.text})
의문
Pydantic
- Pydantic을 사용하면 시리얼라이저 검증이 필요없는건 아닐까?
| 비교 항목 | Django Serializer (DRF) | Pydantic (AI 연동 시) |
|---|
| 주요 역할 | 클라이언트(사람)가 보낸 데이터 검증 및 DB 저장 | AI 모델이 생성한 데이터 검증 및 구조화 |
| 검증 대상 | HTTP Request Body (request.data) | LLM Response Text (response.text) |
| 연동 대상 | Django ORM (DB 테이블) | Python 객체 / JSON Schema |
| 필요 시점 | 사용자가 입력을 잘못 넣었을 때 에러 반환 (400 Bad Request) | AI가 포맷을 어겼을 때 재시도하거나 파싱 에러 방지 |
- Django REST Framework (DRF) 환경에서는 역할이 명확하게 나뉨
- 누가 데이터를 보내느냐(User vs AI)에 따라 검증의 주체가 다르기 때문
이상적인 구조
- "입력은 Serializer로 막고, 출력(AI)은 Pydantic으로 거르는" 형태
- 1단계: 사용자(Frontend) -> 서버
- 사용자가 이상한 값을 보내면 Serializer가 is_valid()로 막음
- 2단계: 서버 -> AI
- 3단계: AI -> 서버
- AI가 환각(Hallucination)으로 JSON 괄호를 빼먹는 것을 Pydantic이 강제
- 4단계: 서버 -> 사용자
- Pydantic 객체를 dict로 변환하여 바로 내려주거나, 다시 Serializer에 태워 보냄
구현
post / get 고민(현재 게임 요약 기준)
- 일반적으로 "DB에 데이터가 저장되거나 변경되면 POST를 써야 한다" 가 정석
- GET 요청은 서버의 상태를 변경하지 않는 '조회(Read-Only)' 작업에만 써야 한다는
GET
- API 설계를 할 때는 "DB 내부에서 무슨 일이 일어나느냐"보다
- 사용자(클라이언트) 입장에서는 요약이 DB에 미리 있었는지, 방금 AI가 만들었는지 알 필요가 없음
- 단지 "게임 요약 정보를 달라"는 조회 요청일 뿐임
- 따라서 GET을 쓰는 것이 자연스러움
- GET은 멱등성(여러 번 호출해도 결과가 같음)을 가져야 함
- 첫 번째 호출: AI가 요약을 생성하고 저장(INSERT)한 뒤 반환
- 두 번째 호출: DB에 저장된 요약을 조회(SELECT)해서 반환
- 세 번째 호출: DB에 저장된 요약을 조회(SELECT)해서 반환
참고