feat(cafe24): 일괄수정 — 상세페이지 <style> 블록 통일
87개 상품의 상세설명 맨 위 <style> 을 정해진 내용으로 바꾸는 화면을 추가했다.
<style>
div {
text-align: center;
}
</style>
그냥 덮어쓰지 않고 검사 → 선택 → 적용 2단계로 만들었다. 상품 131번의 style 안에는
"비디오 태그 모바일 반응형 스타일" 같은 CSS 가 들어 있어서, 무엇이 지워지는지 보지
않고 87건을 일괄 실행하면 필요한 규칙이 조용히 사라진다. 검사 결과 표에 지금 들어
있는 CSS 를 그대로 보여주고, 변경이 필요한 상품만 자동 선택한다(이미 같은 내용이면
「이미 동일」로 제외).
맨 앞 <style> 블록 하나만 바꾼다. 아래쪽에 <style> 이 더 있으면 건드리지 않고
「블록 2개 · 주의」로 표시해 사람이 판단하게 한다 — 일괄 작업이 남의 CSS 를 조용히
지우는 것이 가장 위험하다. 블록이 없는 상품은 맨 앞에 넣는다.
상품 1건당 1요청으로 쪼갰다. 87건을 한 요청으로 묶으면 1분 가까이 걸려 프록시
타임아웃에 걸리고, 동시에 던지면 카페24 호출 제한(429)에 걸린다. 브라우저가 순차
호출하며 진행률을 보여주고, 한 건 실패가 나머지를 막지 않으며 어디까지 됐는지
화면에 남는다.
적용 순서는 단건 편집과 같은 원칙을 지킨다: 카페24 현재값 재조회 → BACKUP 버전 →
교체 → PUT → MANUAL 버전 + 감사로그(action=bulk_style). 검사 때 읽은 값을 재사용하지
않고 쓰기 직전에 다시 읽는다. PC/모바일 분리 상품은 모바일도 함께 바꾼다.
검증: 유닛테스트 51개 통과(신규 6개 — 앞 블록만 교체하고 뒤 블록 보존, 없을 때 삽입,
멱등, 포맷 후 탭 유지, 여러 줄 원문 정확히 절단). 실제 데이터로 미리보기 로직 확인:
비디오 CSS 가 "지워질 내용"에 잡히고, 이미 동일한 상품은 will_change=False,
style 없는 상품은 삽입 대상으로 판정. 라우트 13개 등록, 템플릿 렌더 확인.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
+29
-2
@@ -26,13 +26,15 @@ app/integrations/cafe24/ ← 공통 (상품관리 + 향후 주문관리
|
||||
app/modules/cafe24/ ← 상품관리 모듈
|
||||
├─ router.py 루트 라우터(prefix=/cafe24) + 서브 라우터 결합
|
||||
├─ routes_products.py 2분할 화면 · 편집기 조각 · 적용(쓰기)
|
||||
├─ routes_bulk.py 일괄수정 (검사 → 선택 적용)
|
||||
├─ routes_system.py 연결(OAuth)·상태·API 로그·작업 로그
|
||||
├─ common.py 가드/컨텍스트 헬퍼 (순환 import 방지로 분리)
|
||||
├─ db.py Cafe24Store (cafe24_db, psycopg3 raw SQL)
|
||||
├─ store.py 순수 로직 — 상수·상태전이·재시도 규칙·검증
|
||||
├─ tests/ DB/네트워크 없는 유닛테스트
|
||||
└─ templates/cafe24/ _nav.html · products.html(2분할) ·
|
||||
_editor.html(오른쪽 조각) · schedules.html · system.html
|
||||
_editor.html(오른쪽 조각) · bulk.html ·
|
||||
schedules.html · system.html
|
||||
```
|
||||
|
||||
**규칙: 라우터에서 `httpx`/`requests` 를 직접 부르지 않는다.** 반드시
|
||||
@@ -52,6 +54,9 @@ app/modules/cafe24/ ← 상품관리 모듈
|
||||
| `GET /cafe24/products/{product_no}/pane` | 오른쪽 편집기 조각 (JS 가 가져감) | `cafe24` |
|
||||
| `GET /cafe24/products/{product_no}` | → `/cafe24/?selected=` 로 리다이렉트(옛 주소) | `cafe24` |
|
||||
| `POST /cafe24/products/{product_no}/apply` | 편집한 HTML 을 카페24에 즉시 적용 | `cafe24` |
|
||||
| `GET /cafe24/bulk` | 일괄수정 화면 (`<style>` 통일) | `cafe24` |
|
||||
| `GET /cafe24/bulk/scan/{product_no}` | 상품 1건 검사 (JSON, 읽기 전용) | `cafe24` |
|
||||
| `POST /cafe24/bulk/apply/{product_no}` | 상품 1건 적용 (JSON) | `cafe24` |
|
||||
| `GET /cafe24/schedules` | 예약관리 (Phase 5 안내) | `cafe24` |
|
||||
| `GET /cafe24/system` | 연결 상태 · API 로그 · 작업 로그 | `cafe24` |
|
||||
| `GET /cafe24/system/oauth/start` | 카페24 인증 시작 | **admin** |
|
||||
@@ -135,6 +140,28 @@ app/modules/cafe24/ ← 상품관리 모듈
|
||||
|
||||
---
|
||||
|
||||
### 2-3. 일괄수정 (`<style>` 블록 통일)
|
||||
|
||||
87개 상품에 한 번에 쓰는 작업이라 **검사 → 선택 → 적용** 2단계로 나눈다. 무엇이
|
||||
지워지는지 보지 않고 실행하면 남의 CSS(예: 비디오 반응형 스타일)가 조용히 사라진다.
|
||||
|
||||
- **맨 앞 `<style>` 블록 하나만** 바꾼다(`store.replace_first_style_block`). 아래쪽에
|
||||
`<style>` 이 더 있으면 건드리지 않고 화면에 「블록 2개 · 주의」로 표시한다.
|
||||
블록이 없으면 맨 앞에 넣는다.
|
||||
- 검사 결과 표에 **지금 들어 있는 CSS 를 그대로 보여준다** — 그게 지워질 내용이다.
|
||||
변경이 필요한 상품만 자동 선택되고, 이미 같은 내용이면 「이미 동일」로 제외된다.
|
||||
- 적용은 상품 1건당 1요청이다. 87건을 한 요청으로 묶으면 1분 가까이 걸려 프록시
|
||||
타임아웃에 걸리고, 동시에 던지면 호출 제한(429)에 걸린다. 브라우저가 순차 호출하며
|
||||
진행률을 보여주고, 한 건 실패가 나머지를 막지 않는다.
|
||||
- 적용 순서는 단건 편집과 같다: **현재값 재조회 → BACKUP → 교체 → PUT → MANUAL +
|
||||
감사로그**(`action=bulk_style`). 지문 대조는 없다 — 방금 읽은 값을 바로 쓰기 때문.
|
||||
- PC/모바일 분리 상품은 모바일도 함께 바꾼다.
|
||||
|
||||
교체할 내용은 `routes_bulk.TARGET_STYLE_BLOCK` 에 있다. `<style>` 안쪽은
|
||||
`format_html` 이 건드리지 않으므로 탭 들여쓰기까지 그대로 저장된다.
|
||||
|
||||
---
|
||||
|
||||
## 3. OAuth 흐름
|
||||
|
||||
```
|
||||
@@ -301,7 +328,7 @@ DB·네트워크 없이 암호화 왕복, 토큰 만료/자동갱신, 상태 노
|
||||
| 4 | 즉시 적용 · BACKUP · Revision · 감사로그 | ✅ 완료 |
|
||||
| 5 | 예약 DB · Worker(compose 서비스) · 예약관리 화면 | 예정 |
|
||||
| 6 | 자동 종료/복원 · 롤백 | 예정 |
|
||||
| 7 | 일괄 수정 · 일괄 예약 · Rate limit 제어 | 예정 |
|
||||
| 7 | 일괄 수정 · 일괄 예약 · Rate limit 제어 | ◐ `<style>` 통일 일괄수정 완료. 일괄 예약 예정 |
|
||||
|
||||
Phase 5 의 worker 는 `app/modules/cafe24/worker.py` 에 둔다 — `Dockerfile` 이
|
||||
`COPY app/ ./app/` 만 하므로 `scripts/` 에 두면 이미지에 포함되지 않는다.
|
||||
|
||||
Reference in New Issue
Block a user