MAKITTHandoff
Handoff Boards
finishedproduct-admin-and-library-revamp

E2E Handoff — Product Info Property + Sales Rules

E2E Handoff — Product Info Property + Sales Rules

핵심 spec: ./product-info-property-and-sales-rules.md

Phase 0 testbed에서 PDP 노출 + 어드민 IA까지 본 사이클로 끝났다. 남은 제품/정책 의사결정은 backlog/product-revamp-followups에서 추적한다.


목적: 스크린샷/브라우저 눈검수가 아니라, makitt web 상품 form submit이 실제 admin API 저장 → shop-api PDP 응답까지 연결되는지를 스크립트로 검증한다.

검증 대상

  • Shop: henriette (71261f65-ee4e-4238-9b4f-d56cee9be523)
  • Product: 세피라샷 해파리 모공 세럼 (386d4ca2-6dbf-43bb-9d5e-47e215589ebc)
  • Slug: cephira-shot-jellyfish-pore-serum
  • Seller token: chance@makitt.io 개발자 토큰
  • Admin API: http://localhost:8080
  • Shop API: http://localhost:10000

Shop API i18n dev-bypass

E2E는 브라우저 쿠키 없이 shop-api customer/i18n context를 시뮬레이션한다.

curl \
  -H "Authorization: Bearer dev-bypass" \
  -H "X-Dev-Language: en" \
  -H "X-Dev-Currency: USD" \
  http://localhost:10000/shop/{shopId}/products/by-slug/{slug}
  • Authorization: Bearer dev-bypass: local/dev customer context 우회 생성
  • X-Dev-Language: PDP translation overlay language override
  • X-Dev-Currency: pricing context / price label currency override
  • 자세한 운영 메모: .agents/memory/shop_api_dev_bypass_i18n.md

E2E 스크립트

node scripts/e2e/product-i18n-form-submit.mjs

스크립트가 재현하는 흐름:

  1. POST /api/v1/admin/developer/token/email로 henriette seller token 발급
  2. GET /api/v1/products/{productId}/detail로 ProductFormV2 edit form의 초기 source payload 확보
  3. PUT /api/v1/products/{productId}로 form edit submit과 같은 product update 요청 수행
  4. GET /api/v1/shops/{shopId}/products/{productId}/translations로 언어별 draft 로드
  5. PUT /api/v1/shops/{shopId}/products/{productId}/translations/{language}로 번역 draft 저장
  6. GET /shop/{shopId}/products/by-slug/{slug}를 ko/KRW, en/USD, ja/JPY context로 호출해 PDP 응답 검증

통과 기준

  • Product update가 base product의 name / featuredPrice를 보존한다.
  • Product translation API가 en, ja translatedFields를 round-trip 저장한다.
  • Shop API PDP가 언어별로 아래 필드를 번역 적용한다.
    • name
    • shortDescription
    • description
    • categoryName
    • brandName
    • seo.title → seoTitle
    • seo.description → seoDescription
  • Shop API PDP가 가격 context를 별도 파이프라인으로 적용한다.
    • ko/KRW
    • en/USD
    • ja/JPY
    • 각 응답의 currencyCode, featuredPrice, featuredPriceLabel 검증

결과

2026-05-02 로컬 재실행 서버 기준 통과:

✓ Product i18n form-submit E2E passed

확인된 구현 경계

  • 상품 본문/SEO 중심 번역은 UX → admin API → DynamoDB ContentTranslation → shop-api PDP까지 연결됐다.
  • 가격은 상품 번역 row가 아니라 shop preference의 market locale / pricing context를 통해 적용된다. 따라서 상품 국제화 UX에서는 마켓별 통화 context를 보여주되, 가격 변환·표시는 shop-api pricing pipeline을 검증 대상으로 둔다.
  • 생성 form에서도 번역 draft를 입력한 뒤 product 생성 성공 후 새 productId로 translation row를 저장할 수 있다.
  • shop-api by-slug endpoint는 기존부터 translation overlay + price conversion을 수행했고, by-id endpoint도 동일하게 overlay/conversion을 적용하도록 맞췄다.

Production blocking 기준

Production blocking = henriette shop PDP에서 target locale로 보이는 모든 고객 노출 문자열과 고객 노출 상품 자산이 국제화되어야 한다. Core product fields만 번역되는 상태는 통과가 아니다. en/USD, ja/JPY PDP 응답에서 한국어가 고객 노출 영역에 남아 있거나 locale별 이미지/가격 override가 적용되지 않으면 blocker로 본다.

2026-05-06 재검증 결과:

  • en/USD: visible-copy Hangul audit 통과
  • ja/JPY: visible-copy Hangul audit 통과
  • PDP price context: ko/KRW, en/USD, ja/JPY 통과
  • PDP image translation: image.main.{index}.url, image.detail.{index}.url, image.thumbnail.url 통과
  • PDP resolve visible-copy audit: en, ja 통과

