Saleor Core 분석: GraphQL / Settings / Dependencies
분석 대상: saleor v3.24.0-a.0 (
/tmp/saleor-core/) 작성일: 2026-03-27
1. GraphQL Layer Architecture
1.1 전체 구조
saleor/graphql/
├── api.py # Root schema 정의 (Query, Mutation, Subscription 조합)
├── views.py # Django View → GraphQL 실행 엔드포인트
├── middleware.py # Django middleware (GraphQLView 감지)
├── context.py # SaleorContext 생성 (dataloaders, auth, replica 설정)
├── decorators.py # permission_required, account_passes_test 등
├── query_cost_map.py # 필드별 cost map
├── metrics.py # OTel 기반 메트릭 수집
├── promise.py # graphql-core-legacy Promise 패치
├── core/ # 공용 인프라
│ ├── mutations.py # BaseMutation, DeprecatedModelMutation, ModelDeleteMutation
│ ├── types/ # ModelObjectType, NonNullList, File, Upload 등
│ ├── fields.py # BaseField, ConnectionField, FilterConnectionField
│ ├── connection.py # Relay connection 유틸
│ ├── enums.py # 공통 enum (LanguageCodeEnum 등)
│ ├── scalars.py # PositiveDecimal, UUID 등 커스텀 스칼라
│ ├── validators/ # query_cost.py (CostValidator), file.py
│ ├── federation/ # Apollo Federation v1 지원 (_Entity, _Service, _Any)
│ ├── dataloaders.py # 기본 dataloader 패턴
│ ├── filters/ # django-filter 기반 GraphQL 필터
│ └── doc_category.py # @doc directive 카테고리 매핑
├── account/ # 도메인별 schema 모듈
├── checkout/
├── order/
├── product/
├── payment/
├── shipping/
├── warehouse/
├── webhook/
├── ... (app, attribute, channel, csv, discount, giftcard, invoice, menu, meta, page, plugins, shop, tax, translations)
└── tests/
1.2 Schema 빌드 흐름
api.py
├── Query(AccountQueries, AppQueries, ..., WebhookQueries) # 22개 도메인 Queries mixin
├── Mutation(AccountMutations, ..., WebhookMutations) # 23개 도메인 Mutations mixin
├── Subscription # webhook subscription types
└── build_federated_schema(Query, Mutation, types, subscription, directives)
└── Apollo Federation v1 래핑 (_Entity union, _Service SDL)
schema = build_federated_schema(...)
backend = GraphQLCachedBackend(SaleorGraphQLBackend(), cache_map=CacheDict(1000))
핵심 포인트:
graphql-corev2 (legacy) +graphenev2 기반. graphql-core v3 / graphene v3 아님.GraphQLCachedBackend로 파싱/검증 결과를 LRU 캐시 (1000 entries)- Apollo Federation v1 프로토콜 내장 (
_entities,_service { sdl })
1.3 Request 처리 파이프라인
HTTP POST /graphql/
│
├─ Django Middleware Chain
│ ├── SecurityMiddleware
│ ├── CommonMiddleware
│ └── jwt_refresh_token_middleware
│
├─ GraphQLView.dispatch()
│ ├── handle_query() — OTel span + 메트릭 계측
│ ├── parse_body() — orjson 파싱 (multipart file upload 지원)
│ ├── execute_graphql_request()
│ │ ├── parse_query() — backend.document_from_string() (캐시 히트 여부)
│ │ ├── validate_query_cost() — CostValidator (max: GRAPHQL_QUERY_MAX_COMPLEXITY)
│ │ ├── get_context_value() — SaleorContext 구성
│ │ │ ├── dataloaders = {}
│ │ │ ├── set_app_on_context()
│ │ │ ├── set_auth_on_context()
│ │ │ └── set_decoded_auth_token()
│ │ └── document.execute(context, middleware=[...])
│ │ └── GraphQL Middleware (GRAPHQL_MIDDLEWARE 설정, 기본값 빈 리스트)
│ └── clear_context() — dataloader 캐시 / reference cycle 정리
│
└─ JsonResponse (orjson 직렬화, UTC_Z 옵션)
1.4 Mutation 구조 패턴
계층:
graphene.Mutation
└── BaseMutation # 권한 체크, 에러 핸들링, 메타데이터 지원
└── DeprecatedModelMutation # Django Model 바인딩 (return_field_name, object_type)
└── ModelDeleteMutation # 삭제 전용
Mutation 작성 패턴 (CheckoutCreate 예시):
class CheckoutCreate(DeprecatedModelMutation, I18nMixin):
# 1. 반환 필드
created = graphene.Field(graphene.Boolean, ...)
# 2. Arguments — Input Type으로 래핑
class Arguments:
input = CheckoutCreateInput(required=True, ...)
# 3. Meta — 모든 mutation에 필수
class Meta:
description = "Create a new checkout."
doc_category = DOC_CATEGORY_CHECKOUT
model = models.Checkout # Django 모델
object_type = Checkout # GraphQL 타입
return_field_name = "checkout" # 응답 필드명
error_type_class = CheckoutError # 에러 타입
error_type_field = "checkout_errors" # deprecated 에러 필드
webhook_events_info = [...] # 트리거되는 webhook 이벤트
# permissions = (...) # 필요시 튜플로 지정
# 4. 비즈니스 로직
@classmethod
def perform_mutation(cls, root, info, **data):
# clean → validate → save → trigger webhook
...핵심 규칙:
permissions튜플이 있으면mutate()진입 시 자동 체크 →PermissionDenied발생perform_mutation()내에서ValidationError발생 시 →handle_errors()가 에러 타입 변환webhook_events_info가 description에 자동 추가됨 (Webhooks 참조)doc_category는@docdirective에 매핑
1.5 Resolver 패턴
Query 정의 (schema.py):
class CheckoutQueries(graphene.ObjectType):
checkout = BaseField(
Checkout,
id=graphene.Argument(graphene.ID),
permissions=[CheckoutPermissions.MANAGE_CHECKOUTS, ...],
doc_category=DOC_CATEGORY_CHECKOUT,
)
checkouts = FilterConnectionField(
CheckoutCountableConnection,
sort_by=..., filter=..., channel=...,
permissions=[CheckoutPermissions.MANAGE_CHECKOUTS],
)Resolver 구현 (resolvers.py):
@traced_resolver
def resolve_checkout(info, token, id):
validate_one_of_args_is_in_query("id", id, "token", token)
# DataLoader 사용 → Promise chain
return CheckoutByTokenLoader(info.context).load(token).then(with_checkout)특징:
- DataLoader 패턴이 핵심. 모든 관계 필드는 dataloader로 해결 (N+1 방지)
SyncWebhookControlContext로 노드를 래핑하여 sync webhook 호출 여부 제어get_database_connection_name()으로 replica DB 라우팅@traced_resolver데코레이터로 OTel tracing
1.6 Permission 체크 패턴
# Mutation level — Meta.permissions 튜플
class Meta:
permissions = (OrderPermissions.MANAGE_ORDERS,)
# BaseMutation.mutate() 내부:
if not cls.check_permissions(info.context, data=data):
raise PermissionDenied(permissions=cls._meta.permissions)
# check_permissions() 기본 동작:
# - permissions가 없으면 True (공개 mutation)
# - one_of_permissions_or_auth_filter_required() — OR 로직 (하나만 만족하면 OK)
# - require_all_permissions=True면 AND 로직
# Query level — BaseField / FilterConnectionField의 permissions 인자
checkout = BaseField(Checkout, permissions=[...])
# Resolver level — 데코레이터 or 직접 체크
@permission_required(OrderPermissions.MANAGE_ORDERS)
def resolve_orders(root, info, **kwargs): ...1.7 에러 처리 패턴
ValidationError (Django)
↓ validation_error_to_error_type()
↓ error_dict 순회 → snake_to_camel_case 변환
GraphQL Error Type (e.g., CheckoutError)
├── field: String (camelCase 필드명)
├── message: String (에러 메시지)
├── code: ErrorCodeEnum (예: INVALID, NOT_FOUND, REQUIRED)
└── 추가 params (에러 타입별)
# 모든 mutation 응답에 포함:
{
errors: [CheckoutError!]! # 현재 표준
checkoutErrors: [CheckoutError!]! # deprecated (하위호환)
}
1.8 Query Cost Validation
CostValidator(graphql-core ValidationRule)- 기본 max complexity: 50,000 (
GRAPHQL_QUERY_MAX_COMPLEXITY) COST_MAP딕셔너리로 타입/필드별 cost 정의- 응답 extensions에
{ cost: { requestedQueryCost, maximumAvailable } }포함
2. Settings 카탈로그
2.1 Environment Variables 전체 목록
| 변수명 | 기본값 | 설명 |
|---|---|---|
| Core | ||
DEBUG | True | Django 디버그 모드 |
SECRET_KEY | 랜덤 (DEBUG=True 시) | 필수. production에서 반드시 설정 |
ALLOWED_HOSTS | localhost,127.0.0.1 | Django ALLOWED_HOSTS |
ALLOWED_CLIENT_HOSTS | localhost,127.0.0.1 | CORS 허용 호스트. DEBUG=False 시 필수 |
ALLOWED_GRAPHQL_ORIGINS | * | GraphQL 요청 허용 origin |
PUBLIC_URL | (없음) | Saleor API 공개 URL (설정 시 ENABLE_SSL 무시) |
ENABLE_SSL | False | HTTPS 리다이렉트 |
INTERNAL_IPS | 127.0.0.1 | 내부 IP 목록 |
DEFAULT_COUNTRY | US | 기본 국가 코드 |
DEFAULT_CHANNEL_SLUG | default-channel | 기본 채널 slug |
POPULATE_DEFAULTS | True | 마이그레이션 시 기본 데이터 생성 |
REAL_IP_ENVIRON | REMOTE_ADDR | 실제 IP 헤더 (프록시 환경) |
| Database | ||
DATABASE_URL | postgres://saleor:saleor@localhost:5432/saleor | 기본 DB |
DATABASE_URL_REPLICA | (DATABASE_URL 사용) | 읽기 전용 replica DB |
DB_CONN_MAX_AGE | 0 | DB 커넥션 유지 시간 (초) |
| Cache / Redis | ||
CACHE_URL | (없음) | Redis 캐시 URL |
REDIS_URL | (없음) | Redis URL (CACHE_URL 대체) |
CACHE_TIMEOUT | 7 days | 캐시 TTL |
| Celery | ||
CELERY_BROKER_URL | (없음) | Celery 브로커 URL (Redis/SQS/AMQP) |
CELERY_RESULT_BACKEND | None | 결과 백엔드 |
CELERY_WORKER_PREFETCH_MULTIPLIER | 1 | 워커 프리페치 |
EMAIL_URL | (없음) | 이메일 SMTP URL |
DEFAULT_FROM_EMAIL | noreply@example.com | 발신 이메일 |
SENDGRID_USERNAME / SENDGRID_PASSWORD | (없음) | SendGrid 인증 |
USER_EMAIL_URL | (없음) | 사용자 이메일 플러그인 SMTP |
| JWT | ||
RSA_PRIVATE_KEY | (없음) | JWT 서명용 RSA 키 |
RSA_PRIVATE_PASSWORD | (없음) | RSA 키 비밀번호 |
JWT_MANAGER_PATH | saleor.core.jwt_manager.JWTManager | JWT 매니저 클래스 |
JWT_TTL_ACCESS | 5 minutes | Access token TTL |
JWT_TTL_APP_ACCESS | 5 minutes | App access token TTL |
JWT_TTL_REFRESH | 30 days | Refresh token TTL |
JWT_TTL_REQUEST_EMAIL_CHANGE | 1 hour | 이메일 변경 토큰 TTL |
| GraphQL | ||
GRAPHQL_QUERY_MAX_COMPLEXITY | 50000 | 쿼리 cost 상한 (0=비활성) |
FEDERATED_QUERY_MAX_ENTITIES | 100 | Federation 엔터티 최대 수 |
GRAPHQL_CACHE_SUFFIX | "" | 스키마 캐시 무효화 접미사 |
PLAYGROUND_ENABLED | True | GraphQL Playground UI |
| Storage (AWS) | ||
AWS_ACCESS_KEY_ID | (없음) | AWS 인증 |
AWS_SECRET_ACCESS_KEY | (없음) | AWS 시크릿 |
AWS_MEDIA_BUCKET_NAME | (없음) | 미디어 버킷 |
AWS_MEDIA_PRIVATE_BUCKET_NAME | (없음) | 비공개 미디어 버킷 |
AWS_STORAGE_BUCKET_NAME | (없음) | 정적 파일 버킷 |
AWS_S3_ENDPOINT_URL | (없음) | S3 호환 엔드포인트 |
AWS_S3_REGION_NAME | (없음) | S3 리전 |
AWS_QUERYSTRING_AUTH | False | 서명된 URL |
AWS_DEFAULT_ACL | None | 기본 ACL |
AWS_S3_FILE_OVERWRITE | True | 파일 덮어쓰기 |
| Storage (GCS) | ||
GS_PROJECT_ID | (없음) | GCS 프로젝트 ID |
GS_BUCKET_NAME | (없음) | GCS 버킷 |
GS_MEDIA_BUCKET_NAME | (없음) | 미디어 버킷 |
GS_MEDIA_PRIVATE_BUCKET_NAME | (없음) | 비공개 미디어 버킷 |
GS_EXPIRATION | 1 day | 서명 URL 만료 시간 |
| Storage (Azure) | ||
AZURE_ACCOUNT_NAME | (없음) | Azure 스토리지 계정 |
AZURE_ACCOUNT_KEY | (없음) | 계정 키 |
AZURE_CONTAINER | (없음) | 공개 컨테이너 |
AZURE_CONTAINER_PRIVATE | (없음) | 비공개 컨테이너 |
| Checkout | ||
ANONYMOUS_CHECKOUTS_TIMEDELTA | 30 days | 익명 체크아웃 만료 |
USER_CHECKOUTS_TIMEDELTA | 90 days | 사용자 체크아웃 만료 |
EMPTY_CHECKOUTS_TIMEDELTA | 6 hours | 빈 체크아웃 만료 |
CHECKOUT_PRICES_TTL | 1 hour | 가격 캐시 TTL |
CHECKOUT_DELIVERY_OPTIONS_TTL | 24 hours | 배송 옵션 캐시 TTL |
CHECKOUT_TTL_BEFORE_RELEASING_FUNDS | 6 hours | 결제 해제 대기 |
CHECKOUT_COMPLETION_LOCK_TIME | 3 minutes | 완료 처리 lock 시간 |
AUTOMATIC_CHECKOUT_COMPLETION_DELAY | 30 (분) | 자동 완료 지연 |
CHECKOUT_SEARCH_UPDATE_PARALLEL_TASKS | 5 | 검색벡터 병렬 작업 수 |
| Webhook | ||
WEBHOOK_CELERY_QUEUE_NAME | (없음) | 비동기 webhook 큐 |
WEBHOOK_DEFERRED_PAYLOAD_QUEUE_NAME | (없음) | 지연 페이로드 큐 |
WEBHOOK_SQS_CELERY_QUEUE_NAME | (WEBHOOK_CELERY_QUEUE_NAME) | SQS webhook 큐 |
WEBHOOK_PUBSUB_CELERY_QUEUE_NAME | (WEBHOOK_CELERY_QUEUE_NAME) | PubSub webhook 큐 |
CHECKOUT_WEBHOOK_EVENTS_CELERY_QUEUE_NAME | (WEBHOOK_CELERY_QUEUE_NAME) | 체크아웃 webhook 큐 |
ORDER_WEBHOOK_EVENTS_CELERY_QUEUE_NAME | (WEBHOOK_CELERY_QUEUE_NAME) | 주문 webhook 큐 |
| Observability | ||
OBSERVABILITY_BROKER_URL | (없음) | 관측성 브로커 URL |
OBSERVABILITY_REPORT_ALL_API_CALLS | False | 전체 API 콜 리포트 |
OBSERVABILITY_MAX_PAYLOAD_SIZE | 25000 | 최대 페이로드 크기 |
OBSERVABILITY_BUFFER_SIZE_LIMIT | 1000 | 버퍼 크기 제한 |
OBSERVABILITY_BUFFER_BATCH_SIZE | 100 | 배치 크기 |
OBSERVABILITY_REPORT_PERIOD | 20 seconds | 리포트 주기 |
OBSERVABILITY_BUFFER_TIMEOUT | 5 minutes | 버퍼 타임아웃 |
| Sentry | ||
SENTRY_DSN | (없음) | Sentry DSN |
| Security | ||
HTTP_IP_FILTER_ENABLED | True | webhook 호출 시 사설 IP 차단 |
HTTP_IP_FILTER_ALLOW_LOOPBACK_IPS | False | 루프백 IP 허용 |
RESET_PASSWORD_LOCK_TIME | 15 minutes | 비밀번호 초기화 lock |
CONFIRMATION_EMAIL_LOCK_TIME | 15 minutes | 확인 이메일 lock |
| Memory | ||
SOFT_MEMORY_LIMIT_IN_MB | (없음) | 프로세스 힙 소프트 제한 |
HARD_MEMORY_LIMIT_IN_MB | (없음) | 프로세스 힙 하드 제한 |
| Beat schedules | ||
BEAT_EXPIRE_ORDERS_AFTER_TIMEDELTA | 5 minutes | 주문 만료 체크 주기 |
BEAT_UPDATE_SEARCH_FREQUENCY | 20 seconds | 검색 인덱스 갱신 주기 |
BEAT_PRICE_RECALCULATION_SCHEDULE | 30 seconds | 가격 재계산 주기 |
EVENT_PAYLOAD_DELETE_PERIOD | 14 days | 이벤트 페이로드 보관 기간 |
DELETE_APP_TTL | 1 day | 삭제 앱 보관 기간 |
| 기타 | ||
MAX_USER_ADDRESSES | 100 | 사용자당 최대 주소 수 |
EXPORT_FILES_TIMEDELTA | 30 days | 내보내기 파일 보관 기간 |
ORDER_RULES_LIMIT | 100 | 주문 규칙 최대 수 |
GIFTS_LIMIT_PER_RULE | 500 | 규칙당 최대 선물 수 |
EDITOR_JS_LISTS_MAX_DEPTH | 10 | EditorJS 중첩 리스트 최대 깊이 |
SEND_USAGE_TELEMETRY | True | 사용 텔레메트리 전송 |
BREAKER_BOARD_ENABLED | False | Circuit breaker 활성화 |
BREAKER_BOARD_SYNC_EVENTS | (없음) | 모니터링 대상 sync webhook |
UPLOAD_ADDITIONAL_ALLOWED_MIME_TYPES | {} | 추가 허용 MIME 타입 (JSON) |
TELEMETRY_SLOW_GRAPHQL_OPERATION_THRESHOLD | 1.0 (초) | 느린 쿼리 임계값 |
2.2 Database 설정
- 엔진: PostgreSQL (psycopg v3, binary 모드)
- Primary-Replica 라우팅:
PrimaryReplicaRouter(saleor.core.db_routers)DATABASE_URL→ default (read/write)DATABASE_URL_REPLICA→ replica (read-only)allow_replica플래그로 context에서 제어
- Connection pooling:
DB_CONN_MAX_AGE=0(기본값, 요청마다 새 커넥션)- Production에서는 반드시 증가 권장 (e.g., 600)
2.3 Celery / Redis 설정
- Broker: Redis, SQS, AMQP 지원
CELERY_TASK_ALWAYS_EAGER = not CELERY_BROKER_URL— 브로커 미설정 시 동기 실행- Beat Schedule: 16개 이상의 정기 작업
- 검색 인덱스 갱신 (20초), 만료 체크아웃/주문 삭제, 재고 할당 정리, 가격 재계산 등
- 도메인별 큐 분리 가능 (webhook, checkout, order, search 등)
3. Dependencies 분석
3.1 핵심 의존성
| 패키지 | 버전 제약 | 위험도 | 비고 |
|---|---|---|---|
django | ~=5.2.11 | 낮음 | 최신 LTS. 안정적 |
graphene | <3.0 | 높음 | v2에 잠김. graphene v3는 API 호환 안 됨 |
graphql-core | >=2.3.2,<3 | 높음 | Legacy v2. 커뮤니티 지원 사실상 종료 |
graphql-relay | >=2.0.1,<3 | 높음 | graphql-core v2 전용 |
psycopg[binary] | >=3.2.9,<4 | 낮음 | PostgreSQL 드라이버 (최신) |
celery[redis,sqs] | >=4.4.5,<6 | 낮음 | 넓은 범위, 안정적 |
redis | >=5.0.1,<6 | 낮음 | |
pydantic | >=2.11.0,<3 | 낮음 | |
sentry-sdk | ~=2.12 | 낮음 | |
opentelemetry-* | >=1.32.1,<2 / >=0.53b1 | 중간 | Beta 버전 의존 |
Rx | >=1.6.3,<2 | 높음 | RxPY v1, graphql-core v2 의존 |
promise | ~=2.3 | 높음 | graphql-core v2 Promise 패턴 |
pillow | >=12.1.1,<13 | 낮음 | |
boto3 | ~=1.28 | 낮음 | AWS SDK |
stripe | >=3.0.0,<4 | 중간 | v3에 잠김 (현재 최신 v11+) |
braintree | >=4.2,<4.32 | 낮음 | |
requests-hardened | >=1.0.0,<2 | 낮음 | SSRF 방지 |
orjson | >=3.11.4 | 낮음 | 고성능 JSON 직렬화 |
authlib | >=1.6.9,<2 | 낮음 | OAuth/OIDC |
petl | ==1.7.17 | 중간 | 정확한 버전 pin (CSV 처리) |
3.2 Legacy GraphQL Stack 위험 평가
가장 큰 기술 부채:
graphene <3.0 ←→ graphql-core >=2.3.2,<3 ←→ graphql-relay >=2.0.1,<3
↕
promise ~=2.3
Rx >=1.6.3,<2
graphql-corev2는 2020년 이후 유지보수만 (버그픽스 수준)- Saleor 자체적으로
graphql_core.py,promise.py에서 monkey-patching하여 메모리 누수 대응 patch_executor(),patch_execution_context(),patch_execution_result(),patch_promise()등 5개 이상의 런타임 패치- graphql-core v3 마이그레이션은 graphene v3 전환과 동시에 필요하며 사실상 전체 GraphQL 계층 재작성에 해당
3.3 문제성 핀 버전
| 패키지 | 제약 | 문제 |
|---|---|---|
petl==1.7.17 | 정확한 버전 pin | 보안 패치 적용 어려움 |
graphene<3.0 | 메이저 버전 상한 | graphene v3 전환 불가 |
stripe>=3.0.0,<4 | v3에 잠김 | 최신 Stripe API 기능 사용 불가 |
opentelemetry-semantic-conventions>=0.53b1,<0.54 | beta 버전 마이너 범위 | OTel 업그레이드 시 충돌 가능 |
3.4 Python 버전
requires-python = ">=3.12,<3.13"— Python 3.12 전용- Docker 이미지:
python:3.12/python:3.12-slim - 패키지 매니저: uv (lockfile:
uv.lock)
4. Docker / Deployment
4.1 Dockerfile 분석
빌드 스테이지: python:3.12 + uv 패키지 매니저
→ uv sync --locked (lockfile 기반 재현성)
→ collectstatic
런타임 스테이지: python:3.12-slim
→ uvicorn ASGI 서버
→ Port 8000
→ Workers: 2
→ Request 제한: 10,000 요청 후 워커 재시작
→ Keep-alive: 35초
→ Graceful shutdown: 30초
4.2 서비스 구성 요소
| 컴포넌트 | 실행 명령 |
|---|---|
| API 서버 | uvicorn saleor.asgi:application |
| Celery Worker | celery --app saleor.celeryconf:app worker -E |
| Celery Beat | celery --app saleor.celeryconf:app beat --scheduler saleor.schedulers.schedulers.DatabaseScheduler |
4.3 외부 서비스 의존
- PostgreSQL — 필수
- Redis — 캐시 + Celery 브로커 (또는 SQS/AMQP)
- Object Storage — AWS S3 / GCS / Azure Blob (선택)
- Sentry — 에러 트래킹 (선택)
- SMTP — 이메일 발송 (선택)
5. 한국 로컬라이제이션 설정 가이드
5.1 언어 지원 현황
saleor/core/languages.py에 ("ko", "Korean")과 ("ko-kr", "Korean (South Korea)") 포함됨. Saleor의 Translation 시스템으로 제품/카테고리/페이지 등의 한국어 번역 가능.
5.2 권장 환경 변수 설정
# 기본 국가
DEFAULT_COUNTRY=KR
# 채널 설정 — Saleor는 통화를 채널 단위로 관리
# 채널 생성 시 currency_code="KRW" 설정
# 타임존은 settings.py에서 UTC 고정 (Saleor 권장)
# 프론트엔드에서 KST 변환 처리
# 주소 검증 — google-i18n-address가 한국 주소 형식 지원
# i18n_rules_override()로 커스텀 가능5.3 통화(KRW) 고려사항
DEFAULT_DECIMAL_PLACES = 3→ KRW는 소수점 없음. 채널별currency_code설정으로 처리됨- Saleor의 가격 처리는 채널(Channel) 단위. 원화 채널 생성 시 자동으로 KRW 통화 적용
- Stripe, Razorpay 등 결제 게이트웨이의 KRW 지원 여부 별도 확인 필요
5.4 주소 형식
django-countries+google-i18n-address조합- 한국 주소 형식 (도/시/구/동) 지원
i18n_rules_override()를 통한 한국 주소 검증 규칙 커스터마이즈 가능phonenumber_field가 한국 전화번호 형식 지원 (Address 참조)
6. Fork 준비 체크리스트
6.1 즉시 변경 필요
-
SECRET_KEY재생성 (절대 공유하지 않을 것) -
DEFAULT_COUNTRY=KR설정 - 한국어 채널 생성 (currency=KRW, language=ko)
-
ALLOWED_CLIENT_HOSTS,ALLOWED_HOSTS,PUBLIC_URL프로덕션 값 설정 -
DEBUG=False확인 -
SEND_USAGE_TELEMETRY=False(Saleor 텔레메트리 비활성화) -
PLAYGROUND_ENABLED=False(프로덕션) - RSA 키 생성 및
RSA_PRIVATE_KEY설정
6.2 인프라 준비
- PostgreSQL 15+ 프로비저닝 (replica 구성 권장)
- Redis 7+ 프로비저닝 (캐시 + Celery 브로커)
- Object Storage 선택 및 설정 (AWS S3 / GCS / Azure / MinIO)
- Celery Worker + Beat 프로세스 배포 구성
-
DB_CONN_MAX_AGE조정 (프로덕션: 300-600 권장) - Sentry DSN 설정
6.3 GraphQL 계층 커스터마이징 시 주의사항
- 새 도메인 추가 시:
saleor/graphql/{domain}/디렉토리 →schema.py,types.py,mutations/,resolvers.py구조 따를 것 -
api.py의 Query/Mutation mixin에 새 도메인 추가 - 모든 mutation은
BaseMutation또는DeprecatedModelMutation상속 필수 -
doc_category설정 (schema 문서화) -
webhook_events_info정의 (webhook 자동 문서화) - DataLoader 패턴 반드시 사용 (N+1 문제 방지)
-
graphql-corev2 제약 이해: 비동기(async) resolver 미지원
6.4 기술 부채 우선순위
| 우선순위 | 항목 | 영향도 |
|---|---|---|
| P0 | graphql-core v2 → v3 마이그레이션 가능성 평가 | Saleor upstream에서 미착수. fork 시 독자적 진행 필요 |
| P1 | stripe SDK v3 → 최신 업그레이드 | 신규 결제 기능 제한 |
| P1 | DeprecatedModelMutation 정리 | 코드 내부에서도 deprecated로 표기됨 |
| P2 | petl==1.7.17 pin 해제 | CSV 내보내기 관련 보안 패치 |
| P3 | OTel 의존성 beta → stable 전환 | semantic-conventions 안정화 대기 |
6.5 Upstream 동기화 전략
- Saleor는 BSD-3-Clause 라이선스 — fork 후 수정/배포 자유
saleor/graphql/하위는 변경 빈도가 높음. 커스텀 코드를 별도 앱/모듈로 분리 권장saleor/settings.py변경은 최소화하고, 환경 변수 또는 별도 settings 파일(saleor/settings_local.py)로 오버라이드- migration 충돌 방지: 커스텀 모델은 별도 Django 앱으로 분리
관련 문서
Index
핵심 개념
- 📒 Saleor Composable Commerce | 📒 Saleor Architecture | 📒 Saleor Core Concepts
- 📒 Saleor GraphQL | 📒 Saleor Apollo Federation
- 📒 Saleor Authentication | 📒 Saleor Permissions | 📒 Saleor Error Handling
도메인
- 📒 Saleor Channels · 📒 Saleor Channels Detail
- 📒 Saleor Products · 📒 Saleor Products Detail
- 📒 Saleor Checkout · 📒 Saleor Checkout Detail
- 📒 Saleor Orders · 📒 Saleor Order Detail
- 📒 Saleor Payments · 📒 Saleor Payments Detail
- 📒 Saleor Stock | 📒 Saleor Shipping Detail | 📒 Saleor Discounts
- 📒 Saleor Users | 📒 Saleor Address | 📒 Saleor Taxes
- 📒 Saleor Price Calculation | 📒 Saleor API Prices | 📒 Saleor API Filtering
확장 및 커스터마이징
- 📒 Saleor Extending Overview | 📒 Saleor Webhooks
- 📒 Saleor Async Events | 📒 Saleor Sync Events Overview
- 📒 Saleor Sync Events Payment | 📒 Saleor Sync Events Tax | 📒 Saleor Sync Events Shipping
- 📒 Saleor Apps | 📒 Saleor App Architecture
- 📒 Saleor Attributes | 📒 Saleor Metadata | 📒 Saleor Data Modeling