Market
docs/domain/market.md
Market
개요
마켓(국가/지역)별 메타데이터를 관리하는 도메인. 각 마켓의 통화, 세금 체계, 사업자 정보 필드, 필수 정책, 권장 결제/인증 수단 등을 정의한다.
MarketMeta는 **Shop 생성/설정을 위한 참고 데이터(SSOT)**이다. Shop이 마켓을 선택하면 해당 마켓의 메타데이터를 기반으로 온보딩 항목, 사업자 폼, 필수 정책 페이지 등을 동적으로 구성한다.
서버 패키지: com.makitt.core.domain.market
DynamoDB Entity
MarketMeta
| 키 | 패턴 | 예시 |
|---|---|---|
| PK | MARKET#{marketCode} | MARKET#KR |
| SK | META | META |
| GSI1 PK | SYSTEM | SYSTEM |
| GSI1 SK | MARKET#{marketCode} | MARKET#KR |
필드 목록
| 필드 | DynamoDB Attribute | 타입 | 설명 |
|---|---|---|---|
| marketCode | market_code | String | 마켓 코드 (KR, US, JP, SG) |
| name | name | String | 마켓 이름 |
| currency | currency | String | 통화 코드 (KRW, USD, JPY, SGD) |
| locale | locale | String | 로케일 (ko, en, ja) |
| taxes | taxes | List<MarketTax> | 세금 체계 정의 |
| businessInfoFields | business_info_fields | List<BusinessInfoFieldDef> | 사업자 정보 필드 (전체, 레거시) |
| businessInfoFieldsByType | business_info_fields_by_type | Map<String, List<BusinessInfoFieldDef>> | 사업자 유형별 필드 정의 |
| requiredPolicies | required_policies | List<RequiredPolicyDef> | 필수 약관/정책 정의 |
| recommendedProcessors | recommended_processors | List<String> | 권장 결제 프로세서 |
| recommendedOAuth | recommended_oauth | List<String> | 권장 소셜 로그인 |
| timezone | timezone | String | 기본 타임존 |
| numberingSystem | numbering_system | String | 숫자 표기 체계 |
| dateTimeFormat | date_time_format | String | 날짜/시간 포맷 |
Nested Objects
MarketTax
마켓의 세금 체계를 정의. 실제 세율 계산이 아닌, "이 마켓은 어떤 세금 체계인가"에 대한 참고 정보.
| 필드 | 타입 | 설명 |
|---|---|---|
| code | String | 세금 코드 (KR_VAT, US_SALES_TAX, JP_CT, SG_GST) |
| name | String | 세금 이름 (부가가치세, Sales Tax, 消費税, GST) |
| type | TaxType | 세금 유형 enum (VAT, SALES_TAX, CONSUMPTION_TAX, GST) |
| priceInclusiveDefault | Boolean | 가격 포함 표시 기본값 (true=포함, false=별도) |
| rateMode | RateMode | 세율 결정 방식 (FIXED, LOOKUP) |
| displayRateHint | String | UI 표시용 세율 힌트 ("10%", "VARIES") |
| setupChecklist | List<String> | 셀러 준비 항목 목록 |
소비되는 곳:
- Admin 마켓 관리 페이지: 세금 정의 생성/수정
- Shop 생성 시: 마켓 정보 패널에 세금 체계 표시
BusinessInfoFieldDef
마켓별 사업자 정보 폼 필드를 정의. 어떤 필드를 어떤 섹션에, 어떤 순서로, 필수 여부와 함께 보여줄지를 결정.
| 필드 | 타입 | 설명 |
|---|---|---|
| field | String | 필드 키 (businessNumber, representativeName, ein 등) |
| section | String | 섹션 분류: registration, contact, hours |
| required | Boolean | 필수 여부 |
| displayOrder | Integer | 섹션 내 정렬 순서 |
소비되는 곳:
- 온보딩 위저드 BusinessInfoStep: registration 섹션 필드만 렌더링
- VendorManagement 페이지: 전체 섹션 필드 렌더링
- MarketBusinessInfoForm: 섹션별 그룹핑 + displayOrder 정렬
- ShopMarketProfileApplication (서버): 필수 필드 검증
i18n 번역 키: shopGeneral.businessInfo.fields.{field} (예: shopGeneral.businessInfo.fields.businessNumber → "사업자등록번호")
businessInfoFieldsByType (사업자 유형별 필드)
businessInfoFields가 모든 사업자 유형에 동일한 필드를 제공하는 반면, businessInfoFieldsByType은 유형별로 다른 필드 세트를 정의한다.
키는 사업자 유형 문자열: INDIVIDUAL, SOLE_PROPRIETOR, CORPORATION
KR 마켓 예시:
| 유형 | registration 필드 |
|---|---|
| INDIVIDUAL | representativeName, postalCode, businessAddress |
| SOLE_PROPRIETOR | businessNumber, onlineBusinessNumber(선택), companyName, representativeName, postalCode, businessAddress |
| CORPORATION | businessNumber, onlineBusinessNumber, companyName, representativeName, postalCode, businessAddress |
소비되는 곳:
- 온보딩 위저드 BusinessInfoStep:
businessInfoFieldsByType[businessType]우선 사용, 없으면businessInfoFieldsfallback - VendorManagement 페이지: 사업자 유형 선택에 따라 필드 변경 (향후 적용)
RequiredPolicyDef
마켓별 법적으로 필요한 약관/정책 페이지를 정의.
| 필드 | 타입 | 설명 |
|---|---|---|
| slug | String | 정책 슬러그 (terms-of-service, privacy-policy, refund-policy) |
| label | String | 정책 이름 (이용약관, 개인정보처리방침) |
| category | String | 카테고리: TERMS, POLICY, LEGAL_NOTICE |
| legalBasis | String | 법적 근거 (전자상거래법 제10조, CCPA 등) |
| placements | List<String> | 배치 위치 (footer, checkout, signup) |
| mandatory | Boolean | 필수 여부 |
| displayOrder | Integer | 정렬 순서 |
소비되는 곳:
- StaticPageApplication (서버): Shop 생성 시 마켓별 필수 정책 페이지 자동 생성
- PolicyManagement 페이지 (클라이언트): 정책 편집기에서 필수 정책 정의 표시
Enums
TaxType
| 값 | 설명 | 마켓 |
|---|---|---|
VAT | 부가가치세 | KR |
SALES_TAX | 판매세 (주/지역별 상이) | US |
CONSUMPTION_TAX | 소비세 | JP |
GST | 상품서비스세 | SG |
RateMode
| 값 | 설명 |
|---|---|
FIXED | 고정 세율 (KR 10%, JP 10%/8%, SG 9%) |
LOOKUP | 주소/상품 기반 조회 (US Sales Tax) |
API
조회 API (일반 사용자)
GET /api/v1/markets -> 전체 Market 목록
GET /api/v1/markets/{code} -> 특정 Market 메타데이터
Admin CRUD API
POST /api/v1/admin/markets -> 새 마켓 생성
PUT /api/v1/admin/markets/{code} -> 마켓 수정
DELETE /api/v1/admin/markets/{code} -> 마켓 삭제
데이터 소비 맵
MarketMeta의 각 필드가 어디서 소비되는지를 정리한다.
서버 소비
| 필드 | 소비자 | 용도 |
|---|---|---|
| businessInfoFields | ShopMarketProfileApplication | 필수 사업자 정보 필드 검증, 온보딩 BUSINESS_INFO_SET 완료 판단 |
| requiredPolicies | StaticPageApplication | Shop 생성 시 마켓별 필수 정책 StaticPage 자동 생성 |
| timezone, currency, locale | ShopMarketProfileApplication | MarketLocaleSettings 초기화 → Shop 기본 로케일 설정 |
클라이언트 소비
| 필드 | 소비자 (컴포넌트/페이지) | 용도 |
|---|---|---|
| marketCode, name | CreateShopPage, MarketSelector, MarketConfigSection | 마켓 선택 UI, 검색/필터 |
| currency | MarketConfigSection | 통화 표시 |
| taxes | Admin 마켓 관리 페이지 | 세금 정의 생성/수정/표시 |
| businessInfoFields | MarketBusinessInfoForm, VendorManagement | 사업자 정보 폼 필드 렌더링 (레거시, 유형 무관) |
| businessInfoFieldsByType | BusinessInfoStep (온보딩 위저드) | 사업자 유형별 폼 필드 렌더링 |
| requiredPolicies | PolicyManagement | 정책 편집기에서 필수 정책 정의 매칭 |
| recommendedProcessors | Admin 마켓 목록 | 결제 프로세서 로고/이름 표시 |
| recommendedOAuth | Admin 마켓 목록 | OAuth 제공자 표시 |
데이터 흐름
MarketMeta (DynamoDB)
│
├──→ MarketService → MarketApplication → MarketController
│ GET /api/v1/markets
│
├──→ ShopMarketProfileApplication
│ - 필수 사업자 필드 검증
│ - MarketLocaleSettings 초기화
│
├──→ StaticPageApplication
│ - 필수 정책 페이지 자동 생성
│
└──→ Client (React Query 캐시)
│
├──→ 온보딩 위저드 (BusinessInfoStep, CreateShopStep)
├──→ VendorManagement (사업자 정보 관리)
├──→ PolicyManagement (정책 관리)
├──→ CreateShopPage (샵 생성, 마켓 선택)
└──→ Admin 마켓 관리 (CRUD)
현재 마켓 데이터
KR (한국)
| 항목 | 값 |
|---|---|
| currency | KRW |
| locale | ko |
| timezone | Asia/Seoul |
| taxes | KR_VAT (VAT, 10%, FIXED) |
| recommendedProcessors | TOSS_PAYMENTS, STRIPE |
| recommendedOAuth | KAKAO, NAVER, GOOGLE, APPLE |
| requiredPolicies | 이용약관, 개인정보처리방침, 반품/환불 정책 |
US (United States)
| 항목 | 값 |
|---|---|
| currency | USD |
| locale | en |
| timezone | America/New_York |
| taxes | US_SALES_TAX (SALES_TAX, VARIES, LOOKUP) |
| recommendedProcessors | STRIPE |
| recommendedOAuth | GOOGLE, APPLE, FACEBOOK |
| requiredPolicies | Terms of Service, Privacy Policy, Refund Policy |
JP (日本)
| 항목 | 값 |
|---|---|
| currency | JPY |
| locale | ja |
| timezone | Asia/Tokyo |
| taxes | JP_CT (CONSUMPTION_TAX, 10%/8%, FIXED) |
| recommendedProcessors | STRIPE |
| recommendedOAuth | GOOGLE, APPLE, LINE |
| requiredPolicies | 利用規約, プライバシーポリシー, 返品・返金ポリシー, 特定商取引法に基づく表記 |
SG (Singapore)
| 항목 | 값 |
|---|---|
| currency | SGD |
| locale | en |
| timezone | Asia/Singapore |
| taxes | SG_GST (GST, 9%, FIXED) |
| recommendedProcessors | STRIPE |
| recommendedOAuth | GOOGLE, APPLE, FACEBOOK |
| requiredPolicies | Terms of Service, Privacy Policy, Refund Policy |
서버 아키텍처
makitt-core/
com.makitt.core.domain.market/
entity/
MarketMeta.java # DynamoDB Entity
MarketTax.java # 세금 체계 Nested Object
BusinessInfoFieldDef.java # 사업자 정보 필드 정의
RequiredPolicyDef.java # 필수 정책 정의
TaxType.java # 세금 유형 Enum
RateMode.java # 세율 결정 방식 Enum
service/
MarketService.java # Service (Repository 래핑)
repository/
MarketRepository.java # DynamoDB Repository
vo/
MarketResponseVo.java
MarketTaxVo.java
CreateMarketRequestVo.java
UpdateMarketRequestVo.java
makitt-application/
com.makitt.application.market/
MarketApplication.java # Application (비즈니스 로직)
makitt-api/
com.makitt.api.controller.market/
MarketController.java # 조회 API
com.makitt.api.controller.admin/
MarketAdminController.java # Admin CRUD API
com.makitt.api.dto.market/
MarketResponse.java
BusinessInfoFieldDefDto.java
RequiredPolicyDefDto.java
MarketTaxResponse.java
CreateMarketRequest.java
UpdateMarketRequest.java
com.makitt.api.config/
MarketSeedDataInitializer.java # 시드 데이터 초기화
GSI (Global Secondary Index)
| GSI | PK 패턴 | SK 패턴 | 용도 |
|---|---|---|---|
| EntityLookupIndex | SYSTEM | MARKET#{marketCode} | 전체 마켓 목록 조회 |
MarketMeta는 소규모 데이터(4~10개)이므로 SYSTEM을 PK로 고정하여 전체 목록을 한 파티션에서 조회.
Key Builder
MarketKey.pk(marketCode) -> "MARKET#{marketCode}"
MarketKey.sk() -> "META"
MarketKey.gsi1Pk() -> "SYSTEM"
MarketKey.gsi1Sk(marketCode) -> "MARKET#{marketCode}"
MarketKey.gsi1SkPrefix() -> "MARKET#"
MarketKey.extractMarketCode(pk) -> marketCode