| 일 | 월 | 화 | 수 | 목 | 금 | 토 |
|---|---|---|---|---|---|---|
| 1 | 2 | 3 | 4 | |||
| 5 | 6 | 7 | 8 | 9 | 10 | 11 |
| 12 | 13 | 14 | 15 | 16 | 17 | 18 |
| 19 | 20 | 21 | 22 | 23 | 24 | 25 |
| 26 | 27 | 28 | 29 | 30 | 31 |
- React
- 프론트엔드
- contextBridge
- Android
- 인증
- python
- REST API
- github actions
- php
- Vite
- 마이그레이션
- 백엔드
- FastAPI
- JWT
- typescript
- 소셜로그인
- 배포
- 아키텍처
- next.js
- 크롤링
- 보안
- electron
- 메일자동화
- Kotlin
- sqlite
- javascript
- 상태관리
- 도메인설계
- 온디바이스AI
- playwright
- Today
- Total
HM소프트
상품·카테고리 도메인 설계와 복합 필터 구현 — 이커머스 백엔드 (1) 본문
다축 복합 필터를 한 쿼리로 푸는 설계. 고정값 축은 컬럼, 운영 중 바뀌는 축은 타입 판별자를 둔 카테고리 테이블과 다대다로 가르고, 걸린 조건만 AND 누적·입력 화이트리스트·두 단계 조회와 윈도 함수로 N+1을 막는다. 요청 일련번호로 경합을, id 정렬로 비결정성을 잡는다.
이커머스(교육 자료 판매) 백엔드의 상품·카테고리 도메인 설계와 복합 필터 구현을 정리한다.

시리즈를 시작하며
교육 자료(강의·학습 자료)를 사고파는 플랫폼을 만들었다. 상품을 카테고리로 분류하고, 여러 조건으로 필터링하고, 결제·회원·관리자 기능까지 갖춘 커머스다. 백엔드는 Python, 프론트엔드는 TypeScript로 짰다.
세 편으로 나눠 기록한다.
- (이 글) 상품·카테고리 도메인과 복합 필터
- 인증과 소셜 로그인(네이버·카카오)
- 배포·운영·알림 (Docker · CI/CD · 메일)
특정 서비스명·도메인은 모두 제외하고 구현만 다룬다.
상품 도메인 — 일반 상품과 강의 상품
판매하는 건 단순 파일만이 아니라 강의 상품도 있었다. 그래서 상품을 다룰 때 "강의형 상품"을 별도로 처리하는 로직이 필요했다. 상품 목록 API와 관리자 기능에서 강의 상품을 구분해 다뤘다.
핵심: 카테고리와 복합 필터
이 플랫폼의 상품은 여러 축으로 분류된다. 영역, 갈래, 학년 같은 분류 축이다. 사용자는 이 축들을 조합해 원하는 자료를 좁혀 찾는다. 그래서 복합 필터(여러 조건 동시 적용)가 핵심 기능이었다.
// 복합 필터 — 여러 축을 동시에 적용
interface ProductFilter {
area?: string; // 영역
genre?: string; // 갈래
grade?: string; // 학년
categoryId?: number;
}
// 상품 목록 API가 이 필터들을 AND로 결합해 조회
GET /products?area=...&genre=...&grade=...
상품 목록 API에 area(영역)/genre(갈래) 필터를 추가하고, 목록에서 영역·갈래 정보를 함께 표시하도록 했다. 필터를 점진적으로 개선하며 복합 필터를 지원했다.
카테고리를 단일 소스로 — Category 테이블 연동
분류 값(영역·갈래·학년 등)을 화면마다 하드코딩하면 금방 어긋난다. 그래서 Category 테이블을 단일 소스(source of truth)로 삼았다. 예를 들어 계정 설정의 "학년 선택"도 코드에 박힌 목록이 아니라 Category 테이블에서 가져오도록 바꿨다. 분류 체계가 바뀌면 테이블만 고치면 모든 화면에 반영된다.
// 학년 선택지를 코드가 아니라 DB(Category)에서
const grades = await fetchCategories({ type: 'grade' });
버그: 카테고리 정렬과 product_count
카테고리를 다루며 두 가지 버그를 잡았다.
1) product_count 계산 오류
카테고리별 "상품 개수(product_count)"가 틀리게 집계됐다. 카테고리에 속한 상품 수를 세는 쿼리가 정확하지 않았던 것. 집계 로직을 바로잡았다.
2) 정렬 불일치 — id 추가
카테고리 정렬이 화면과 관리자 API에서 달랐다. 정렬 기준에 id를 추가해, 같은 우선순위일 때도 순서가 결정적으로 정해지게 했다. 정렬 키가 모호하면(동순위 다수) 화면마다 순서가 들쭉날쭉해지는데, id를 마지막 정렬 키로 두면 항상 같은 순서가 보장된다.
ORDER BY sort_order ASC, id ASC -- 동순위는 id로 결정적 정렬
이 "관리자와 사용자 화면의 정렬을 일치시킨다"는 작은 작업이, 실제로는 "어, 관리자에서 본 순서랑 다르네?" 류의 혼란을 없애는 중요한 수정이었다.
정리하며
- 상품 유형 구분(일반/강의)을 도메인에 반영.
- 복합 필터(영역·갈래·학년)로 다축 분류 상품을 좁혀 찾기.
- Category 테이블을 단일 소스로 — 분류 체계를 한 곳에서 관리.
- 집계(product_count)와 결정적 정렬(id 추가) 버그를 잡아 화면 간 일관성 확보.
다음 2편에서는 커머스의 관문, 인증과 소셜 로그인(네이버·카카오)을 다룬다.
'외주 개발일지' 카테고리의 다른 글
| Docker + CI/CD 배포와 메일 알림 구현 — 교육 플랫폼 (3) (0) | 2026.06.13 |
|---|---|
| 네이버·카카오 소셜 로그인 구현과 bcrypt 함정 — 교육 플랫폼 (2) (0) | 2026.06.13 |
| TipTap 에디터·인증·E2E 테스트 구현 — 연구기관 사이트 (3) (0) | 2026.06.13 |
| 웹 국제화(i18n)·SEO·접근성·성능 최적화 — 연구기관 사이트 (2) (0) | 2026.06.13 |
| pg_trgm 통합 검색과 콘텐츠 모델 설계 — PostgreSQL (1) (0) | 2026.06.13 |