wardrobe는 사진 속 옷을 AI로 오려내(extract) 투명 배경 상품 이미지로 만들고, 그것들을 갤러리처럼 정리해 주는 1인용 로컬 앱이다. 핵심 흐름은 세 문장으로 끝난다. 사진을 끌어다 놓는다 → 사진 속 각 옷이 깔끔한 카탈로그 컷아웃으로 바뀐다 → 원하면 "내가 그 옷을 입은" 사진까지 만들어 준다. 이 모든 결과물(원본·작업파일·완성 PNG·JSON DB)은 클라우드로 나가지 않고 data/ 폴더 안에만 쌓인다. 제작자는 tandpfun(Discord 봇 생태계로 유명한 개발자)이고, 원 게시물은 X(@cdngdev)에 올라온 데모다.
온라인 쇼핑몰의 옷 사진은 배경이 새하얗고 옷만 딱 떠 있다. 그런 "상품컷"을 찍으려면 보통 조명·배경지·포토샵 누끼(배경 제거)가 필요하다. wardrobe는 그 스튜디오 전체를 AI로 대신한다 — 게다가 사진이 클라우드로 올라가지 않고 내 컴퓨터 안에서만 처리된다. "내 옷들을 디지털 카탈로그로 아카이빙하고 싶다"는 욕구를 정확히 겨냥한, 개인 소장용 프로젝트다.
data/ 폴더에만 두고, 그 폴더는 .gitignore로 저장소에서 아예 제외된다. 그래서 클론하면 갤러리가 텅 비어 있는 게 정상이다.업로드·작업 큐·리뷰 승인·이미지 서빙까지 있어서 "Next.js 풀스택 SaaS"처럼 읽히지만, 실제로는 Vite 6 단일 페이지 앱이다. 모든 API(/api/import/*)는 별도 서버 프로세스가 아니라 Vite 서버에 apply: "serve"로 얹힌 미들웨어 플러그인으로 존재한다. 즉 이 앱의 백엔드는 vite dev / vite preview가 돌 때만 살아 있다. vite build로 뽑은 정적 번들엔 /api/import/*가 없다 — GitHub Pages 같은 정적 호스팅엔 "가져오기(import)" 기능을 올릴 수 없다는 뜻이다. 이 한 가지만 이해하면 나머지 구조가 전부 풀린다.
wardrobe가 TrendShift Daily 상위에 오른 이유는 셋으로 요약된다. 첫째, 결과물이 눈에 확 들어온다 — "내 옷 사진 → 쇼핑몰 상품컷 + 내가 입은 화보"라는 변환은 데모 영상 한 편으로 설득이 끝난다. 둘째, 로컬 퍼스트라는 안심 포인트 — 개인 사진을 다루는 앱인데 클라우드로 안 나간다. 셋째, 코드를 열어 보면 "AI에 의존하지 않고 직접 푼" 영리한 트릭들이 촘촘하다. 특히 "투명 배경을 모델에 맡기지 않고 내가 크로마키로 파낸다"는 결정이 백미다.
이미지 모델에게 "배경 투명하게 해 줘"라고 시키면 가장자리에 잔털·반투명 얼룩이 남기 쉽다. wardrobe는 대신 모델에게 "완벽하게 균일한 단색 크로마(초록/마젠타/시안) 배경에 옷만 그려라"라고 시키고, 돌아온 이미지를 앱이 직접 크로마키(초록화면 합성)로 파낸다. 게다가 옷 색과 가장 먼 크로마색을 골라 "옷까지 파여 나가는" 사고를 막는다. 이 한 끗이 결과 품질을 가른다.
업로드→작업 생성→리뷰 승인→에셋 서빙까지 완전한 REST API가 있는데, 그게 전부 Vite 플러그인의 configureServer 미들웨어 안에 산다. 프레임워크(Express/Hono)도, 배포할 서버도 없다. "프론트 개발 서버가 곧 백엔드"라는 이 구조는, 로컬 전용 도구를 만들 때 배포·운영 부담을 통째로 없애는 흥미로운 선택지다.
저장소엔 .agents/skills/ 아래 Codex 스킬 두 개(import-clothes, generate-outfits)가 들어 있어, 앱을 켜지 않고도 에이전트가 같은 옷-오려내기·화보 생성 파이프라인을 돌린다. 심지어 재-임포트 시 중복이 안 생기게 이미지 바이트의 해시로 안정적 UUID를 만든다. "앱 로직을 그대로 에이전트가 조작 가능하게 노출"하는 좋은 본보기다.
비슷한 결을 가진 서비스로는 유료 "가상 피팅/누끼" SaaS(Photoroom·remove.bg 류)나 옷장 관리 앱(Whering·Acloset 등)이 있다. wardrobe의 차별점은 완전 오픈소스 + 완전 로컬 + gpt-image 기반이라는 삼박자다. 대신 트레이드오프도 분명하다 — OpenAI API 키가 필요하고(생성 비용 발생), 배포형 서비스가 아니라 내가 직접 클론해 돌리는 개발자용 도구에 가깝다. "예쁜 제품"이라기보단 "잘 만든 레퍼런스 구현"으로 보는 게 정확하다.
wardrobe의 의존성 목록은 "AI 이미지 앱치고 이렇게 얇다고?" 싶을 만큼 단출하다. 런타임 의존성이 9개뿐이고 devDependencies는 아예 없다. TypeScript도, Tailwind도, ORM도, 상태관리 라이브러리도 없다. 무거운 일(이미지 픽셀 연산)은 sharp 하나가 다 하고, 지능(옷 인식·생성)은 OpenAI API가 다 한다. 나머지는 순수 React + 손으로 쓴 CSS다.
| 영역 | 기술 · 버전 | 역할 |
|---|---|---|
| UI 프레임워크 | React 19.2.0 + react-dom | 갤러리·임포트 UI. 상태는 순수 훅(useState/useEffect/useMemo)만 — Redux·Zustand·Router 없음 |
| 빌드/개발 | Vite 6.4.3 (@vitejs/plugin-react 5.0.4) | 번들러이자 백엔드 호스트. 미들웨어 플러그인이 여기 얹힌다 |
| 이미지 표시 | @unpic/react 1.0.2 | 반응형 <Image> 컴포넌트. srcset으로 알맞은 해상도 로드 |
| 아이콘 | @phosphor-icons/react 2.1.10 | UI 아이콘 세트 |
| 폰트 | @fontsource-variable/instrument-sans 5.2.8 | 자가 호스팅 가변 폰트(외부 CDN 의존 없음) |
| 스타일 | 손으로 쓴 CSS (styles.css 914줄 · import-flow.css 320줄) | Tailwind·CSS 프레임워크 없음. 종이질감 "에디토리얼" 디자인 토큰 |
| 영역 | 기술 · 버전 | 역할 |
|---|---|---|
| 서버 | Vite 미들웨어 (import-job-api.mjs 711줄) | 업로드·작업·리뷰·에셋 서빙 전부. Express/Hono 없음 |
| 이미지 처리 | sharp 0.34.5 (libvips) | 정규화·크롭·크로마키 제거·despill·프레이밍·검증. 이 앱의 진짜 엔진 |
| 반응형 리사이즈 | ipx 3.1.1 | /_ipx/* 엔드포인트에서 즉석 리사이즈/화질 조정 |
| OpenAI 호출 | 네이티브 fetch + FormData/Blob | OpenAI SDK조차 안 씀 — 순수 fetch로 /responses·/images/edits 호출 |
| 저장 | Node fs/promises + crypto | 로컬 JSON DB + PNG 파일. DB 엔진·ORM 없음 |
| 영역 | 기술 | 역할 |
|---|---|---|
| 옷 인식(비전) | OpenAI Responses API · 기본 gpt-5.4-mini | 사진 속 옷을 찾아 이름·카테고리·색·바운딩박스를 구조화 JSON으로 반환 |
| 이미지 생성 | OpenAI Images edits API · 기본 gpt-image-2 | 옷 컷아웃(단색 배경) + "내가 입은" 화보 생성 |
| PWA | manifest.webmanifest + sw.js | 오프라인 앱 셸 캐시 + 이미지 stale-while-revalidate (프로덕션에서만 등록) |
| 에이전트 층 | Codex 스킬 (.agents/skills/) | 앱 없이 같은 파이프라인 실행 (openai.yaml 인터페이스) |
기본 모델명 gpt-5.4-mini(비전)·gpt-image-2(이미지)는 2026년 시점의 미래형 이름으로 코드와 .env.example에 하드코딩돼 있다. 지금 실제 API로 돌리려면 OPENAI_VISION_MODEL·OPENAI_IMAGE_MODEL(또는 단계별 OPENAI_GARMENT_MODEL·OPENAI_MODELED_MODEL) 환경변수로 실존하는 모델명으로 덮어써야 한다. 클론만 하고 그대로 돌리면 "그런 모델 없음" 오류가 날 수 있다.
클라이언트(React SPA)와 "서버"(Vite 미들웨어)는 같은 프로세스 안에 있다. 브라우저는 /api/import/*로 요청하고, Vite 서버에 얹힌 wardrobeImportApi 핸들러가 받아 sharp로 이미지를 주무르고 OpenAI를 호출한 뒤 파일로 저장한다. 별도 백엔드 서버는 존재하지 않는다.
apply: "serve" · configureServerconfigureServer(server) 훅에서 개발 서버의 요청 처리 파이프라인(connect 미들웨어)에 직접 함수를 끼워 넣을 수 있다. wardrobe는 이걸로 /api/import/* 요청을 가로채 처리한다. apply: "serve"는 "이 플러그인은 개발/프리뷰 서빙 때만 작동하고 빌드 땐 빠진다"는 표시. 그래서 이 앱의 API는 vite dev·vite preview에서만 살아 있다.모든 옷은 crop → garment → modeled라는 3단계 상태 기계를 거친다. 각 단계는 "생성 → 리뷰(사용자 승인/거절/재생성)"를 반복한다. 순서대로 뜯어보자.
업로드된 사진을 sharp로 정규화한 뒤, OpenAI /responses에 엄격한 json_schema(이름 wardrobe_items, 최대 8개)로 질의한다. 모델은 옷마다 이름·카테고리·주색(hex)·부색·태그와 함께 1000×1000으로 정규화한 정수 바운딩박스를 돌려준다. 카테고리는 딱 5종으로 제한: upperbody(상의) · wholebody_up(원피스류) · lowerbody(하의) · accessories_up(액세서리) · shoes(신발). 앱은 이 박스를 픽셀로 환산하고 max(12px, 8%) 여백을 더해 crop을 떠낸다.
여기가 핵심 트릭이다. 앱은 먼저 옷의 주색과 가장 먼 크로마색을 고른다(chooseChromaKey: 초록 #00ff00·마젠타 #ff00ff·시안 #00ffff 중 RGB 거리 최대). 그 색을 프롬프트에 넣어 gpt-image에 "그 단색 배경 위에 옷만" 그리게 한다. 실제 프롬프트(buildGarmentPrompt)의 배경·금지 조항이 특히 촘촘하다:
# buildGarmentPrompt — 핵심 조항 (요약 발췌)
Primary request: Reconstruct ONLY the complete empty ${name} (${category})
as a clean, front-facing ecommerce catalog product photograph.
If a wearer is present, remove them. ...no person, body, mannequin, or hanger.
Background: Perfectly flat, absolutely uniform solid ${chromaKey} chroma-key
color, edge-to-edge. No shadows, gradient, texture, vignette, reflection...
Critical: Use no ${chromaKey} anywhere in the garment.
Produce exactly one complete garment with a crisp, separable outer silhouette.
실제 API 호출은 /images/edits에 멀티파트 폼으로 나간다. OpenAI SDK 없이 순수 fetch+FormData다:
// openAIEdit — /images/edits 멀티파트 호출
const form = new FormData();
form.set("model", model); // gpt-image-2 (또는 OPENAI_GARMENT_MODEL)
form.set("prompt", prompt);
form.set("size", size); // 옷 컷아웃: "1024x1024"
form.set("quality", quality || "high");
form.set("output_format", "png");
for (const [i, image] of images.entries()) {
const normalized = await normalizeImage(image.data);
form.append("image[]", new Blob([normalized], { type: "image/png" }), ...);
}
const response = await fetch(`${baseUrl}/images/edits`, {
method: "POST", headers: { Authorization: `Bearer ${key}` }, body: form,
});
// result.data[0].b64_json → Buffer 로 디코드
모델이 준 이미지는 투명이 아니라 크로마색 배경이다. 진짜 투명화는 앱이 sharp로 직접 한다(removeChromaBackground) — 이 과정엔 AI가 전혀 없다:
이미지 모델은 "투명 배경"을 못 미덥게 처리하지만, "완벽한 단색 배경"은 잘 그린다. 그 강점만 쓰고, 신뢰가 필요한 누끼는 결정적(deterministic) 픽셀 연산으로 앱이 직접 한다. 여기에 "옷 색과 가장 먼 크로마를 고른다"는 동적 키색 선택까지 더해 "초록 옷을 초록화면으로 파다가 옷이 사라지는" 사고를 막는다. 튜토리얼들이 잘 안 알려주는 실전 트릭이다.
옷 컷아웃이 승인되면(선택) /images/edits를 한 번 더 부른다. 이번엔 이미지를 순서대로 두 장 넣는다 — data/model-reference.png(나) 먼저, 옷 PNG 나중, 크기 1536x1024(3:2). 프롬프트는 "Image 1의 사람이 Image 2의 옷을 입은 프로페셔널 에디토리얼 사진을 만들되 얼굴·정체성·나이·비율을 보존하고 옷의 색·소재·핏·로고를 그대로 유지"하라고 지시한다. 재생성 시엔 사용자의 지시문(User regeneration direction: ...)이 뒤에 덧붙는다.
/images/edits에 image[]로 여러 장을 순서 있게 넣으면, 모델이 "1번 사람 + 2번 옷"처럼 참조를 조합한다. wardrobe는 이걸로 "내 얼굴은 그대로, 옷만 이 옷으로" 하는 가상 피팅(try-on)을 구현한다. 순서가 의미를 갖는다는 점(정체성 원본이 먼저)이 포인트다.ORM도 SQL도 없다. 두 개의 JSON "테이블"과 작업별 상태 파일이 전부다. 완성된 옷은 data/library.json(평면 배열)에, 진행 중 작업은 data/jobs/<uuid>/job.json에 담긴다. 기록은 항상 원자적 쓰기(임시 파일 → rename, 실패 시 복사 폴백)로 안전하게 이뤄지고, 서버 재시작 시 중단된 작업을 스스로 정리·재개한다.
// data/library.json — 옷 한 벌의 레코드 모양
{
"id": "import-<uuid>", // Codex 경로에선 바이트 해시로 안정 UUID
"name": "Navy Fair Isle Cardigan",
"part": "wholebody_up", // 5종 카테고리 enum
"color": "#172033",
"secondaryColor": "#f2efe6", // hex 또는 null
"palette": ["#172033", "#f2efe6"],
"tags": ["knit", "fair isle", "zip"],
"image": "/api/import/library/import-<uuid>-garment.png",
"modeledImage": "/api/import/library/import-<uuid>-modeled.png", // 또는 null
"importJobId": "<uuid>"
}
사용자의 이름 변경·색 조정·삭제 같은 편집은 서버 레코드 위에 브라우저 localStorage(open-wardrobe-edits-v1·open-wardrobe-deleted-v1)로 얹힌다. 덕분에 모든 필드마다 쓰기 API를 만들지 않고도 UI가 즉각 반응하고 편집이 유지된다("낙관적 UI + 로컬 오버레이" 패턴).
src/는 프론트, scripts/는 "서버", .agents/는 에이전트 스킬구조를 읽는 열쇠는 "폴더 이름이 곧 역할"이다. src/는 브라우저에서 도는 클라이언트, scripts/는 Vite 서버 안에 사는 서버 로직, .agents/skills/는 앱 대신 에이전트가 같은 파이프라인을 돌리게 하는 Codex 스킬, public/은 PWA 자산이다. 그리고 실제 데이터가 쌓이는 data/는 런타임에 생성되고 저장소엔 없다(gitignore).
| 파일 | 줄 수 | 무엇을 배우나 |
|---|---|---|
| scripts/import-job-api.mjs | 711 | 이 저장소의 90%. 인식 프롬프트·크로마키 제거·despill·검증·원자적 저장·작업 GC가 전부 여기 |
| src/App.jsx | 650 | Canvas로 색 팔레트 추출, 알파 인식 스포이드 샘플링, localStorage 오버레이 병합 |
| src/import-flow.jsx | 294 | drag/drop/paste 입력 + 3단계 리뷰 상태기계, 900ms 폴링, 낙관적 갤러리 추가 |
| .agents/skills/…/import-to-wardrobe.mjs | — | PNG 검증(RGBA·투명+불투명 픽셀 필수) + 바이트 해시 → 안정 UUID로 멱등 임포트 |
| 주제 | 이 저장소에서 배울 구체적인 것 |
|---|---|
| Vite 플러그인 | configureServer/configurePreviewServer 미들웨어로 백엔드 없이 REST API 한 벌 짓기. apply:"serve"의 의미 |
| 크로마키 · despill | 초록화면 합성의 실전 수학 — 거리 기반 키잉, feather 램프, spill = keyed − neutral 스필 제거, 검증 후 throw |
| 구조화 출력 | OpenAI Responses의 json_schema로 enum·hex 정규식·정규화 바운딩박스를 강제해 파싱 가능한 인식 결과 얻기 |
| 다중 참조 편집 | /images/edits에 순서 있는 image[](정체성+옷)로 정체성 보존 가상 피팅 |
| 로컬 영속성 | 원자적 JSON 쓰기(tmp+rename+폴백), 작업별 폴더, 재시작 자가 복구, 내용 주소 기반(content-addressed) 안정 UUID로 멱등 재임포트 |
| 낙관적 UI | 서버 레코드 + localStorage 편집/삭제 오버레이 병합으로 즉각 반응하는 UI |
| 클라이언트 색 과학 | Canvas 팔레트 추출(버킷 평균 + 최소 색거리), 알파 인식 스포이드 반경 탐색 |
| PWA 캐시 | /_ipx 리사이즈 엔드포인트에 stale-while-revalidate + LRU(최대 800개) 이미지 캐시 |
| 앱=스킬 미러링 | 같은 파이프라인을 Codex SKILL.md로도 표현(QA 루프·매니페스트·결정적 임포터) → "앱을 에이전트가 조작 가능하게" |
규모가 작아서(파일 30개) 하루면 전체를 다 읽을 수 있다. 그런데도 "AI 앱을 실제로 만들 때 마주치는 결정들"이 압축돼 있다 — 모델을 어디까지 믿고 어디부터 직접 풀까, 투명 배경을 어떻게 신뢰성 있게 뽑을까, 서버 없이 로컬 도구를 어떻게 완결할까, 앱 로직을 어떻게 에이전트도 쓰게 노출할까. "큰 프레임워크에 기대지 않고 문제를 직접 푸는" 감각을 기르기에 이상적이다.
| 항목 | 요구 |
|---|---|
| Node.js | 22 이상 (CI가 node 22 고정, README 배지 Node 22+). npm(package-lock.json) |
| OpenAI API 키 | 필수. 비전·이미지 모델 접근 권한 필요. OPENAI_API_KEY 환경변수 |
| 내 참조 사진 | data/model-reference.png — 웹 임포터 활성화 조건이자 "입은 모습" 생성에 필요 |
| GPU | 불필요. 무거운 이미지 연산은 OpenAI(클라우드) 또는 CPU sharp(libvips)가 처리 |
| 네이티브 의존성 | sharp가 사전 빌드 바이너리를 받음 — 사실상 유일하게 신경 쓸 네이티브 부분 |
| 클라우드/배포 | 없음. 클론 → npm install → .env+참조 PNG → npm run dev → localhost:5173 |
웹 UI의 임포터는 GET /api/import/config가 ready: hasApiKey && hasModelReference를 반환할 때만 활성화된다. 즉 옷 컷아웃만 뽑고 싶어도 앱에선 내 참조 사진(data/model-reference.png)이 있어야 문이 열린다. "누끼만 필요한데 왜 내 사진을?" 싶다면, 참조 사진이 필요 없는 Codex 스킬 경로(import-clothes)를 쓰면 된다. 참고로 개인 사진을 다루는 도구이므로, 참조 사진과 data/ 폴더는 내 기기 밖으로 나가지 않는다.
.env.example에 없지만 실제로 읽는 환경변수 4개OPENAI_API_BASE_URL(기본 https://api.openai.com/v1 — 프록시·Azure·호환 엔드포인트로 바꿀 수 있음), WARDROBE_DATA_DIR(기본 data), 그리고 단계별로 다른 이미지 모델을 쓰게 해 주는 OPENAI_GARMENT_MODEL·OPENAI_MODELED_MODEL(둘 다 없으면 OPENAI_IMAGE_MODEL로 폴백). 고급 설정을 하려면 .env.example만 보지 말고 소스를 봐야 한다.클론 후 .env에 실제 OpenAI 키와 지금 존재하는 비전·이미지 모델명을 OPENAI_VISION_MODEL·OPENAI_IMAGE_MODEL로 지정한다. data/model-reference.png에 내 사진을 넣고 npm run dev. 옷 사진 하나를 임포트해 crop→garment→modeled 3단계가 도는 걸 눈으로 확인하자. 미래형 기본 모델명(§3 주의)을 왜 덮어써야 하는지 체감하는 게 목표다.
scripts/import-job-api.mjs의 openAIAnalyze에 console.log를 넣어 모델이 돌려준 items[](이름·카테고리·색·바운딩박스)를 찍어 보자. json_schema가 어떻게 출력을 강제하는지, 정규화 1000×1000 박스가 실제 픽셀로 어떻게 환산되는지(cropDetectedItem) 따라가면 "구조화 출력 → 결정적 후처리"의 연결이 보인다.
removeChromaBackground의 tolerance(기본 46)·feather(80)를 바꿔 가며 가장자리 잔털과 "옷이 파여 나감" 사이의 트레이드오프를 눈으로 비교하자. 초록·마젠타·시안 중 어떤 옷에서 어떤 키색이 유리한지(chooseChromaKey) 직접 확인하면, "동적 키색 선택"이 왜 필요한지 손에 잡힌다. 심화: 검증에서 throw하는 대신 더 먼 키색으로 자동 재시도하게 고쳐 보기.
Vite 미들웨어에 GET /api/import/stats를 새로 만들어 library.json을 읽어 카테고리별 옷 개수·색 분포를 반환하게 해 보자. configureServer 미들웨어에 라우트를 어떻게 끼워 넣는지, 응답을 어떻게 직렬화하는지 익히는 과제다. Express 없이 REST를 짓는 감각을 기른다.
현재 generate-outfits 스킬은 data/outfits.json과 정사각 룩북 이미지를 만들지만, React 앱엔 이를 보여줄 라우트가 없다(§11 함정). outfits.json을 서빙하는 미들웨어 라우트 + 갤러리 옆 "코디" 탭을 추가해 in-app으로 렌더해 보자. 저장부터 서빙·표시까지 한 기능을 끝까지 관통하는 종합 과제다. 심화: 미들웨어를 독립 Express/Hono 서버로 떼어내 정적 호스팅에도 배포 가능하게 만들기.
| 주차 | 주제 · 학습 활동 |
|---|---|
| 1주차 | Vite 플러그인 시스템. configureServer·apply·configResolved 훅을 Vite 플러그인 문서로 익히고, import-job-api.mjs가 어떻게 "서버"가 되는지 그려 보기. |
| 2주차 | sharp / 이미지 픽셀 연산. raw RGBA 접근, 합성, 채널 조작. 크로마키·despill·프레이밍 코드를 한 줄씩 재현. 초록화면 합성의 원리를 밑바닥부터. |
| 3주차 | OpenAI 구조화 출력. Responses API의 json_schema, enum·pattern 강제. "자연어 → 파싱 가능한 데이터"의 신뢰성 설계. |
| 4주차 | 이미지 편집/생성 API. /images/edits 멀티파트, 다중 image[] 참조, size·quality·output_format. 정체성 보존 가상 피팅의 프롬프트 엔지니어링. |
| 5주차 | 로컬 퍼스트 영속성. 원자적 파일 쓰기, 내용 주소 기반 UUID(멱등), 재시작 자가복구. Local-first 에세이로 철학적 배경까지. |
| 6주차 | 앱을 에이전트 스킬로. .agents/skills/의 SKILL.md·openai.yaml을 읽고, 내가 짠 기능 하나를 Codex 스킬로도 노출해 보기. "GUI ↔ 에이전트" 양면 설계. |
cat-media(이미지·얼굴) 딥다이브와 "같은 이미지 문제를 어떻게 다르게 풀었나"를 비교하고, Codex 스킬 쪽은 cat-claude-skills 딥다이브와 나란히 보면 학습이 배가된다.| 용어 | 뜻 |
|---|---|
| 로컬 퍼스트(local-first) | 데이터 원본·저장소가 내 기기에 있고, 외부 서비스는 계산만 잠깐 빌려 쓰는 설계. wardrobe는 data/에만 저장(gitignore). |
| 컷아웃 / 누끼 | 배경을 지운 투명 PNG. wardrobe의 목표 산출물. 단, 투명화는 모델이 아니라 앱이 크로마키로 처리. |
| Vite 미들웨어 플러그인 | configureServer로 개발 서버 요청 파이프라인에 함수를 끼워 REST API를 구현. apply:"serve"=서빙 때만 작동, 빌드 땐 빠짐. |
| 3단계 상태기계 | 옷 한 벌이 crop → garment → modeled를 거침. 각 단계 = 생성 후 사용자 리뷰(승인/거절/재생성). |
| Responses API · json_schema | OpenAI 비전 호출. 엄격한 스키마(enum·hex·정규화 1000×1000 박스)로 파싱 가능한 옷 인식 결과를 강제. |
| 크로마키 · chooseChromaKey | 단색(초록/마젠타/시안) 배경을 투명으로 파내는 합성 기법. 옷 색과 가장 먼 키색을 골라 옷이 파이는 걸 방지. |
| despill(스필 제거) | 옷 가장자리에 밴 키색 기운을 중화. spill = keyedLevel − neutralLevel을 여러 패스 반복해 뺌. |
| feather / tolerance | tolerance(기본 46)=완전 투명 경계, feather(80)=부분 투명 램프. 가장자리를 부드럽게 하는 두 손잡이. |
| verifyNoChromaSpill | 완성 컷아웃에 키색 오염 픽셀이 남았는지 검사. 엄격 모드에서 남으면 throw → 수동 cleanup UI로. |
| 다중 참조 편집(image[]) | /images/edits에 순서 있는 여러 이미지(정체성+옷)를 넣어 정체성 보존 가상 피팅을 구현. |
| 내용 주소 기반 UUID | Codex 임포터가 컷아웃 바이트의 sha256으로 안정적 UUID 생성 → 재-임포트 시 중복 없이 갱신(멱등). |
| 낙관적 UI + localStorage 오버레이 | 서버 레코드 위에 브라우저 편집/삭제를 얹어, 필드별 쓰기 API 없이도 즉각 반응·유지. |
| Open Wardrobe | 제품 브랜딩 이름(HTML 타이틀·매니페스트·라이선스). dedup 기준 식별자는 tandpfun/wardrobe. |
공식
· GitHub 저장소: github.com/tandpfun/wardrobe (MIT · v1.0.0)
· 원 게시물(데모): x.com/cdngdev/status/2076812846793650485
· TrendShift 카드: trendshift.io/repositories/84145 (Daily #7)
저장소 안에서 먼저 열 곳
· scripts/import-job-api.mjs — 인식·컷아웃·화보·저장의 심장부(§4)
· src/import-flow.jsx — 3단계 리뷰 상태기계 + 폴링/낙관적 UI
· src/App.jsx — 갤러리·팔레트 추출·스포이드·localStorage 오버레이
· vite.config.mjs — "서버 없는 서버"가 배선되는 곳
· .agents/skills/import-clothes/SKILL.md — 같은 파이프라인의 에이전트 버전
· .env.example + README.md — 설정과 요구사항(숨은 변수는 소스로 보완)
곁들여 학습(표준·이웃)
· Vite 플러그인 API · sharp 문서 — 미들웨어·이미지 연산의 표준
· OpenAI Images 가이드 · Responses API — 인식·생성 호출의 원전
· §인덱스의 다른 cat-media(이미지·얼굴) · cat-claude-skills 딥다이브와 비교
① Next.js가 아니다 — Vite SPA + apply:"serve" 미들웨어. 임포터는 vite dev/preview에서만 살아 있고 정적 빌드엔 /api/import/*가 없다. npm run check는 그냥 빌드(테스트·린트 없음). ② 미래형 모델명 gpt-5.4-mini·gpt-image-2는 하드코딩 — 실존 모델명으로 덮어써야 함. ③ 투명은 앱이 만든다 — gpt-image에 투명을 안 맡기고 크로마키+로컬 제거. ④ .env.example 불완전 — OPENAI_API_BASE_URL·WARDROBE_DATA_DIR·단계별 모델 변수 누락. ⑤ 임포터 이중 잠금 — API 키 그리고 data/model-reference.png 둘 다 필요(Codex 경로는 예외). ⑥ 클론하면 텅 빔 — data/가 gitignore라 샘플 없음. ⑦ GET /api/import/jobs가 읽으면서 완료/거절 작업을 삭제(GC)하는 비멱등 설계 + 코디(outfits) 화면 미구현 + 개발서버 allowedHosts:["terminal.local"] 저자 환경 흔적. 트렌딩 순위·수치는 계속 바뀐다.