Claude Code에게 그림 그리기 가르치기: Gemini의 Interactions API와 MCP를 기반으로 구축된 상태
요약
Google의 Gemini Interactions API와 MCP를 활용하여 Claude Code에서 상태 유지(stateful) 이미지 생성 및 편집이 가능하도록 구현한 기술 사례를 소개합니다. 기존의 상태 비저장 방식과 달리, 이전 컨텍스트를 유지하며 연속적인 이미지 수정이 가능한 워크플로우를 제공합니다.
핵심 포인트
- Gemini Interactions API를 통해 이미지 생성 시 시각적 컨텍스트 유지 가능
- MCP 서버를 구축하여 Claude Code에 이미지 생성 및 편집 도구 제공
- interaction_id를 활용한 연속적인 이미지 편집 워크플로우 구현
- 프롬프트 재입력 없이 자연어 명령만으로 이미지 요소 추가 및 수정 가능
요약 (TL;DR): nb2lite-skill-claude는 Google의 gemini-3.1-flash-lite-image 모델을 작은 FastMCP 서버로 래핑(wrap)하여 Claude Code 스킬로 패키징합니다. Claude Code에 "사이버펑크 주방 이미지를 생성해줘"라고 입력하면, 그냥... 해냅니다. 그다음 "네온 라멘 간판을 추가해줘"라고 말하면, 전체 장면을 다시 프롬프트로 입력하지 않고도 _동일한 이미지_를 편집합니다. 아, 그리고 이 기사의 커버 이미지요? 이 기사에서 다루는 바로 그 기술로 생성되었습니다. 끝까지 제대로 된 도그푸딩(dogfooding)이죠. 자세한 내용은 마지막에 설명하겠습니다.
배경: 왜 또 다른 이미지 도구가 필요한가?
대부분의 이미지 생성 워크플로우는 상태 비저장(stateless) 방식입니다. 프롬프트를 보내면 픽셀을 돌려받고, 모델은 즉시 모든 것을 잊어버립니다. 결과를 수정하고 싶나요? 그러면 _전체 장면_을 다시 묘사하고 캐릭터, 조명, 구도가 이번 왕복 과정에서도 살아남기를 기도해야 합니다. (해설: 살아남지 못합니다.)
Google의 Nano Banana 2 Lite — gemini-3.1-flash-lite-image의 친근한 별명 — 는 다른 접근 방식을 취합니다. 이는 2초 미만의 생성 속도, 25개 이상의 언어에서 안정적인 텍스트 렌더링, 그리고 핵심 기능인 상태 유지(stateful) Interactions API 지원을 갖춘 고효율 이미지 모델입니다. 이 API를 통해 모델이 서버 측에서 시각적 컨텍스트를 유지하는 동안 여러 턴에 걸쳐 이미지를 반복적으로 수정할 수 있습니다.
이 저장소(repo)는 해당 기능을 Claude Code에 결합하여, 사용자의 코딩 에이전트가 세션의 자연스러운 일부로서 이미지를 생성하고 반복적으로 개선할 수 있도록 합니다. 이 저장소는 두 가지 구성 요소로 제공됩니다:
- 정확히 4개의 도구를 노출하는 Model Context Protocol (MCP) 서버 (
nb2lite-agent,server.py에 있는 단일 파일 FastMCP 앱). - Claude에게 해당 도구들을 언제 그리고 어떻게 잘 사용할지 가르치는 Claude Code 스킬 (
nb2lite-image).
Interactions API: 기억력이 있는 이미지
Interactions API는 Gemini의 상태 유지(stateful) 엔드포인트입니다. 핵심 루프는 다음과 같습니다:
- 프롬프트와
store=True를 포함하여client.interactions.create(...)를 호출합니다. - 응답에는 Google 서버에 저장되어 해당 턴의 시각적 컨텍스트(visual context)를 제어할 수 있는 핸들인 **
interaction_id**가 포함됩니다. - 다음 호출 시
previous_interaction_id를 전달하면, 모델은 캐릭터, 스타일, 조명 및 픽셀 연속성(pixel continuity)을 유지하며 _기존 캔버스(existing canvas)_를 편집합니다.
따라서 다음과 같은 방식(상태 비저장(stateless) 방식의 고충) 대신:
"새벽녘 숲속의 수채화풍 여우, 안개, 부드러운 빛, 빨간 목도리를 두르고 있음, 왼쪽에 자작나무 세 그루, 그리고 이제는 랜턴도 들고 있음"
...이렇게 작성하면 됩니다:
"발에 랜턴을 추가해줘."
그게 전부입니다. 저장된 컨텍스트가 나머지를 처리합니다.
서버가 대신 처리해 주는 몇 가지 실무적인 세부 사항은 다음과 같습니다:
- 매 턴마다 새로운 interaction ID가 반환됩니다. 가장 최근의 ID를 체이닝(chain)하세요. 오래된 ID로 편집을 시도하면 세션이 이전 상태에서 조용히 분기(fork)됩니다 (이를 수동으로 구현할 경우 매우 미묘하고 짜증 나는 버그가 됩니다).
- 종횡비(Aspect ratio)는 생성 시점에 결정되며 (
1:1,16:9,9:16,4:3,3:4), 상태 유지(stateful) 편집 시에는 이를 상속(inherited) 받습니다. 세션 중간에 종횡비를 변경하면 픽셀 연속성이 저하되므로, 편집 도구에서는 의도적으로 변경을 허용하지 않습니다. - 사고 수준(Thinking levels):
low(기본값, 빠른 초안) 또는high(복잡한 렌더링, 정확한 텍스트 레이아웃, 캐릭터 구도). 일반적인 API 명세에는minimal과medium도 나열되어 있지만, 실제 API에서는 이 모델에 대해 해당 값들을 HTTP 400 오류로 거부합니다. 서버가 여러분이 이를 직접 겪으며 깨닫지 않도록 미리 막아주는 셈입니다.
MCP란 무엇인가, 1분 요약
**Model Context Protocol (MCP)**는 AI 어시스턴트를 도구 및 데이터와 연결하기 위한 개방형 표준입니다. MCP 이전에는 모델에 특정 서비스에 대한 액세스 권한을 주려면 각 어시스턴트마다 맞춤형 통합(bespoke integration) 코드를 작성해야 했습니다. 즉, N개의 어시스턴트 × M개의 서비스만큼 모든 사람이 동일한 배관 작업을 새로 만들어야 했습니다. MCP는 이를 하나로 통합합니다. 도구 제작자가 타입이 지정된 도구(typed tools)를 노출하는 하나의 MCP 서버를 작성하면, MCP를 지원하는 모든 클라이언트(Claude Code, Claude Desktop 및 점점 늘어나는 다른 클라이언트들)가 클라이언트별 연결 코드(glue code) 없이도 해당 도구를 발견하고 호출할 수 있습니다.
MCP 서버는 일반적으로 stdio를 통해 JSON-RPC를 사용하는 작은 로컬 프로세스입니다. 클라이언트가 이를 실행하고 "어떤 도구들을 가지고 있나요?"라고 물으면, 그 이후부터 모델은 해당 도구들을 함수처럼 호출할 수 있습니다.
nb2lite-agent 서버는 정확히 4개의 도구를 노출합니다:
| 도구 (Tool) | 기능 |
|---|---|
generate_image | 텍스트 → 1k 이미지 생성. 로컬에 저장하고 경로와 interaction ID를 반환함. |
| ... |
이미지는 gen_<timestamp>_<uuid8>.jpg (또는 edit_/edit_local_ 접두사가 붙은 형태)로 디스크에 저장됩니다. UUID 접미사는 동시에 생성되는 이미지들이 서로를 덮어쓰지 않도록 방지합니다. 에러는 프로토콜 에러가 아닌 🔴 ... 텍스트 문자열로 반환되므로, 에이전트가 이를 읽고 대응할 수 있습니다.
그리고 Claude Code의 _스킬(skill)_이란 무엇인가?
MCP가 손(Claude가 물리적으로 호출할 수 있는 도구들)이라면, **스킬 (skill)**은 _근육 기억 (muscle memory)_입니다. 즉, Claude의 컨텍스트(context)에 로드되어 워크플로우를 가르쳐주는 마크다운 파일(SKILL.md)과 번들된 리소스들의 집합입니다. 어떤 도구를, 어떤 순서로, 어떤 제약 조건 하에 사용해야 하는지를 알려줍니다.
nb2lite-image의 경우, 스킬은 다음과 같은 사항들을 인코딩합니다:
- 설정 문제를 진단할 때는 가장 먼저
get_help를 호출할 것 — API 키가 누락되었다면 다른 어떤 것도 작동하지 않습니다. - 편집 프롬프트는 **점진적 (incremental)**으로 유지할 것: 장면 전체가 아니라 변경 사항만을 설명하세요.
- 항상 최신 (latest) interaction ID를 체이닝(chain)할 것.
- 생성 작업은 비용이 발생함 — 관련 편집 작업들을 배치(batch)로 처리하고, 초안 작업 시에는
thinking_level: low를 선호할 것.
또한 이 스킬은 MCP 서버 자체(mcp/server.py), 요구 사항, 설치 스크립트, 그리고 Interactions API 개발자 가이드의 벤더드(vendored) 복사본을 함께 묶어 제공하므로 자기 완결적(self-contained)입니다. 스킬을 설치하면 서버를 구동하는 데 필요한 모든 것을 갖추게 됩니다.
설치하기: "그냥 바로 작동했으면 좋겠어" 버전
세 가지가 필요합니다: Python 3.10+, Claude Code, 그리고 Gemini API 키 (Google AI Studio에서 무료로 발급 가능)입니다. 아래 경로 중 _하나_를 선택하세요.
경로 A: 플러그인 마켓플레이스 (가장 적은 키 입력)
Claude Code 내부에서 다음과 같이 입력합니다:
/plugin marketplace add xbill9/nb2lite-skill-claude
/plugin install nb2lite-image@nb2lite-skill-claude
이렇게 하면 스킬(skill)이 설치됨과 동시에 MCP 서버가 자동으로 등록됩니다. 플러그인 매니페스트(plugin manifest)에는 API 키가 포함되어 있지 않으며(당연히 그래야 합니다!), 서버는 환경 변수에서 GEMINI_API_KEY를 읽어옵니다. 따라서 Claude Code를 실행하기 전에 해당 키가 export 되어 있는지 확인하세요.
경로 B: 클론(Clone) 및 부트스트랩(bootstrap) (이 리포지토리 사용)
# 1. 코드 가져오기
git clone https://github.com/xbill9/nb2lite-skill-claude.git
cd nb2lite-skill-claude
...
정말로 이게 전부입니다. 무언가 잘못된 것 같다면 init.sh를 다시 실행해도 안전합니다.
경로 C: 본인의 프로젝트에 설치
리포지토리를 클론한 상태에서 다음을 실행합니다:
make init TARGET=/path/to/your/project ARGS='--output-dir ./images'
이 명령은 스킬을 <project>/.claude/skills/nb2lite-image/로 복사하고, 해당 프로젝트의 .mcp.json에 nb2lite-agent 항목을 작성합니다. 이미 설정해 두었다면 ~/gemini.key를 재사용합니다. 대상 프로젝트에서 Claude Code를 재시작하고 서버 승인을 하면 완료됩니다.
경로 D: Docker (호스트에는 Docker만 설치된 경우)
서버는 xbill9/nb2lite-agent로 배포되어 있습니다:
claude mcp add nb2lite-agent --env GEMINI_API_KEY="$(cat ~/gemini.key)" -- \
docker run --rm -i -e GEMINI_API_KEY -v "$PWD:$PWD" -w "$PWD" xbill9/nb2lite-agent
-v "$PWD:$PWD" -w "$PWD" 마운트 설정이 중요합니다. 서버가 이미지를 디스크에 저장하고 edit_local_image를 위해 로컬 파일을 읽기 때문에, 컨테이너는 호스트와 _동일한 절대 경로(absolute path)_에서 프로젝트를 볼 수 있어야 합니다.
문제 해결(Troubleshooting) 가이드 전체
/mcp에 서버가 목록에 나타나지 않음 → 프로젝트 디렉토리에서 Claude Code를 재시작하세요.- 도구가
🔴 GEMINI_API_KEY is not set을 반환함 →source set_env.sh를 실행(또는 키를 export)하고 재시작하세요. - 그 외의 문제 → Claude에게
get_help를 호출하도록 요청하세요. 현재 설정 상태를 보고해 줄 것입니다.
예시: 실제 세션 적용
설치가 완료되면 평범한 영어로 대화할 수 있습니다. 실제 흐름은 다음과 같습니다:
사용자: "해질녘 눈 내리는 숲속의 아늑한 오두막을 16:9 비율로 생성해줘."
Claude 호출:
generate_image(
prompt="A cozy log cabin in a snowy forest at dusk, warm light in the windows",
aspect_ratio="16:9",
...
사용자: "좋아. 굴뚝에서 연기가 피어오르게 해줘."
edit_image(
previous_interaction_id="v1_ChdpRU5...",
edit_prompt="add gentle smoke curling from the chimney",
...
사용자: "이제 밤으로 바꾸고, 하늘에는 오로라를 넣어줘."
동일한 도구, 최신 ID를 사용하며 오두막, 나무, 굴뚝 연기는 그대로 유지됩니다. 오직 하늘만 바뀝니다. 다시 프롬프트를 입력할 필요도, 연속성이 깨질까 걱정할 필요도 없습니다.
모델이 생성하지 않은 이미지의 경우:
사용자: "./whiteboard-sketch.png 파일을 가져와서 깔끔한 3D 제품 목업(mockup)으로 렌더링해줘."
edit_local_image(
image_path="./whiteboard-sketch.png",
edit_prompt="render this hand-drawn sketch as a high-fidelity 3D product mockup",
...
이 역시 상호작용 ID (interaction ID)를 반환하므로, 후속 수정 작업은 edit_image로 전환되어 그 시점부터 상태 유지(stateful) 방식으로 진행됩니다.
Dogfooding: 커버 이미지에 대하여 🐕🍖
이 용어가 생소하시다면: **"자신의 개 사료를 먹는다 (eating your own dog food)\
generate_image(
prompt="A wide tech blog cover illustration: a friendly robot artist "
"painting a glowing galaxy on an easel, while a chain of connected "
...```
(해당 출력물은 모든 증빙과 함께 [`devto-cover.jpg`](https://github.com/xbill9/nb2lite-skill-claude/blob/main/devto-cover.jpg)로 리포지토리에 커밋되었습니다.)
주목할 만한 점:
- **텍스트가 정확하게 렌더링되었습니다.** "NB2Lite + MCP"와 전체 부제목이 오타 없이 선명하게 출력되었습니다. 이것이 바로 텍스트가 많은 레이아웃에서 `thinking_level: "high"`를 설정했을 때 얻을 수 있는 결과입니다.
- **모델이 자신의 핵심 가치를 직접 시각화했습니다.** 프레임의 연속(낮 → 일몰 → 폭풍 → 은하계)은 바로 상태 유지 편집 루프 (stateful edit loop)를 의미합니다. 이 이미지는 제가 직접 그린 다이어그램보다 Interactions API를 더 잘 설명해 줍니다.
- **만약 강조 색상을 바꾸고 싶다면,** 이미지를 다시 생성하지 않습니다. 해당 상호작용 ID (interaction ID)를 사용하여 `edit_image`를 호출하고 "주황색 강조 부분을 마젠타색으로 바꿔줘"라고 말하면 됩니다. 이것이 바로 이 기술의 핵심입니다.
자사 제품 사용 (Dogfooding)은 가장 저렴하게 신뢰를 얻는 방법입니다. 선별된 갤러리도, "결과는 다를 수 있음"이라는 작은 글씨의 면책 조항도 없습니다. 도구의 실제 결과물이 말 그대로 당신이 이 기사를 열었을 때 처음 본 것입니다. 만약 이 기술이 글자를 틀리거나 레이아웃을 망쳤다면, 당신은 지금 바로 그 증거를 보고 있었을 것입니다. 대신, 이 기사는 헤더에 자체적인 증거를 포함한 채 배포됩니다.
## 링크
- **리포지토리 (Repo):** [github.com/xbill9/nb2lite-skill-claude](https://github.com/xbill9/nb2lite-skill-claude) (Apache-2.0)
- **Docker 이미지:** [hub.docker.com/r/xbill9/nb2lite-agent](https://hub.docker.com/r/xbill9/nb2lite-agent)
- **Interactions API 레퍼런스:** [ai.google.dev/api/interactions-api](https://ai.google.dev/api/interactions-api)
- **Model Context Protocol (MCP):** [modelcontextprotocol.io](https://modelcontextprotocol.io)
_본 프로젝트는 Anthropic 또는 Google과 제휴하거나 승인받지 않은 제3자 커뮤니티 프로젝트입니다. 본인의 Gemini API 키를 사용하세요. 생성 시 비용이 발생하므로, 초안은 `low` 설정으로 작성하고 결정적인 장면(money shot)을 위해 `high` 설정을 아껴두는 것을 잊지 마세요._
AI 자동 생성 콘텐츠
본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기