
Claude Code를 사용하여 Moebius 0.2B 이미지 인페인팅 (image inpainting) 모델을 브라우저에서 실행되도록 포팅하기
요약
Claude Code를 활용하여 경량 이미지 인페인팅 모델인 Moebius 0.2B를 브라우저 환경으로 포팅하는 과정을 다룹니다. WebGPU와 ONNX Runtime Web을 사용하여 모델을 웹에서 실행 가능하도록 구현하는 에이전트 기반 개발 워크플로우를 소개합니다.
핵심 포인트
- Claude Code를 이용한 모델 포팅 실무 사례
- Moebius 0.2B 모델의 WebGPU 기반 브라우저 실행 성공
- 에이전트 기반 연구(Agentic Research)를 통한 기술 스택 선정
- ONNX Runtime Web을 활용한 웹 환경 최적화
Claude Code를 사용하여 Moebius 0.2B 이미지 인페인팅 (image inpainting) 모델을 브라우저에서 실행되도록 포팅하기
2026년 6월 22일
오늘 아침 Hacker News에서 'Moebius: 10B 수준의 성능을 가진 0.2B 경량 이미지 인페인팅 (image inpainting) 프레임워크'를 보았습니다. 이는 이미지의 특정 영역을 표시하여 제거하면 모델이 그 공간을 무엇으로 채워야 할지 상상하는, 작지만 효과적인 인페인팅 (inpainting) 모델을 설명하고 있었습니다. 출시된 모델은 PyTorch와 NVIDIA CUDA를 필요로 했지만, 0.2B라고 명시되어 있었기에 브라우저에서 WebGPU를 사용하여 실행해 보기로 결정했습니다. 요약하자면(TL;DR): 성공했으며, simonw.github.io/moebius-web/에서 데모를 체험해 볼 수 있습니다. 자세한 내용은 아래를 읽어주세요.
완성된 도구
완성된 도구의 비디오 데모는 다음과 같습니다:
[IMG:1]
어떤 이미지든 열 수 있으며 (정사각형이 아닌 이미지는 레터박스 처리가 됩니다), 제거할 영역을 강조 표시한 다음, “Run inpaint” 버튼을 클릭하고 모델이 마법을 부릴 때까지 기다리면 됩니다.
병렬적인 에이전트 (agent) 사이드 프로젝트
오늘 저의 주요 프로젝트는 Datasette에 주요 기능을 추가하는 것이었습니다. 지난주에 출시한 행 삽입 및 편집 기능에 이어, 테이블을 생성하고 변경할 수 있는 UI를 만드는 작업입니다.
저는 Codex Desktop에서 이 작업을 진행하고 있었으며 (여기에 PR이 있습니다), 중간 규모의 리팩터링 (refactor)을 완료하거나 UI 변경 사항의 마무리 작업을 기다리는 동안 5~10분씩 손가락을 까딱이며 시간을 보내는 경우가 많았습니다.
(코딩 에이전트 (coding agents)에 관한 재미있는 점은, 문제가 어려울수록 에이전트가 계산을 마칠 때까지 기다리는 동안 주의가 산만해지는 시간이 더 많아진다는 것입니다!)
그래서 저는 터미널 창에 Claude Code를 띄워놓고 Moebius를 웹으로 포팅하는 작업을 어디까지 진행할 수 있을지 확인해 보기로 했습니다.
프로젝트 시작을 위한 에이전트 기반 연구 (agentic research)
첫 번째 단계는 일반 Claude에게 이 프로젝트의 실현 가능성에 대해 묻는 것이었습니다. GitHub에서 리포지토리 (repos)를 복제할 수 있는 기능이 있는 Claude.ai에서 다음과 같이 요청했습니다:
Clone [https://github.com/hustvl/Moebius/](https://github.com/hustvl/Moebius/) 하고, 이 모델을 실행할 수 있는 코드와 가중치 (weights)를 어디에라도 게시했는지 알려줘
(가중치 (weights) 링크는 아직 발견하지 못했습니다. 그건 "News" 섹션에 숨겨져 있더군요.)
그다음:
Moebius를 지금 바로 실행할 수 있는 옵션에는 무엇이 있나요? Python과 NVIDIA CUDA만 가능한가요, 아니면 다른 옵션도 있나요?
그리고:
이를 Transformers.js나 유사한 라이브러리로 포팅하여 브라우저에서 실행할 수 있는 타당성(feasibility)에 대해 심사숙고(Muse)해 보세요.
저는 모델에게 "X에 대해 심사숙고(muse on X)하라"고 말하는 것을 좋아합니다. 구체적인 목표를 제공하지 않고도 모델이 나를 대신해 문제를 깊이 고민하게 만들 수 있는 가장 짧은 방법이기 때문입니다.
여기 그 채팅 기록이 있습니다. 저는 마지막 답변을 복사하여 나중에 Claude Code가 읽을 수 있도록 research.md로 저장했습니다.
Claude는 제가 제안했던 Transformers.js 라이브러리의 하위 계층인 WebGPU 백엔드 기반의 ONNX Runtime Web을 사용하는 것을 제안했습니다.
그것만으로도 Claude Code를 풀어놓고 어디까지 갈 수 있는지 지켜볼 가치가 있다고 확신하기에 충분했습니다.
저는 보통 이런 프로젝트를 시작할 때 코딩 에이전트가 필요할 수 있는 최대한 많은 정보를 수집하는 것부터 시작합니다. 이 프로젝트가 실제로 성공할 것이라고 기대하지 않았기 때문에, 모든 작업을 /tmp 폴더에서 진행했습니다:
cd /tmp
mkdir Moebius
cd Moebius
...
Claude Code 가동하기
Claude가 코드 노트를 커밋하기 시작할 수 있도록 프로젝트의 나머지 부분을 위한 디렉토리를 만들고 그 안에서 git init을 실행했습니다:
mkdir /tmp/Moebius/moebius-web
cd /tmp/Moebius/moebius-web
git init
...
저는 제가 준비한 모든 연구 자료의 상위 레벨인 /tmp/Moebius 폴더에서 claude 인스턴스를 실행했습니다. 그리고 다음과 같이 프롬프트를 입력했습니다:
./moebius-web/research.md를 읽으세요. 당신의 목표는 이 모델을 ONNX와 WebGPU로 포팅하여 간단한 UI와 함께 브라우저에서 직접 실행할 수 있도록 만드는 것입니다.
Claude가 작업을 시작하자, 저는 다음과 같은 후속 프롬프트(오타 포함)를 던졌습니다:
Bulid this in /tmp/Moebius/moebius-web and commit early and often, also maintain a notes.md file in there with notes about what you figure out along the way - also start by writing out a plan.md in there and update that plan as oy work too
저는 에이전트에게 이와 같은 노트를 작성하도록 자주 요청하곤 합니다. 그 결과물은 저 자신뿐만 아니라 동일한 프로젝트를 다루는 다음 에이전트 세션에게도 매우 흥미로운 정보가 됩니다. 프로젝트가 끝날 무렵의 notes.md 파일은 다음과 같은 모습이었습니다.
저는 작업을 시작한 뒤 제 본업으로 돌아갔고, Claude가 어떻게 진행하고 있는지 가끔씩 확인했습니다. Claude가 작동하는 결과물을 만들어낸 것 같아 보였을 때, 저는 다음과 같이 프롬프트를 입력했습니다:
이것을 테스트하기 위해 내 브라우저에서 방문할 수 있는 URL이 무엇인지 알려줘
그 후 Chrome에서 테스트를 진행했고, 발생한 오류들(및 오류 스크린샷)을 다시 Claude Code에 붙여넣었습니다.
이 과정을 몇 차례 반복한 끝에, 마침내 제대로 작동하는 것처럼 보이는 결과물을 얻었습니다! 이제 다른 사람들도 사용할 수 있도록 인터넷에 올릴 차례였습니다.
모델 가중치(model weights)는 그곳에 있고, HTML 데모가 Hugging Face Spaces에 나타나도록 하려면 어떻게 이 프로젝트를 Hugging Face에 게시할 수 있을까?
Claude Code는 hf CLI 도구 사용법을 알고 있었기에, 저는 Hugging Face에 모델 저장소(repo)를 생성한 다음, 해당 저장소에 쓰기 권한이 있는 토큰을 생성하여 Claude가 사용할 수 있도록 /tmp/Moebius/token.txt 파일에 저장했습니다.
Claude는 1.24GB에 달하는 변환된 ONNX 가중치를 huggingface.co/simonw/Moebius-ONNX에 대신 게시해 주었습니다.
이전에도 다른 데모들이 Hugging Face에서 브라우저로 가중치를 로드하는 것을 본 적이 있었기에, 이것이 가능하다는 것을 알고 있었습니다. 저는 제 프론트엔드 코드를 GitHub Pages에 호스팅하기로 결정하고 다음과 같이 말했습니다:
대용량 파일(아마도 models/ 폴더를 제외한)을 제외하고 moebius-web 폴더를 GitHub에 게시하고 싶어. 그래서 해당 저장소의 GitHub Pages를 활성화했을 때 https://simonw.github.io/moebius-web/로 접속하면 UI가 서빙되도록 해줘
최종 URL을 알려주는 것은 중요했습니다. Claude가 제작 중인 데모 내의 URL들을 프로덕션 환경에 배포되었을 때 정상적으로 작동하도록 수정해야 할 수도 있기 때문입니다.
제 본업을 병행하며 몇 차례 더 반복 작업을 거친 끝에, 우리는 작동하며 배포까지 완료된 버전을 얻었습니다!
다만... 페이지를 새로고침할 때마다 약 1.3GB의 모델 가중치를 다시 다운로드하는 것 같았습니다. 이 과정에서 브라우저 캐싱 (Browser caching)이 매우 중요해 보였습니다!
serviceworker나 이와 유사한 기술을 사용하여 이 데이터들을 캐싱하는 데 도움이 될 만한 영리한 방법이 있을까요? 매번 다시 로드되는 것 같습니다. HF (Hugging Face)의 리다이렉트 (redirect) 방식에 무언가 이상한 점이 있어서 브라우저 캐싱 (browser caching)의 이점을 얻지 못하고 있는 것은 아닌지 우려됩니다.
저는 Transformers.js 프로젝트들이 이를 적절히 처리할 수 있다는 것을 알고 있었기에, Whisper Web 데모 복사본을 가져와 /tmp/Moebius/whisper-web 디렉토리에 넣었습니다.
그리고 다음과 같이 명령했습니다:
/tmp/Moebius/whisper-web를 (서브에이전트 (subagent)를 사용하여) 살펴보고 그들이 이를 어떻게 처리하는지 확인해줘
해당 프로젝트는 완전히 난독화 (obfuscated)된 빌드된 JavaScript 파일들로 구성되어 있었기에, 서브에이전트를 사용함으로써 남은 최상위 토큰 컨텍스트 (top-level token context)를 해당 파일들을 해독하는 데 낭비하지 않도록 했습니다.
Claude는 해당 프로젝트가 caches.open("transformers-cache") —즉, CacheStorage API—를 사용하고 있다는 사실을 파악하여 이를 우리 프로젝트에 추가했습니다.
이 프로젝트에 대한 전체 Claude Code 트랜스크립트 (Claude Code transcript)를 공유합니다 (my claude-code-transcripts 도구를 사용하여 게시됨).
이 모든 과정에서 무엇을 배웠을까요?
이것은 확실히 바이브 코딩 (vibe coding)에 해당합니다. 저는 해당 프로젝트의 코드를 단 한 줄도 직접 보지 않았으며, 저의 입력은 테스트, 작은 기능 개선 제안 (대용량 파일 다운로드를 위한 프로그레스 바 (progress bar) 등), 그리고 제가 원하는 작동 방식의 예시를 모델에게 제시하는 방향으로만 제한되었습니다.
제가 코드를 직접 작성하지 않았기 때문에, 기반 기술인 WebGPU, ONNX, 그리고 Moebius 모델 자체에 대해 배운 양은 매우 제한적이었습니다.
이런 종류의 프로젝트에서 흔히 그렇듯, 제가 배운 가장 중요한 것들은 무엇이 가능한가에 관한 것이었습니다:
- Claude Opus 4.8은 PyTorch 모델을 ONNX로 변환하고, 그 결과를 Hugging Face에 게시한 다음, 해당 모델을 로드하고 실행할 수 있는 웹 애플리케이션과 인터페이스를 구축할 수 있습니다.
- Chrome, Firefox, Safari 모두 이제 이러한 종류의 모델을 실행할 수 있습니다. 저는 세 브라우저 모두에서 테스트했습니다.
- CacheStorage API는 약 1.3GB 크기의 모델 파일과 함께 작동합니다.
- ... 이는 우리가 클라이언트 전용 (client-only) 웹 애플리케이션의 기능으로 인페인팅 (inpainting)을 구현할 수 있음을 의미합니다! (사용자가 1.3GB의 다운로드를 감수할 수 있다면 말이죠.)
저는 제 프로젝트에 대해 조금 더 배워봐야겠다고 느꼈습니다. 저는 Claude.ai를 실행하고 다음과 같이 프롬프트를 입력했습니다:
[https://github.com/simonw/moebius-web/](https://github.com/simonw/moebius-web/)를 클론(Clone)하고, 이를 사용하여 모델과 ONNX, 모델을 ONNX 및 WebGPU로 변환하는 과정, 그리고 기본적으로 이 저장소(repo)를 완전히 이해하기 위해 알아야 할 모든 것에 대해 나를 가르쳐줘.
다음은 Claude가 생성한 대화 기록과 understanding.md 마크다운 파일이며, 저는 이를 GitHub 저장소에 추가했습니다. 저는 특히 ONNX에 대한 설명이 매우 유익하다고 느꼈습니다:
ONNX (Open Neural Network Exchange)는 신경망을 위한 휴대 가능하고 프레임워크 중립적인 (framework-neutral) 파일 형식입니다. .onnx 파일은 본질적으로 다음 두 가지가 결합된 것입니다:
- 연산 그래프 (computation graph) — 노드들의 유향 그래프 (directed graph)로, 각 노드는 연산자 (
Conv,MatMul,Add,Einsum,Softmax,Gather,Resize, …)이며, 이들은 그 사이를 흐르는 이름이 지정된 텐서 (tensors)에 의해 서로 연결됩니다. 이것이 순전파 (forward pass)를 위한 "레시피"입니다. - 가중치 (weights) — 학습된 파라미터 텐서 (합성곱 커널 (convolution kernels), 임베딩 테이블 (embedding table) 등)이며, 동일한 그래프 내의 초기화 도구 (initializers)로 저장됩니다.
결정적으로, ONNX는 어떻게(how) 또는 어떤 하드웨어에서(on what hardware) 수행하는지 말하지 않고, 무엇을 계산할지 (what to compute)를 추상적으로 기술합니다. 연산자 세트는 opset 번호 (이 저장소는 opset 18을 사용함)에 의해 버전이 관리되며, 이는 어떤 연산자가 존재하는지, 그리고 그 의미론적 (semantics) 정의가 무엇인지를 정확하게 고정합니다.
여기 export_onnx.py에서 볼 수 있듯이, PyTorch에는 ONNX로 내보내기 위한 내장 메커니즘이 있는 것으로 나타났습니다:
torch.onnx.export( dec, (lat,), dec_path, opset_version=args.opset, input_names=["latent"], output_names=["image"], dynamic_axes={"latent": {0: "B"}, "image": {0: "B"}}, )
또한 여기에는 유용한 용어 사전과 모델 파이프라인(pipeline)이 어떻게 구성되는지 보여주는, 아주 약간의 오류가 있는 ASCII 아트 다이어그램이 포함되어 있었습니다.
AI 자동 생성 콘텐츠
본 콘텐츠는 Simon Willison Blog의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기