Saleor vs Medusa 비교 분석
한국 시장을 타겟으로 하는 이커머스 플랫폼 구축 시 Saleor와 Medusa 중 어떤 오픈소스 코어를 선택할지 판단하기 위한 비교 문서다.
1. 개요
- Saleor — Python/Django 기반의 GraphQL-only 헤드리스 커머스 플랫폼. Channel 모델을 통한 멀티테넌시와 App 시스템을 통한 확장을 지향한다. BSD-3 라이선스.
- Medusa — Node.js/TypeScript 기반의 모듈형 헤드리스 커머스 프레임워크. Workflow + Module 시스템으로 비즈니스 로직을 조합하며, MIT 라이선스로 더 자유로운 사용이 가능하다.
2. 기술 스택 비교
| 항목 | Saleor | Medusa |
|---|---|---|
| 언어 | Python 3.12 | TypeScript (Node.js) |
| 프레임워크 | Django 5.2 | Express.js + 자체 프레임워크 |
| API | GraphQL only (Graphene 2.x) | REST (파일 기반 라우팅) |
| DB | PostgreSQL 전용 | PostgreSQL (MikroORM) |
| ORM | Django ORM | MikroORM |
| 비동기 처리 | Celery + Redis | Workflow SDK (내장) |
| 캐시 | Redis | Redis |
| 라이선스 | BSD-3-Clause | MIT |
| GitHub Stars | ~22.7k | ~30k+ |
Saleor는 Python 생태계의 성숙함과 Django의 안정성을 기반으로 하고, Medusa는 프론트엔드 개발자에게 친숙한 TypeScript 풀스택 환경을 제공한다.
3. 아키텍처 비교
Saleor — 서비스 지향 모놀리스
📒 Saleor Architecture에서 정리한 대로, Saleor는 Web Worker(GraphQL API) + Background Worker(Celery) + PostgreSQL Cluster 구조다. 코드 자체는 Django 모놀리스이지만 수평 확장이 가능한 서비스 그룹으로 배포된다.
- GraphQL 스키마가 모델과 밀결합 (1,743개 파일)
- Graphene 2.x 레거시 —
graphql-core<3사용 중 - Migration 1,388개 누적
Medusa — 모듈 조합형 아키텍처
요청 라이프사이클이 4단계로 명확히 분리된다:
- API Routes (Express.js, 파일 기반 라우팅)
- Workflows (비즈니스 로직 오케스트레이션, Step 단위 롤백 지원)
- Modules (도메인별 독립 패키지, DI 컨테이너로 결합)
- Data Store (PostgreSQL, MikroORM)
Module은 자체 Data Model + Service를 갖는 독립 단위로, medusa.config.ts에 등록하면 DI 컨테이너를 통해 시스템에 통합된다. 코어 수정 없이 Module 추가만으로 기능 확장이 가능하다는 점이 핵심 설계 원칙이다.
아키텍처 비교 요약
| 관점 | Saleor | Medusa |
|---|---|---|
| 구조 | Django 모놀리스 + 수평 확장 | 모듈 조합형 프레임워크 |
| API 접근 | GraphQL 단일 | REST 중심 |
| 로직 분리 | Model-Service-GraphQL 3계층 | Route-Workflow-Module-Store 4계층 |
| 커플링 | GraphQL 스키마 밀결합 | 모듈 간 DI 기반 느슨한 결합 |
| 비동기 | 외부 의존 (Celery + Redis) | Workflow SDK 내장 (Step 롤백 포함) |
4. 멀티테넌시 비교
Saleor — Channel 기반 논리적 분리
📒 Saleor Channels에서 분석한 대로, Channel은 하나의 인스턴스에서 가격, 세금, 재고, 결제 흐름, 통화를 분리하는 논리적 테넌시 모델이다.
- 장점: 상품/고객 데이터 공유가 자연스러움 (멀티브랜드, B2B+B2C 병행)
- 한계: 진정한 DB 격리가 아님. Channel 간 데이터 접근 제어는 주문 수준에만 적용되고, Product/Voucher 등 공유 객체에는 Channel별 권한이 없음
- 엄격한 격리가 필요하면 테넌트별 인스턴스 분리 필요
Medusa — 네이티브 멀티테넌시 없음, 커뮤니티 패턴 존재
Medusa 코어에는 Channel 같은 멀티테넌시 추상화가 없다. 실전에서는 커뮤니티가 다양한 패턴을 적용하고 있다:
- Row-Level Security (RLS): Postgres RLS를 활용한 테넌트 데이터 격리 (Rigby팀 접근법)
- Store/Sales Channel 활용: 기본 제공되는 Region/Sales Channel을 테넌트 구분에 활용
- 미들웨어 주입: 요청 헤더에서 tenant ID를 추출하여 모든 쿼리에 필터 적용
- 실제 사례: Rapid Data가 1,000+ 스토어프론트를 단일 인스턴스에서 운영 중
| 항목 | Saleor | Medusa |
|---|---|---|
| 네이티브 지원 | Channel (논리적 분리) | 없음 |
| DB 격리 | 미지원 (인스턴스 분리 필요) | RLS 또는 스키마 분리 직접 구현 |
| 공유 데이터 | 상품/고객 자연스럽게 공유 | 직접 설계 필요 |
| 실전 검증 | 공식 기능 | 커뮤니티 패턴 (1,000+ 스토어 사례 존재) |
5. 결제 시스템 비교
Saleor
📒 Saleor Payments Detail에서 상세히 분석한 구조:
TransactionItem모델이 gateway-agnostic 설계- 결제 연동 방법:
- (A)
saleor/payment/gateways/에 새 gateway 추가 — 코어 수정 - (B) Saleor App으로 Transaction webhook 처리 — 코어 수정 없음 (권장)
- (A)
- Payment flow가 Channel별로 설정 가능 (인가, 결제 조건, 만료)
Medusa
- Payment Module이 독립 패키지로 분리
- Authorize → Capture → Refund 3단계 플로우
- Payment Collection으로 단일 리소스(cart 등)의 복수 결제 관리
- Custom Payment Provider를 Module로 구현하여 등록
- Webhook 이벤트 핸들링 내장
- Stripe 공식 지원, 그 외는 커뮤니티 플러그인
| 항목 | Saleor | Medusa |
|---|---|---|
| 결제 추상화 | TransactionItem (gateway-agnostic) | Payment Module + Provider |
| 코어 수정 없는 연동 | Saleor App webhook | Custom Module 등록 |
| 복수 결제 | 지원 | Payment Collection |
| Webhook | 지원 | 지원 |
| Stripe | 플러그인 | 공식 모듈 |
6. 확장성 비교
Saleor — App + Plugin + Fork
📒 Saleor Extending Overview에서 정리한 확장 모델:
- Saleor App: 외부 서비스로 구현, Webhook 기반 연동. 코어 수정 없음. Dashboard에서 관리
- Plugin:
saleor/plugins/에 코드 추가. 코어 수정에 해당 - Fork: 코어 직접 수정. Upstream 동기화 부담 발생
- GraphQL 밀결합 때문에 모델 변경 시 mutation/query/resolver 전체 수정 필요
Medusa — Module + Workflow + Subscriber
- Module: 독립 패키지로 도메인 로직 캡슐화.
medusa.config.ts에 등록만 하면 DI로 통합 - Workflow Step/Hook: 기존 Workflow에 Step을 삽입하여 로직 확장. 코어 수정 없음
- Subscriber: 이벤트 기반 확장 (
order.created,cart.updated등) - Service Override: 기존 Service 상속 후 메서드 오버라이드
- API Route: 파일 추가만으로 커스텀 엔드포인트 생성
| 항목 | Saleor | Medusa |
|---|---|---|
| 코어 무수정 확장 | Saleor App (webhook) | Module + Workflow Hook + Subscriber |
| 확장 포인트 수 | App webhook 이벤트 | Module, Workflow, Subscriber, Route, Override |
| 모델 추가 | Migration + GraphQL 스키마 수정 | DML로 모델 정의, Migration 자동 생성 |
| 생태계 | Saleor App Store (공식) | npm 패키지 + 커뮤니티 (Mercur 등) |
| 코어 수정 시 영향 | GraphQL 밀결합으로 광범위 | Module 격리로 영향 범위 제한적 |
7. 한국 현지화 비교
7.1 KRW 통화 처리
| 항목 | Saleor | Medusa |
|---|---|---|
| 원화 설정 | Channel에 currency_code="KRW" | Region에 통화 설정 |
| 소수점 처리 | DEFAULT_DECIMAL_PLACES=0 검증 필요 (📒 Saleor Core Fork Analysis) | MikroORM Money 타입에서 decimal 설정 |
| 난이도 | 낮음 (설정 수준) | 낮음 (설정 수준) |
7.2 한국 주소
| 항목 | Saleor | Medusa |
|---|---|---|
| 주소 라이브러리 | google-i18n-address (한국 기본 지원) | 자체 Address 모델 |
| 한국 형식 | country_area/city/street_address_1 매핑 (📒 Saleor Address) | 커스텀 필드 추가 필요 |
| 도로명주소 API | 프론트엔드 측 구현 | 프론트엔드 측 구현 |
| 난이도 | 낮음 | 중간 (Address 모델 확장 필요) |
7.3 한국 PG 연동 (토스페이먼츠, KG이니시스, 나이스페이)
| 항목 | Saleor | Medusa |
|---|---|---|
| 연동 방식 | Saleor App (Transaction webhook) 권장 | Custom Payment Provider Module |
| 코어 수정 | 불필요 (App 방식) | 불필요 (Module 등록) |
| 기존 한국 PG 플러그인 | 없음 | 없음 |
| 연동 난이도 | 중간 | 중간 |
| TypeScript 친화성 | Python 백엔드 + TS App | 전체 TypeScript |
한국 PG사는 양쪽 모두 직접 구현해야 한다. 다만 Medusa는 전체가 TypeScript이므로 프론트엔드 팀이 백엔드 Payment Provider까지 일관되게 작업할 수 있다는 이점이 있다.
7.4 세금 (VAT)
| 항목 | Saleor | Medusa |
|---|---|---|
| 부가세 10% | TaxClassCountryRate에 flat rate 설정 (📒 Saleor Taxes) | Tax Module에서 region별 설정 |
| 세금포함가 | prices_entered_with_tax=True 지원 | Tax-inclusive pricing 지원 |
| 세금계산서 | 별도 개발 (Invoice 모듈) | 별도 개발 |
| 난이도 | 낮음 (flat rate) | 낮음 (flat rate) |
7.5 커뮤니티/한국어 자료
| 항목 | Saleor | Medusa |
|---|---|---|
| 한국 사용자 | 극소수 | 극소수 |
| 한국어 공식 문서 | 없음 | 없음 |
| 한국 PG 플러그인 | 없음 | 없음 |
| 글로벌 커뮤니티 | Discord (활발), 320+ 기여자 | Discord (활발), GitHub 30k+ stars |
| 한국 사용 사례 | 알려진 사례 없음 | ctrl0 PoC 진행 이력 있음 |
양쪽 모두 한국 현지화 생태계는 부재하며 직접 구축해야 한다.
8. 포크 용이성 비교
| 항목 | Saleor | Medusa |
|---|---|---|
| 라이선스 | BSD-3 (상업 포크 자유) | MIT (더 관대) |
| 코드 규모 | 5,084 파일, Migration 1,388개 | 상대적으로 가벼움 |
| Upstream 동기화 | 매주 릴리스, divergence 빠르게 확대 | 릴리스 주기 규칙적, Module 단위 업데이트 |
| 모델 변경 시 파급 | GraphQL 스키마 전체 수정 (1,743 파일) | Module 내부로 영향 국한 |
| Migration 관리 | squash 필수, 데이터 마이그레이션 포함 시 위험 | MikroORM 자동 생성, Module별 분리 |
| Legacy 부채 | Graphene 2.x, use_legacy_* 플래그 다수 | v2에서 대부분 정리됨 |
| 코어 수정 없는 확장 | 제한적 (App webhook만) | 풍부 (Module, Workflow, Subscriber, Override) |
| 포크 위험도 | 높음 (📒 Saleor Core Fork Analysis) | 중간 |
Medusa는 Module 시스템 덕분에 코어를 포크하지 않고도 대부분의 커스터마이징이 가능하다. Saleor는 GraphQL 밀결합 때문에 모델 수준의 변경이 필요할 경우 포크가 불가피하고, 이후 upstream 동기화 비용이 크다.
9. 종합 평가
점수 비교
| 항목 | Saleor | Medusa | 비고 |
|---|---|---|---|
| 기술 성숙도 | 8 | 7 | Saleor가 더 오래되고 프로덕션 검증 사례 풍부 |
| 아키텍처 모듈성 | 6 | 9 | Medusa의 Module + Workflow 체계가 압도적 |
| 멀티테넌시 | 7 | 5 | Saleor Channel이 네이티브 지원, Medusa는 직접 구현 |
| 결제 확장성 | 7 | 8 | 양쪽 비슷하나 Medusa가 Module 패턴으로 더 깔끔 |
| 한국 현지화 | 7 | 6 | Saleor가 주소/통화에서 약간 우위 |
| 포크 용이성 | 5 | 8 | Medusa가 코어 수정 없이 확장 가능한 범위가 넓음 |
| 개발 생산성 | 6 | 8 | TypeScript 풀스택, 파일 기반 라우팅, DI 패턴 |
| 커뮤니티/생태계 | 7 | 8 | Medusa가 GitHub stars, Mercur 등 생태계 성장세 |
| 운영 복잡도 | 6 | 7 | Saleor는 Celery/Redis 등 외부 의존이 많음 |
| 종합 | 6.6 | 7.3 |
상황별 권장사항
Medusa를 선택해야 하는 경우:
- TypeScript 풀스택 팀 구성일 때
- 코어 포크 없이 Module 단위로 한국 PG, 배송, 세금 로직을 붙이고 싶을 때
- Workflow 기반으로 비즈니스 로직을 Step 단위로 관리하고 롤백까지 보장하고 싶을 때
- WASM 런타임이나 Branch Point 같은 실험적 확장이 필요할 때 (PoC에서 검증 완료)
Saleor를 선택해야 하는 경우:
- Python/Django 역량이 팀에 풍부할 때
- Channel 기반 멀티테넌시가 비즈니스 요구에 정확히 맞을 때 (멀티브랜드, B2B+B2C 병행)
- GraphQL 기반 프론트엔드 생태계를 적극 활용할 때
- 프로덕션 레퍼런스가 많은 검증된 플랫폼이 필요할 때
우리 상황 기준 권장: 한국 PG 연동, 커스텀 배송 로직, 향후 WASM/Branch Point 확장을 고려하면 Medusa가 더 적합하다. 코어 포크 없이 Module 추가만으로 확장할 수 있는 범위가 넓고, TypeScript 단일 스택으로 프론트엔드-백엔드 경계 없이 개발할 수 있다. 다만 멀티테넌시는 직접 설계해야 하므로 초기 아키텍처 투자가 필요하다.
10. 관련 문서
Saleor 분석
- 📒 Saleor Core Fork Analysis — 코어 포크 타당성 분석
- 📒 Saleor Architecture — 시스템 아키텍처
- 📒 Saleor Channels — Channel 기반 멀티테넌시
- 📒 Saleor Payments Detail — 결제 트랜잭션 상세
- 📒 Saleor Taxes — 세금 설정
- 📒 Saleor Address — 주소 형식
- 📒 Saleor Extending Overview — 확장 모델