2026-05-06 field-level coverage

  1. Product core — name, shortDescription, description, categoryName, brandName, supplierName 번역 저장·적용.
  2. SEO — seo.slug, seo.title, seo.description 번역 저장·적용.
  3. Images — main/detail/thumbnail image URL을 locale별 translation field로 저장하고 ProductDocument overlay에 적용.
  4. Variant/options — variant group, option, option value, combination, store item display name 번역 저장·적용.
  5. Additional info — property group title, property label, property description, property value 번역 저장·적용.
  6. Pricing — 기본은 shop-api pricing context/FX pipeline을 사용하고, product/store item 단위 currency override를 admin submit payload로 저장·적용.
  7. Shipping option labels — shop-api shippingOptions customer-facing label에서 locale별 기본 문구 적용.

2026-05-06 E2E command

node scripts/e2e/product-i18n-form-submit.mjs --use-expected-translations

결과:

→ Developer token: chance@makitt.io
→ Load product detail: 71261f65-ee4e-4238-9b4f-d56cee9be523/386d4ca2-6dbf-43bb-9d5e-47e215589ebc
→ Submit idempotent product update, matching ProductFormV2 edit submit
→ Load product translation drafts
→ Submit product translation drafts
→ Assert shop-api PDP language + currency pipeline
✓ Product i18n form-submit E2E passed

남은 UX/운영 확인

  • makitt web은 실제 빌드 산출물 기준으로 확인해야 하므로, 최신 form UI 확인 전 web rebuild가 필요하다.
  • ProductFormV2에는 별도 “상품 국제화” 섹션을 만들지 않고, 기본정보/이미지/옵션/추가정보/SEO 섹션 내부에 표준 i18n 패널을 배치한다.
  • 가격 override는 대표가격과 store item 가격에 각각 제공한다. market profile에서 통화가 비어 있어도 언어 설정 기반 fallback currency를 노출한다.
  • custom image group 자체의 PDP contract 적용은 main/detail image pipeline과 별개다. 현재 production blocker는 세피라샷 PDP의 main/detail/thumbnail customer-facing asset 기준으로 닫는다.

구현/검증 항목은 2026-05-06 closeout에서 완료했다. 남은 의사결정 항목은 finished 문서에 open checklist로 유지하지 않고 backlog/product-revamp-followups로 분리했다.

완료된 closeout 범위:

  • 셀러 페르소나 풀스택 검증은 shop-api PDP E2E + web prod form smoke로 대체 검증했다.
  • /merchant/products/policies 판매 설정 IA와 legacy /merchant/products/shipping route 제거를 확인했다.
  • SystemPropertyTemplateSeeder는 legacy/unknown status row 방어 + makitt-api 전용 best-effort seed + makitt-shop-api skip 로그를 확인했다.
  • PDPPreviewV2는 form state 기반 propertyGroups 시뮬레이션을 지원한다.
  • HCS validator pre-commit guard, Template delete-impact UX, legacy policy migration/cleanup endpoint, propertyTemplateKey 잔여 제거를 완료했다.

검증된 testbed 데이터

  • finn shop (3d08aa8c-c31d-48a8-aae2-9c8e0a3ce7ac) — 화장품 1개 product
  • henriette shop (71261f65-ee4e-4238-9b4f-d56cee9be523) — 화장품 7개 + 식품 1개

각 shop에 시드된 system templates: purchase.payment.note, purchase.return.note, legal.kr.standard.cosmetic. 같은 키를 운영 시드에서도 사용하므로, 운영 마이그레이션 작성 시 멱등성 보장(이미 존재하면 skip).

빌드 상태

  • 서버: ./gradlew build -x test 통과
  • 클라이언트: pnpm check-types --filter=@makitt/web 통과
  • E2E: shop-api 응답 + PDP 빌더 미리보기 모두 검증

세피라샷 상품 수정 화면을 실제 makitt web prod build로 열어 국제화 UX를 브라우저에서 확인했다.

UI 확인 결과

  • 기본정보/이미지/옵션·판매항목/추가정보/추천영역/SEO 섹션 내부에 표준 언어 탭 기반 i18n 패널이 렌더된다.
  • 각 패널은 English (en), 日本語 / Japanese (ja) 탭과 입력 수 요약을 표시한다.
  • 긴 필드 목록은 그룹 disclosure로 나뉜다.
    • 기본정보: 상품 표시, 분류/브랜드
    • 이미지: 메인 이미지, 상세 이미지
    • 옵션: 옵션 표시명, 옵션 값, 조합 표시명, 판매 항목 표시명
    • 추가정보: 구매정보, 법정고지 등 property group별
    • 추천영역: 추천 섹션 제목
    • SEO: 검색 노출
  • 이미지 현지화 필드는 thumbnail preview와 URL 입력을 함께 보여준다.
  • 가격 override는 대표가격과 판매항목 가격 모두 KRW 기준가/비교가와 자동 환율/수동 가격 상태를 표시한다.
  • SEO preview는 active translation language의 title/description/image/slug 값을 사용한다.
  • 브라우저 console의 duplicate form field id issue는 제거됐다.

