Skip to content

feat(cody): ① Upload 화면 (Storage real) #627

Description

@thxforall

목적

코디 재현 엔진 MVP의 진입점 화면 ①. 사용자가 인플루언서/셀럽 룩 이미지를 업로드(드래그-드롭/파일/카메라/URL)하면 Storage에 저장 후 분석 단계(②)로 진입. E1(Upload-first) 엔트리의 P1 파이프라인 구현. 동시에 E2(Content-first)·E3(Search-first) 진입 보조 CTA 제공.

범위

  • 업로드 인터페이스: Drag-drop · File picker · 카메라 캡처 · URL 붙여넣기
  • Image 전처리: 압축(compressImage) + Storage 업로드(uploadImage)
  • 새 cody 전용 상태 관리: codyStore (useImageUpload 재사용 금지)
  • 진입점 보조: 자연어 검색(E3) / 추천 룩 둘러보기(E2) CTA
  • 실패상태 소유: 비패션·저해상도 이미지 감지 및 사용자 안내
  • 인증 정책: 비로그인 가능 (분석·추천은 로그인 불필요)

화면 명세

와이어프레임 출처: wireframes.md §2 ①

┌────────────────────────────┐
│  decoded               ☰   │
│                            │
│   코디 스샷을 올려보세요    │
│                            │
│   ┌────────────────────┐   │
│   │   ⤒  드래그 & 드롭 │   │
│   │     또는 파일 선택   │   │
│   └────────────────────┘   │
│   [ 📷 카메라 ]  [ 🔗 URL ] │
│                            │
│   ── 또는 ──                │
│   [ 🔍 자연어로 검색 ]  → E3 │
│   [ ✨ 추천 룩 둘러보기 ] → E2│
└────────────────────────────┘

라우트: packages/web/app/cody/page.tsx

컴포넌트 위치: packages/web/lib/components/cody/ (DropZone, UploadButton 등)

로직 위치: packages/web/lib/ (compressImage, uploadImage, codyStore)

Acceptance Criteria

  • Upload 화면 라우트 (/cody) 구현 완료. 모바일 우선 레이아웃(360–390px viewport, wireframes §4 참조).

  • Drag-drop 업로드: DropZone 컴포넌트로 파일 드래그 시 자동 선택. 피드백 상태(hover, active) 표시.

  • File picker: [파일 선택] 버튼 → HTML5 input type=file, accept="image/*". 다중 파일 선택 시 첫 파일만 처리(MVP).

  • 카메라 캡처: [📷 카메라] 버튼 → 모바일 카메라 API 호출 (getUserMedia), 캡처 후 자동 압축.

  • URL 붙여넣기: [🔗 URL] 버튼 → input 모달 → URL 검증 후 fetch → 이미지 전처리.

  • Image 압축: compressImage(file, {maxWidth: 1080, maxHeight: 1080, quality: 0.85}) 호출. 결과 크기 < 500KB 목표(wireframes §4 비기능).

  • Storage 업로드: 압축 후 uploadImage(compressedBlob, { codyUploadPath: true })raw_post_images/{uuid}.jpg 경로. 응답으로 imagePath·URL 수신.

  • 로딩 상태: 업로드 중 ⟳ 스피너 표시. "이미지를 업로드하는 중..." 텍스트.

  • 비패션·저해상도 실패: 업로드 후 클라이언트 시점 이미지 검증(dimension < 720px 또는 파일 크기 < 50KB 경고). 오류 메시지 "패션 코디 이미지를 올려주세요. 명확한 의류 이미지 권장" + [재시도] CTA.

  • 성공 전환: 업로드 성공 시 /cody/analysis?imageUrl=${URL}&imagePath=${path} 로 네비게이트 (P1→P2). imagePath/imageUrl을 codyStore에 저장.

  • 진입 보조 CTA: [🔍 자연어로 검색] → /cody/recommended?search=... (E3). [✨ 추천 룩 둘러보기] → /cody/recommended (E2). 둘 다 비로그인 가능.

  • useImageUpload 재사용 금지: 내부 requestStore·startDetection() 호출이 옛 detection flow 유발. 대신 new upload hook(useImageUpload 아님) + codyStore만 사용.

  • VTON 분리: lib/components/cody/ 내 코드는 lib/components/vton, lib/stores/vtonStore, /api/v1/vton import 0건. smoke test로 검증.

  • 에러 바운더리: Upload 실패(네트워크/서버 5xx) 시 "다시 시도해주세요" 재시도 버튼 제공.

  • Fixture·Mock: F1 이 생성한 단일 adapter lib/api/adapters/cody-match.ts 에서 이미지URL만 extract. mock 은 lib/api/adapters/cody-match-mock.ts 사용(F7 reconcile 대상).

