MAKITTDocs

Shop

docs/domain/shop.md

Shop

개요

쇼핑몰 생성, 관리, 커스텀 도메인, 버전 관리를 담당하는 핵심 도메인. 사용자(Seller)가 만드는 쇼핑몰의 메타데이터, 브랜딩, 현지화, SEO, 커스텀 도메인 등을 관리합니다.

서버 패키지: com.makitt.core.domain.shop


DynamoDB Entity

Shop

Single Table Design 기반의 메인 Shop 엔티티. 2개의 레코드로 구성됩니다:

  • METADATA 레코드 (PK=SHOP#{shopId}, SK=METADATA) — Shop의 모든 상세 정보
  • User Shop List 레코드 (PK=USER#{userId}, SK=SHOP#{shopId}) — 사용자의 Shop 목록 조회용

키 구조 (METADATA):

패턴예시
PKSHOP#{shopId}SHOP#shop123
SKMETADATAMETADATA

키 구조 (User Shop List):

패턴예시
PKUSER#{userId}USER#abc123
SKSHOP#{shopId}SHOP#shop123

필드:

필드DynamoDB Attribute타입필수설명
pkPKStringOPartition Key
skSKStringOSort Key
entityTypeentity_typeStringO"SHOP" 고정
shopIdshop_idStringOShop 고유 ID
userIduser_idStringO소유자 User ID
organizationIdorganization_idStringO소속 Organization ID
shopNameshop_nameStringO상점 이름
shopUrlshop_urlStringOMAKITT 서브도메인 (예: fashion-store)
statusstatusShopStatusO상점 상태
marketsmarketsList<String>X대상 마켓 코드 (예: ["KR", "US", "JP"])
primaryMarketCodeprimary_market_codeStringX기본 마켓 코드
brandingbrandingShopBrandingX브랜딩 정보 (nested)
localizationlocalizationShopLocalizationX현지화 설정 (nested)
seoseoShopSeoXSEO 설정 (nested)
customDomaincustom_domainCustomDomainX커스텀 도메인 (nested)
createdAtcreated_atInstantO생성 시각
updatedAtupdated_atInstantO수정 시각

Factory Methods:

  • Shop.create(shopId, userId, organizationId, shopName, shopUrl, markets, primaryMarketCode) — METADATA 레코드 생성. branding/localization/seo는 기본값으로 초기화.
  • Shop.createUserShopListRecord(userId, organizationId, shopId, shopName, shopUrl, markets, primaryMarketCode) — User Shop List 레코드 생성 (목록 조회용 최소 정보).

Nested Objects

ShopBranding

필드DynamoDB Attribute타입설명
descriptiondescriptionString상점 설명
logoUrllogo_urlString로고 이미지 URL
bannerImageUrlbanner_image_urlString배너 이미지 URL

ShopLocalization

필드DynamoDB Attribute타입설명
languagelanguageString기본 언어 (예: ko)
timezonetimezoneString타임존 (예: Asia/Seoul)
currencycurrencyString기본 통화 (예: KRW)
localizationConfirmedlocalization_confirmedBoolean국제화 기본값 확정 여부 (1회 확정 후 변경 불가)

비즈니스 규칙:

  • confirm(language, timezone, currency) — 1회 확정. 이미 확정된 경우 IllegalStateException 발생.
  • 확정 시 ShopOnboarding의 SHOP_PROFILE_SET.LOCALIZATION_CONFIRMED sub-step 자동 완료.
  • defaults() — 기본값 생성 (en, UTC, USD, localizationConfirmed=false).

주의 (DynamoDB Enhanced Client): @DynamoDbBean nested 클래스에서 Boolean 필드에 isXxx() 메서드를 정의하면 JavaBeans getter 충돌로 직렬화 실패. 반드시 Boolean.TRUE.equals(field) 인라인으로 사용할 것.

ShopSeo

필드DynamoDB Attribute타입설명
titletitleStringSEO 제목
descriptiondescriptionStringSEO 설명
imageUrlimage_urlStringOG 이미지 URL

CustomDomain

커스텀 도메인 설정 및 SSL 인증서 상태 관리.

필드DynamoDB Attribute타입설명
domaindomainString도메인 주소 (예: www.myshop.com)
statusstatusCustomDomainStatus도메인 상태
requestedAtrequested_atInstant등록 요청 시각
updatedAtupdated_atInstant상태 변경 시각
verifiedAtverified_atInstantDNS 인증 완료 시각 (ACTIVE 시)
currentIpscurrent_ipsList<String>현재 A 레코드 IP (FAILED 시)
failureReasonfailure_reasonString실패 사유 (FAILED 시)

상태 전이:

REGISTERED → PENDING → ACTIVE
                ↓
              FAILED → REGISTERED (retry)

Enums

ShopStatus

설명
DRAFT생성 중 (아직 발행 안 됨)
ACTIVE운영 중 (고객 접근 가능)
INACTIVE비활성화 (소유자에 의해 일시 중지 / 삭제)
SUSPENDED정책 위반으로 정지

CustomDomainStatus

설명
REGISTERED도메인 등록됨 — DNS A 레코드 확인 대기
PENDINGDNS 확인 완료 — SSL 인증서 발급 대기
ACTIVE인증서 발급 완료 — HTTPS 정상 동작
FAILEDDNS 확인 실패 — 사용자가 DNS 설정 수정 후 재시도 필요

GSI 키 구조

GSI용도PK 패턴SK 패턴
GSI1 (EntityLookupIndex)Organization의 Shop 목록ORGANIZATION#{organizationId}SHOP#{shopId}
GSI2 (UniqueLookupIndex)shopUrl 유니크 조회{shopUrl}SHOP#{shopId}
GSI3 (SecondaryIdIndex)커스텀 도메인 상태별 조회 (sparse)CUSTOM_DOMAIN_STATUS#{status}SHOP#{shopId}
GSI4 (FilterIndex)커스텀 도메인 이름 조회 (sparse)CUSTOM_DOMAIN#{domain}SHOP#{shopId}

Shop Onboarding

Shop별 온보딩 진행 상태. MAKITT HAPPEN (9 steps) + MAKITT EXPLODE (6 steps)를 추적합니다. Shop과 동일 파티션에 저장됩니다.

키 구조:

패턴예시
PKSHOP#{shopId}SHOP#shop123
SKONBOARDINGONBOARDING

상세 문서: ShopOnboarding

ShopOnboardingStep (HAPPEN)

Step설명필수Per-Market
SHOP_PROFILE_SET상점 프로필 설정OO
MARKET_SET마켓 설정OX
POLICIES_SET정책 설정OO
SHOP_DESIGN_SET상점 디자인OX
LOGISTICS_SET물류 설정XO
SHIPPING_SET배송 설정OO
PAYMENT_SET결제 설정OO
PRODUCT_REGISTERED상품 등록OX
PRICING_SET가격/세금 설정OO

BUSINESS_INFO_SET는 legacy (SHOP_PROFILE_SET sub-step으로 통합됨, happenSteps()에서 제외)

ShopOnboardingStep (EXPLODE)

Step설명필수Per-Market
PROMOTIONS프로모션XO
MEMBER_TIERS회원 등급XX
REWARDS리워드XX
COUPONS쿠폰XO
SOCIAL_LOGIN소셜 로그인XO
CRMCRM/세그먼트XX

ShopProfileSubStep (SHOP_PROFILE_SET의 하위)

Sub-Step설명필수Per-Market
LOCALIZATION_CONFIRMED국제화 기본값 확정OX
BASIC_INFO_SET기본 정보 설정OO
BRAND_ASSET_SET브랜드 에셋 설정OO
BUSINESS_INFO_SET사업자 정보 설정OO

주요 비즈니스 메서드

Shop 정보 수정

  • updateInfo(shopName, description, logoUrl, bannerImageUrl, language, timezone, currency, seoTitle, seoDescription, seoImageUrl) — 전체 정보 일괄 수정
  • updateBranding(description, logoUrl, bannerImageUrl) — 브랜딩만 수정
  • updateLocalization(language, timezone, currency) — 현지화만 수정
  • updateSeo(title, description, imageUrl) — SEO만 수정
  • updateMarkets(markets, primaryMarketCode) — 마켓 설정 수정
  • updateStatus(ShopStatus status) — 상태 변경

국제화 기본값 확정

  • ShopLocalization.confirm(language, timezone, currency) — 1회 확정 (되돌리기 불가). 이미 확정된 경우 예외 발생.
  • ShopApplication.confirmLocalization(shopId, language, timezone, currency) — 소유권 확인 → 확정 → DB 저장 → 온보딩 sub-step 완료

커스텀 도메인 관리

  • requestCustomDomain(domain) — 도메인 등록 요청 (REGISTERED), GSI3/GSI4 업데이트
  • activateCustomDomain() — 도메인 활성화 (SSL 인증서 발급 완료), GSI4 설정
  • failCustomDomain(currentIps, reason) — 도메인 발급 실패 (IP/사유 기록), GSI4 제거
  • retryCustomDomain() — 실패 후 재시도 (FAILED → REGISTERED)
  • removeCustomDomain() — 도메인 제거, GSI3/GSI4 제거
  • hasActiveCustomDomain() — 활성 도메인 여부
  • getCustomDomainName() — 도메인 주소 반환

API

Shop CRUD

메서드엔드포인트설명
POST/api/v1/shopsShop 생성 (초기 버전 자동 생성)
GET/api/v1/shops내 Shop 목록 조회
GET/api/v1/shops/{shopId}Shop 상세 조회
PUT/api/v1/shops/{shopId}Shop 메타데이터 수정
DELETE/api/v1/shops/{shopId}Shop 삭제 (soft-delete → INACTIVE)

Localization

메서드엔드포인트설명
POST/api/v1/shops/{shopId}/localization/confirm국제화 기본값 확정 (1회, 비가역)

Shop Onboarding

메서드엔드포인트설명
GET/api/v1/shops/{shopId}/onboardingShop 온보딩 상태 조회 (HAPPEN + EXPLODE)
POST/api/v1/shops/{shopId}/onboarding/steps/{stepId}/complete온보딩 step 완료 (?marketCode=KR)
POST/api/v1/shops/{shopId}/onboarding/steps/{stepId}/sub-steps/{subStepId}/complete온보딩 sub-step 완료 (?marketCode=KR)

Custom Domain

메서드엔드포인트설명
POST/api/v1/shops/{shopId}/custom-domain커스텀 도메인 등록 요청
DELETE/api/v1/shops/{shopId}/custom-domain커스텀 도메인 제거
POST/api/v1/shops/{shopId}/custom-domain/retry실패한 도메인 재시도
GET/api/v1/shops/{shopId}/custom-domain/status도메인 상태 상세 조회

Market Profile

메서드엔드포인트설명
GET/api/v1/shops/{shopId}/market-profiles전체 마켓 프로필 목록 조회
GET/api/v1/shops/{shopId}/market-profiles/{marketCode}마켓별 프로필 단건 조회
PUT/api/v1/shops/{shopId}/market-profiles/{marketCode}마켓 프로필 생성/수정 (upsert)

Shop Version

메서드엔드포인트설명
POST/api/v1/shops/{shopId}/versions버전 생성 (DRAFT)
GET/api/v1/shops/{shopId}/versions버전 목록 조회
PUT/api/v1/shops/{shopId}/versions/{versionId}DRAFT 버전 수정
PUT/api/v1/shops/{shopId}/versions/{versionId}/compiledCompiled URL 업데이트 (내부용)
POST/api/v1/shops/{shopId}/versions/{versionId}/publish버전 발행
GET/api/v1/shops/{shopId}/versions/{versionId}/detailShop + Version 상세 조회

HCS / System Defaults

메서드엔드포인트설명
POST/api/v1/shops/hcs-json/presigned-urlHCS JSON 업로드 Presigned URL 생성
POST/api/v1/shops/{shopId}/compiled/presigned-urlCompiled 파일 업로드 Presigned URL 생성
GET/api/v1/shops/system-defaults/urlsSystem default HCS 파일 URL 조회
POST/api/v1/shops/system-defaults/presigned-urlSystem default 업로드 Presigned URL 생성

Public API

메서드엔드포인트설명
GET/api/v1/public/shops/url/{shopUrl}shopUrl로 발행된 Shop 조회 (인증 불필요)

Key Builder

클래스: com.makitt.core.common.dynamodb.key.ShopKey

// METADATA 레코드
ShopKey.pk(shopId)                      → "SHOP#{shopId}"
ShopKey.sk()                            → "METADATA"

// User Shop List 레코드
ShopKey.userShopListPk(userId)          → "USER#{userId}"
ShopKey.userShopListSk(shopId)          → "SHOP#{shopId}"

// GSI1: Organization의 Shop 목록
ShopKey.gsi1Pk(organizationId)          → "ORGANIZATION#{organizationId}"
ShopKey.gsi1Sk(shopId)                  → "SHOP#{shopId}"

// GSI2: shopUrl 유니크 조회
ShopKey.gsi2Pk(shopUrl)                 → "{shopUrl}"
ShopKey.gsi2Sk(shopId)                  → "SHOP#{shopId}"

// GSI3: 커스텀 도메인 상태별 (sparse)
ShopKey.gsi3Pk(status)                  → "CUSTOM_DOMAIN_STATUS#{status}"
ShopKey.gsi3Sk(shopId)                  → "SHOP#{shopId}"

// GSI4: 커스텀 도메인 이름 조회 (sparse)
ShopKey.gsi4Pk(customDomain)            → "CUSTOM_DOMAIN#{customDomain}"
ShopKey.gsi4Sk(shopId)                  → "SHOP#{shopId}"

// ShopMarketProfile
ShopKey.marketProfileSk(marketCode)    → "MARKET_PROFILE#{marketCode}"
ShopKey.marketProfileSkPrefix()        → "MARKET_PROFILE#"

// ShopOnboarding
ShopKey.onboardingSk()                  → "ONBOARDING"

// ShopVersion
ShopKey.versionSk(timestamp, versionId) → "VERSION#{timestamp}#{versionId}"
ShopKey.versionSkPrefix()               → "VERSION#"

// Helper
ShopKey.extractShopId(pk)               → "shop123" (from "SHOP#shop123")

서버 아키텍처

ShopController / CustomDomainController / ShopMarketProfileController (makitt-api)
    |
    v  (DTO <-> VO)
ShopApplication / ShopMarketProfileApplication (makitt-application)
    |
    v  (VO)
ShopService / ShopOnboardingService / ShopMarketProfileService (makitt-core/service)
    |
    v  (DynamoDB 조작)
ShopRepository / ShopOnboardingRepository / ShopMarketProfileRepository (makitt-core/repository)
    |
    v
Shop / ShopOnboarding / ShopMarketProfile (makitt-core/entity)

패키지 구조:

makitt-core/
  com.makitt.core.domain.shop/
    entity/
      Shop.java                  # DynamoDB Entity
      ShopStatus.java             # 상태 Enum
      ShopStatusConverter.java    # DynamoDB Converter
      ShopBranding.java           # 브랜딩 Nested Object
      ShopLocalization.java       # 현지화 Nested Object
      ShopSeo.java                # SEO Nested Object
      CustomDomain.java           # 커스텀 도메인 Nested Object
      CustomDomainStatus.java     # 도메인 상태 Enum
      CustomDomainStatusConverter.java
      ShopVersion.java            # 버전 Entity (같은 파티션)
      VersionStatus.java          # 버전 상태 Enum
      VersionStatusConverter.java
      ShopOnboarding.java         # Shop 온보딩 Entity (같은 파티션)
      ShopOnboardingStep.java     # HAPPEN/EXPLODE step Enum
      ShopProfileSubStep.java     # SHOP_PROFILE_SET sub-step Enum
      ShopHcs.java                # HCS 관련 Nested Object
      ShopMarketProfile.java      # 마켓별 프로필 Entity
      MarketBusinessInfo.java     # 사업자 정보 Nested Object
      MarketSeo.java              # 마켓별 SEO Nested Object
      PlatformLink.java           # 소셜/플랫폼 링크 Nested Object
      PredefinedPlatform.java     # 사전정의 플랫폼 Enum
    service/
      ShopService.java
      ShopOnboardingService.java
      ShopAuthSettingsService.java
      ShopPaymentSettingsService.java
      ShopMarketProfileService.java
    repository/
      ShopRepository.java
      ShopOnboardingRepository.java
      ShopAuthSettingsRepository.java
      ShopPaymentSettingsRepository.java
      ShopMarketProfileRepository.java
    vo/
      CreateShopRequestVo.java
      UpdateShopRequestVo.java
      ShopResponseVo.java
      ShopOnboardingResponseVo.java
      ShopVersionResponseVo.java
      CreateShopVersionRequestVo.java
      UpdateShopVersionRequestVo.java
      ShopWithVersionResponseVo.java
      PublishedShopResponseVo.java
      HcsJsonUploadRequestVo.java
      HcsJsonUploadResponseVo.java
      CompiledUploadRequestVo.java
      CompiledUploadResponseVo.java
      SystemDefaultUrlsVo.java
      InitialVersionVo.java
      ShopMarketProfileResponseVo.java
      UpdateMarketProfileRequestVo.java

makitt-application/
  com.makitt.application.shop/
    ShopApplication.java
    ShopMarketProfileApplication.java

makitt-api/
  com.makitt.api.controller.shop/
    ShopController.java
    CustomDomainController.java
    PublicShopController.java
    ShopAuthSettingsController.java
    ShopPaymentSettingsController.java
    ShopMarketProfileController.java
  com.makitt.api.dto.shop/
    CreateShopRequest.java
    UpdateShopRequest.java
    ShopResponse.java
    ShopListResponse.java
    ShopOnboardingResponse.java
    ConfirmLocalizationRequest.java
    RegisterCustomDomainRequest.java
    CustomDomainResponse.java
    CustomDomainStatusResponse.java
    CreateShopVersionRequest.java
    UpdateShopVersionRequest.java
    ShopVersionResponse.java
    ShopVersionListResponse.java
    ShopWithVersionResponse.java
    PublishedShopResponse.java
    HcsJsonUploadRequest.java
    HcsJsonUploadResponse.java
    CompiledUploadResponse.java
    SystemDefaultUrlsResponse.java
    MarketProfileResponse.java
    UpdateMarketProfileRequest.java

ShopMarketProfile

마켓별 상점 프로필 엔티티. Shop과 동일 파티션에 저장됩니다.

키 구조:

패턴예시
PKSHOP#{shopId}SHOP#shop123
SKMARKET_PROFILE#{marketCode}MARKET_PROFILE#KR

필드:

필드DynamoDB Attribute타입필수설명
pkPKStringOPartition Key
skSKStringOSort Key
entityTypeentity_typeStringO"SHOP_MARKET_PROFILE" 고정
shopIdshop_idStringOShop ID
marketCodemarket_codeStringO마켓 코드 (예: KR, US, JP)
displayNamedisplay_nameStringX마켓별 상점 표시 이름
descriptiondescriptionStringX마켓별 상점 설명
representativeImageUrlrepresentative_image_urlStringX대표 이미지 URL
logoslogosMap<String, String>X브랜드 로고 맵 (예: {"main": "url", "monochrome": "url"})
faviconUrlfavicon_urlStringX파비콘 URL
platformLinksplatform_linksList<PlatformLink>X소셜/마켓플레이스 링크 (nested list)
businessInfobusiness_infoMarketBusinessInfoX마켓별 사업자 정보 (nested)
seoseoMarketSeoX마켓별 SEO 설정 (nested)
createdAtcreated_atInstantO생성 시각
updatedAtupdated_atInstantO수정 시각

Factory Methods:

  • ShopMarketProfile.create(shopId, marketCode) — PK/SK/entityType/timestamps 초기화
  • updateProfile(...) — 전체 프로필 일괄 수정, updatedAt 갱신

Nested: MarketBusinessInfo

마켓별 사업자/법인 정보. 마켓마다 요구 필드가 다르며, MarketMeta.businessInfoFields에서 어떤 필드를 표시할지 정의.

필드DynamoDB Attribute대상 마켓설명
businessNumberbusiness_numberKR사업자등록번호
onlineBusinessNumberonline_business_numberKR통신판매업 신고번호
companyNamecompany_nameAll상호(법인명)
representativeNamerepresentative_nameAll대표자명
businessAddressbusiness_addressAll사업장 주소
businessAddressDetailbusiness_address_detailAll상세 주소
postalCodepostal_codeAll우편번호
customerServicePhonecustomer_service_phoneAllCS 전화번호
customerServiceEmailcustomer_service_emailAllCS 이메일
customerServiceKakaocustomer_service_kakaoKR카카오톡 채널
operatingHoursoperating_hoursAll영업시간
lunchBreaklunch_breakAll점심시간
closedDaysclosed_daysAll휴무일
corporateNumbercorporate_numberJP法人番号
tokushohoRepresentativetokushoho_representativeJP特定商取引法 대표자
eineinUSEmployer Identification Number
stateTaxIdstate_tax_idUSState Tax ID
vatNumbervat_numberEUVAT 번호

Nested: MarketSeo

마켓별 SEO 메타데이터.

필드DynamoDB Attribute타입설명
titletitleStringSEO 제목
descriptiondescriptionStringSEO 설명
imageUrlimage_urlStringOG 이미지 URL

Nested: PlatformLink

소셜 미디어 / 마켓플레이스 링크. platform_links 리스트의 각 항목.

필드DynamoDB Attribute타입설명
platformplatformStringPredefinedPlatform enum name 또는 "CUSTOM"
labellabelString커스텀 링크일 때만 사용 (predefined는 null)
urlurlString플랫폼 URL

Enum: PredefinedPlatform

설명
INSTAGRAMInstagram
FACEBOOKFacebook
YOUTUBEYouTube
TIKTOKTikTok
TWITTERTwitter / X
BLOGBlog
KAKAO_CHANNELKakao Channel
AMAZONAmazon
SHOPEEShopee
NAVER_SMARTSTORENaver Smartstore
COUPANGCoupang

관련 엔티티

엔티티관계설명
UserUSER#{userId} PK로 Shop List 레코드 저장소유자 관계
OrganizationGSI1으로 Organization별 Shop 조회소속 관계
ShopMarketProfile동일 파티션 (SHOP#{shopId}, SK=MARKET_PROFILE#{marketCode})마켓별 프로필 (표시 이름, 사업자 정보, SEO, 로고 등)
ShopOnboarding동일 파티션 (SHOP#{shopId}, SK=ONBOARDING)HAPPEN/EXPLODE 온보딩 추적
ShopVersion동일 파티션 (SHOP#{shopId}, SK=VERSION#...)HCS 디자인 버전 관리
UserOnboardingFIRST_SHOP_CREATED 트리거Shop 생성 시 BEGIN 온보딩 완료

미구현 (계획)

ShopBranding (Deprecated)

ShopBranding (description, logoUrl, bannerImageUrl)은 deprecated. 브랜딩 데이터는 마켓별 ShopMarketProfile이 canonical source:

  • descriptionShopMarketProfile.description
  • logoUrlShopMarketProfile.logos
  • bannerImageUrlShopMarketProfile.representativeImageUrl
  • faviconUrlShopMarketProfile.faviconUrl

신규 코드는 ShopMarketProfile만 참조할 것. 기존 소비처 마이그레이션 완료 후 Shop entity에서 branding 필드 제거 예정.


참조