2장. 개발 환경 구성과 LLM API 호출
따라하기 — 첫 LLM 호출
목표
첫 실습 파일을 직접 만들어 Gemini에게 고객 문의 하나를 보내고 답을 받습니다. 이 절에서 "파일 만들기 → 코드 붙여 넣기 → 실행"의 과정을 익힙니다. 그리고 그 답에서 LLM 혼자서는 무엇을 못 하는지를 직접 확인합니다. 3장부터 24장까지 그 한계를 차례로 보완해 갑니다.
0. 실습 준비
$ conda activate myenv
python config.py에서 API 키 : 찾음을 확인한 상태여야 합니다.
1. 파일 만들기
(1) 새 파일 만들기
VS Code 왼쪽 탐색기에서 haru-market 폴더의 빈 곳을 마우스 오른쪽 버튼으로 눌러 새 파일(New File) 을 고르고, 이름을 아래와 같이 입력합니다.
lesson02_first_call.py
파일은
config.py와 같은 위치(폴더 맨 위)에 만듭니다.data나app폴더 안에 만들지 않도록 주의합니다.
(2) 코드 붙여 넣기
아래 코드 블록에 마우스를 올리면 오른쪽 위에 복사 버튼이 나타납니다. 눌러서 전체를 복사하고, 방금 만든 파일에 붙여 넣은 뒤 저장합니다(Ctrl + S).
# -*- coding: utf-8 -*-
"""[2장] Gemini API 첫 호출 — 가장 단순한 형태의 고객 문의 응답
환경 구성이 끝났는지 확인하는 장.
- .env 에서 API 키를 읽는다 (키는 절대 코드에 하드코딩하지 않는다)
- 요청 → 응답 구조를 눈으로 확인한다
- 토큰 사용량을 출력해 '이 호출에 얼마가 드는지' 감각을 잡는다
실행: python lesson02_first_call.py
"""
from config import MODEL, get_client, print_usage
client = get_client()
# ── 1. 가장 단순한 호출 ────────────────────────────────────────────────
question = "주문한 상품이 언제 도착하는지 어떻게 확인하나요?"
print(f"고객 문의: {question}\n")
response = client.models.generate_content(
model=MODEL, # .env 의 GEMINI_MODEL (기본: gemini-3.8-flash)
contents=question,
)
print("모델 답변:")
print(response.text)
print_usage(response)
# ── 2. 응답 객체의 구조 들여다보기 ────────────────────────────────────
# response.text 는 사실 아래 경로의 축약이다. 구조를 알아야 나중에
# function_call 같은 텍스트가 아닌 응답을 다룰 수 있다.
cand = response.candidates[0]
print("\n응답 구조:")
print(f" candidates 수 : {len(response.candidates)}")
print(f" finish_reason : {cand.finish_reason}")
print(f" parts[0] 타입 : text={cand.content.parts[0].text is not None}")
# ── 3. 같은 질문, 다른 모델 — 비용 차이 감각 ──────────────────────────
# flash 는 저렴하고 빠르다. pro 는 더 똑똑하지만 비싸다.
# 고객지원 챗봇의 대부분 작업(분류·조회·안내)은 flash 로 충분하다.
print("\n" + "=" * 60)
print("정리: 지금 이 답변의 한계")
print("=" * 60)
print("""- 모델은 '일반론'만 말한다. 하루마켓의 실제 주문 데이터를 모른다.
- "제 주문 HR20260701001 어디 있어요?"에는 답을 못 하거나 지어낸다.
→ 3장: 하루마켓 상담원처럼 말하게 만들기 (프롬프트)
→ 5장: 실제 데이터를 조회하게 만들기 (Function Calling)""")
2. 코드에서 볼 곳
길어 보이지만 LLM을 부르는 부분은 이 한 덩어리가 전부입니다.
response = client.models.generate_content(
model=MODEL, # 어떤 모델에게
contents=question, # 무엇을 물을지
)
print(response.text) # 모델이 만든 답
| 부분 | 뜻 |
|---|---|
client.models.generate_content(...) |
"한 번 묻고 한 번 답받기". LLM 호출의 가장 기본 형태 |
model=MODEL |
사용할 모델. .env의 GEMINI_MODEL 값이 들어옵니다 |
contents=question |
모델에게 보낼 내용 |
response.text |
모델이 생성한 답(문자열) |
이 과정이 끝날 때까지 만드는 모든 것은 결국 이 호출을 어떻게 감싸고, 몇 번 하고, 무엇을 함께 보내는가의 문제입니다.
3. 실행하기
실행하기 전에 짐작해 보세요. "주문한 상품이 언제 도착하는지 어떻게 확인하나요?"라는 질문에 모델은 하루마켓의 배송 조회 방법을 알려 줄까요?
$ python lesson02_first_call.py
실행 결과 (답변 문장은 실행할 때마다 달라집니다)
고객 문의: 주문한 상품이 언제 도착하는지 어떻게 확인하나요?
모델 답변:
주문하신 상품의 배송 조회는 주로 다음과 같은 방법으로 확인하실 수 있습니다.
**1. 쇼핑몰 앱/웹사이트 마이페이지 이용 (가장 일반적)**
* 상품을 구매하신 쇼핑몰(네이버쇼핑, 쿠팡, 11번가, G마켓 등)에 로그인합니다.
* **[마이페이지] > [주문/배송 조회]** 메뉴로 이동합니다.
(중략)
* 더 자세한 확인이 필요하신 경우, **어떤 쇼핑몰에서 구매하셨는지** 알려주시면 더 정확한 안내를 도와드릴 수 있습니다!
[토큰] 입력 15 + 출력 724 = 총 1540
응답 구조:
candidates 수 : 1
finish_reason : FinishReason.STOP
parts[0] 타입 : text=True
(이하 생략)
답이 나왔다면 여러분의 코드가 LLM과 처음으로 통신한 것입니다.
4. 무엇을 관찰했나
답은 그럴듯하다. 그런데 하루마켓의 답이 아니다
모델은 "네이버쇼핑, 쿠팡, 11번가"를 예로 들고, 마지막에는 "어떤 쇼핑몰에서 구매하셨는지" 를 되묻습니다. 모델은 자기가 하루마켓의 상담 창구라는 사실을 모릅니다. 우리가 알려 준 적이 없기 때문입니다.
- 말투와 역할을 알려 주는 일 → 3장 (프롬프트 설계)
- 실제 주문 데이터를 조회하게 하는 일 → 5장부터 (도구)
토큰 — 글을 세는 단위
출력에 [토큰] 입력 15 + 출력 724 = 총 1540이 보입니다. 토큰(token) 은 LLM이 글을 다루는 단위입니다. 한글은 대략 한 글자가 한두 토큰입니다. API 사용량은 토큰 수로 셉니다.
질문은 15토큰인데 답은 724토큰입니다. 이 답이 필요 이상으로 길다는 뜻이기도 합니다. 3장에서 답변 길이를 정해 주면 이 숫자가 크게 줄어듭니다.
입력과 출력을 더하면 739인데 총합은 1,540입니다. 차이 801은 모델이 답을 쓰기 전에 생각하는 데 쓴 토큰(생각 토큰) 입니다. 화면에는 보이지 않지만 사용량에 포함됩니다.
응답은 글자만 들어 있는 상자가 아니다
response.text는 사실 response.candidates[0].content.parts[0].text를 줄여 쓴 것입니다.
| 항목 | 뜻 |
|---|---|
candidates |
모델이 만든 답의 후보 목록. 보통 1개입니다 |
finish_reason |
답이 끝난 이유. 화면에는 FinishReason.STOP처럼 찍힙니다. STOP은 정상 종료입니다. MAX_TOKENS면 길이 제한에 걸려 중간에 잘린 것입니다 |
parts |
답을 이루는 조각들. 지금은 글자(text) 하나뿐입니다 |
지금은 parts에 글자만 들어 있지만, 5장에서는 여기에 "이 함수를 불러 달라"는 요청이 들어옵니다. 그래서 구조를 미리 봐 둡니다.
핵심 정리
- LLM 호출의 최소 단위는
client.models.generate_content(model=, contents=)이고, 답은response.text에 있습니다. - 아무것도 알려 주지 않은 LLM은 일반론을 말합니다. 하루마켓을 모릅니다.
- 토큰이 사용량의 단위입니다. 답이 길수록, 호출이 많을수록 토큰이 늘어납니다.
finish_reason이STOP이면 정상,MAX_TOKENS면 답이 잘린 것입니다.