feat(cafe24): 상품관리를 좌우 2분할로 — 목록(좁게) | 상세페이지 편집(넓게)
왼쪽에서 상품을 클릭하면 오른쪽에 편집기가 바로 열린다. 목록을 다시 받지 않고
오른쪽 조각만 교체한다(GET /products/{no}/pane → JS 삽입). 목록까지 다시 그리면
클릭마다 카페24 호출이 2회 더 늘어나기 때문이다. JS 가 실패하거나 없으면 각 행의
링크(/cafe24/?selected=)로 그대로 동작한다.
목록은 페이지를 없애고 전체를 한 번에 받는다(list_all_products, 1회 100개·상한
1000개). 필터·정렬을 한 페이지에만 적용하면 다음 페이지에 있는 상품이 빠져
"진열중만 보기" 가 거짓이 된다. 현재 87개라 1회 호출로 끝난다.
컬럼은 요청대로 번호·상품명·진열·판매·수정 5개다. 좁은 칸에 맞춰 진열/판매는
배지 대신 점, 수정일은 월-일만 표시하고 전체 값은 title 로 둔다. 긴 상품명은
2줄로 제한해 행 높이를 고르게 유지한다(전체 이름은 title·편집기 제목에서 확인).
진열중/판매중 체크박스는 중복 선택이 되며 둘 다 켜면 AND 다. 문서에 없는 API
필터 파라미터에 기대지 않고 받아온 뒤 파이썬에서 걸러낸다. 제목행 클릭은
오름↔내림 토글이며 한글 정렬은 localeCompare(ko) 를 쓴다.
편집 영역을 넓게 쓰려고 이 화면에서만 .erp-page 의 max-width 를 풀었다. 이때
box-sizing:border-box 를 함께 줘야 한다 — width:100% + padding:24px 이라
max-width 만 풀면 문서 전체에 가로 스크롤이 생긴다(측정으로 확인 후 수정).
편집 중 다른 상품을 클릭하거나 페이지를 벗어나면 저장 안 됨 경고를 띄운다.
옛 단독 화면(product.html)은 제거하고 /products/{no} 는 2분할 화면으로
리다이렉트한다. 편집기 조각을 두 곳에서 함께 쓰도록 _editor.html 로 분리했다.
검증: 유닛테스트 33개 통과(신규 3개 — 전체 조회의 페이지 순회·상한 처리·1회
종료). 상한 처리는 테스트가 잡아서 고쳤다(요청한 만큼 받았는지로 판정). 가짜
데이터로 렌더해 브라우저에서 실측: 왼쪽 360px·오른쪽 940px, 각 칸 독립 스크롤,
분할 영역이 화면 높이에 맞고, 가로 스크롤 없음, 정렬 오름/내림 동작 확인.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -71,6 +71,39 @@ def list_products(
|
||||
return products if isinstance(products, list) else []
|
||||
|
||||
|
||||
def list_all_products(
|
||||
client: Cafe24Client,
|
||||
*,
|
||||
product_name: str = "",
|
||||
max_items: int = 1000,
|
||||
) -> tuple[list[dict[str, Any]], bool]:
|
||||
"""전체 상품을 페이지를 넘겨가며 모두 가져온다.
|
||||
|
||||
2분할 화면의 왼쪽 목록은 페이지 없이 한 번에 보여주고 필터·정렬을 브라우저에서
|
||||
처리한다. 그래야 "진열중만" 같은 필터가 전체 기준으로 정확해진다
|
||||
(한 페이지만 받아 걸러내면 다음 페이지의 해당 상품이 빠진다).
|
||||
|
||||
반환: (상품 목록, 상한에 걸려 잘렸는지)
|
||||
상품이 max_items 를 넘으면 거기서 멈춘다 — 무한 호출로 API 제한에 걸리는
|
||||
것을 막기 위한 안전장치다(현재 쇼핑몰 87개, 1회 100개 조회).
|
||||
"""
|
||||
collected: list[dict[str, Any]] = []
|
||||
while len(collected) < max_items:
|
||||
want = min(PAGE_LIMIT, max_items - len(collected))
|
||||
batch = list_products(
|
||||
client,
|
||||
limit=want,
|
||||
offset=len(collected),
|
||||
product_name=product_name,
|
||||
)
|
||||
collected.extend(batch)
|
||||
if len(batch) < want:
|
||||
return collected, False # 요청한 만큼 못 받았다 = 마지막 페이지
|
||||
if len(collected) >= max_items:
|
||||
return collected, True # 상한에서 멈췄다 — 뒤에 더 있을 수 있다
|
||||
return collected, False
|
||||
|
||||
|
||||
def get_product(client: Cafe24Client, product_no: int) -> dict[str, Any]:
|
||||
"""상품 1건 상세. 이 응답에 상세설명 필드까지 들어 있다."""
|
||||
no = int(product_no)
|
||||
|
||||
Reference in New Issue
Block a user