재검증

node scripts/e2e/product-i18n-form-submit.mjs --use-expected-translations

결과:

✓ Product i18n form-submit E2E passed

추가 안정화

  • shop-api PDP detail은 review OpenSearch stats timeout이 발생해도 상품 detail 전체를 500으로 실패시키지 않고 review summary fallback을 반환한다.

Translation data cleanup — 2026-05-06

E2E seed가 production visible-copy 검증을 위해 넣던 placeholder 문자열을 실제 세피라샷 번역값으로 교체했다.

  • 제거 대상: English value ..., English label ..., 日本語 value ..., 日本語 label ... 등 dummy translation copy

  • 정정 범위: option group/value/combination/store item, purchase/legal property group/label/value

  • scripts/e2e/product-i18n-form-submit.mjs --use-expected-translations는 앞으로도 실제 번역 copy를 seed한다.

  • API 재조회 결과: en, ja translation row 모두 dummy key 0건

  • shop-api PDP 재조회 결과: en/USD, ja/JPY 모두 placeholder/hangul visible-copy audit clean

  • Changed image localization from per-image translation fields to product imageGroups media sets.

  • Seeded Sephyra Shot localized media sets through the same admin product update payload used by ProductFormV2:

    • localized:en:main / localized:en:detail
    • localized:ja:main / localized:ja:detail
  • Verified admin translation rows no longer persist image.* URL override keys for English/Japanese.

  • Verified shop-api PDP applies localized media sets for English/Japanese:

    • mainImages[0] matches the localized main group first image.
    • thumbnailImage follows the localized main group first image.
    • detailImages[0] matches the localized detail group first image.
  • Verified Product Admin edit page renders the image UI as 언어별 이미지 세트, not the legacy 이미지 현지화 per-image field panel.

  • Browser smoke: http://makitt.localhost:3002/en/merchant/products/386d4ca2-6dbf-43bb-9d5e-47e215589ebc

    • Active shop: Henriette MAKITT ADMIN
    • UI snippets: English · 메인 6 / 상세 33, 日本語 · 메인 6 / 상세 33, 기본 메인 이미지 복사
    • Console error count: 0 after aligning edit form shopId to the loaded product's shop.

Validation commands:

cd makitt-server && ./gradlew :makitt-shop-api:compileJava -Dorg.gradle.java.home=/Library/Java/JavaVirtualMachines/temurin-21.jdk/Contents/Home -Dfile.encoding=UTF-8
cd makitt-client && pnpm --filter web check-types
node --check scripts/e2e/product-i18n-form-submit.mjs
./makitt-devctl.sh restart shop-api
./makitt-devctl.sh restart web
node scripts/e2e/product-i18n-form-submit.mjs --use-expected-translations

Validation commands run after devctl restart:

cd makitt-server && ./gradlew :makitt-api:compileJava :makitt-shop-api:compileJava -Dorg.gradle.java.home=/Library/Java/JavaVirtualMachines/temurin-21.jdk/Contents/Home -Dfile.encoding=UTF-8
cd makitt-client && pnpm --filter web check-types
./makitt-devctl.sh restart api
./makitt-devctl.sh restart shop-api
./makitt-devctl.sh restart web
node scripts/e2e/product-i18n-form-submit.mjs --use-expected-translations

Results:

  • makitt-api / makitt-shop-api compile: passed.
  • @makitt/web check-types: passed.
  • Product i18n form-submit E2E: passed.
  • Hardening API E2E: passed.
    • system templates present for Henriette shop.
    • legal.kr.standard.cosmetic delete-impact returned attachedProductCount=7.
    • media tree returned { unassigned, other, products }.
    • legacy product policy migration returned shopsScanned=56, shopsMigrated=1, policiesCleaned=1.
  • Browser smoke: actual prod web build loaded http://makitt.localhost:3002/en/merchant/products/386d4ca2-6dbf-43bb-9d5e-47e215589ebc; product form showed embedded i18n panels and no English value / English label placeholder leak.
  • Browser smoke: http://makitt.localhost:3002/en/merchant/content/media showed media tree 전체 / 미분류 / 기타 / 상품 sidebar.
  • Service log: makitt-shop-api reported System property template seed skipped: applicationName=makitt-shop-api; seeder now runs from makitt-api only.

Deferred decision-only items:

  • Legal review for Korean cosmetic disclosure fields.
  • Category-to-template preset auto-attach behavior.
  • Fragment rendering strategy for non-text property types.
  • PDP group visibility control in commerce library.

비어 있음

당신이 처리할 항목

docs / spec / finished / product-admin-and-library-revamp / e2e.md