핵심 파일

경로 용도
packages/web/app/cody/page.tsx Upload 화면 라우트 (신규)
packages/web/lib/components/cody/DropZone.tsx Drag-drop 컴포넌트 (신규)
packages/web/lib/components/cody/ImageUploadForm.tsx 전체 업로드 폼 (신규)
packages/web/lib/stores/codyStore.ts Cody 상태 관리 (신규, useImageUpload 재사용 금지)
packages/web/lib/utils/compressImage.ts 이미지 압축(기존 재사용 가능)
packages/web/lib/utils/uploadImage.ts Storage 업로드(기존 재사용 가능)
packages/web/lib/api/adapters/cody-match.ts F1 생성 단일 adapter (F2 읽기만)

금지 경로: /recreate, app/request, app/(request), app/(consumer), app/cody/screens, app/cody/detail, codyAnalysisAdapter.ts, useCodyMatch.ts, CodyEngineAdapter.

Mock·Real 경계

Contract 출처: cody-match-stage2 §2.1 CodyAnalysis input schema (Stage 2 소비 필드 기준).

F2 단계에서는 이미지URL·imagePath 저장만 — 실제 분석(VLM)은 F3(Analysis 화면)에서.

  • Mock: codyStore에 fixture image URL(e.g., /api/v1/cody/fixtures/sample-outfit.jpg) 저장. F7(Mock→Real reconcile) 때 확인.
  • Real: Storage 응답의 이미지URL → codyStore 기록.

의존성

  • F1 (Cody Match Adapter 단일화): F2가 F1 생성 adapter (lib/api/adapters/cody-match.ts)를 읽기만 하므로, 타입·entry function 확정 필수.
  • compressImage, uploadImage: 기존 유틸 재사용 (packages/web/lib/utils/).
  • Storage: Supabase Storage 또는 기존 uploaded-image handling 흐름.

제약·주의

  1. useImageUpload 절대 금지: 이 hook은 requestStore + startDetection()을 호출하는 구 requestor 중심. codyStore와 충돌 가능. 새 hook(예: useCodyImageUpload) 작성.

  2. 타입 mirror 준수: 최종적으로 F3(Analysis)이 소비할 CodyAnalysis 의 모든 필드는 stage2 §2.1 참조. F2는 imagePath/imageUrl만 저장하므로 타입 충돌 없음. 단, fixture 작성 시 cody-match-stage2 §2.2 full 형태 숙지.

  3. VTON 분리 필수: /cody/* 화면은 VTON modal/state 의존 금지. 감지: grep -r "vtonStore\|VtonModal\|/api/v1/vton" packages/web/app/cody packages/web/lib/components/cody 실행 후 0건 확인.

  4. 비패션 감지 한계: 클라이언트 side dimension/filesize 체크는 저해상도 이미지의 경고일 뿐, 100% 신뢰성 보장 안 함. 최종 검증은 F3(분석 confidence)에서 하고, F2는 UX friendly 조기 피드백만.

  5. Persona 가설: 본 와이어프레임은 Primary=Aspirational 기준 (raf 확정 6/11 예정, open-questions Q5.1). E1 흐름이 대상. 페르소나 변경 시 §5(user-scenarios) 스토리보드만 조정 — 화면 골격은 안정.

  6. Affiliate URL 미포함: F2 단계에선 affiliate_url 불필요 (cody-match-stage2 §7.1 출처: catalog_items). F5/F6에서 추가.

참조

  • wireframes.md §2① Upload spec · §4 비패션·저해상도 실패 상태
  • mvp-scope.md §1 P1 파이프라인 · §2① Upload 화면 · §4 비로그인 정책
  • user-scenarios.md §1 P1 · §2 ①→② 전환 · §3 E1 Upload-first entry · §4 비패션 실패
  • cody-match-stage2.md §2.1 CodyAnalysis input contract
  • CONTEXT.md Cody Recreation Engine glossary · Shopper Preference (fit 필드 없음)
  • decoded-docs/Project/personas.md — Primary(Aspirational) 가설

Metadata

Metadata

Assignees

No one assigned

    Labels

    codyCody Recreation Engine MVPenhancementNew feature or requestfrontend프론트엔드

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    Status
    Todo

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions