08ebe7b53d
요청은 스킨 detail.html 의 `const numbers = [...]` 를 고치는 것이었지만, 카페24
Admin API 로는 스킨 HTML 파일을 읽거나 쓸 수 없다. 확인 결과 테마는 조회만
가능하고(GET /admin/themes) 스킨 파일 엔드포인트가 없다. 쓸 수 있는 것은 테마
페이지와 스크립트 태그뿐이다.
그래서 같은 결과를 상품 상세설명 안의 CSS 로 낸다. 상세설명은 이미 우리가 쓸 수
있는 영역이고, 상품별로 켜고 끌 수 있으며, 상태가 그 상품 소스에 그대로 보인다.
새 권한이나 재인증도 필요하지 않다.
<style id="cf24-hide-common-promo">.edb-img-tag-w{display:none !important}</style>
id 로 우리 블록만 찾으므로 사람이 쓴 <style> 은 건드리지 않는다. 넣기/빼기는
멱등이고 소스 정리(format_html)를 거쳐도 상태가 유지된다.
PC·모바일 모두 반영한다. 미분리 상품은 같은 HTML 이 양쪽에 들어가고, 분리 상품은
모바일 본문을 건드리지 않되 이 블록만 모바일에도 맞춘다 — 양쪽에 걸지 않으면 한쪽에
홍보가 그대로 남는다. PC/모바일 상태가 다르면 화면에 불일치를 알린다.
"변경 없음" 판정에 모바일 변경도 포함시켰다. PC 는 그대로인데 모바일 숨김만
바뀌는 경우가 있어서, 예전 조건이면 아무 일도 하지 않고 끝났다.
스킨의 numbers 목록과는 독립이며 충돌하지 않는다(스킨은 요소 제거, 이쪽은 CSS 숨김).
이미 목록에 있는 상품은 그대로 두면 된다.
검증: 유닛테스트 49개 통과(신규 4개 — 추가/제거 왕복, 멱등, 포맷 통과 후 인식,
사람이 쓴 style 보존).
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
340 lines
18 KiB
Markdown
340 lines
18 KiB
Markdown
# 카페24 상품 상세페이지 관리 모듈 (cafe24)
|
|
|
|
> 카페24 관리자 페이지에 직접 들어가지 않고 상품 상세페이지를 조회·편집·예약
|
|
> 적용하고, 언제든 이전 상태로 되돌리기 위한 운영용 모듈.
|
|
> main-app ERP 에 편입된 **모듈**이다(별도 앱/포트 아님). 인증은 기존 Google
|
|
> OAuth + 권한키 `cafe24` 를 재사용한다.
|
|
|
|
---
|
|
|
|
## 1. 구조 — 왜 두 곳으로 나눴나
|
|
|
|
향후 **카페24 주문관리**(주문 조회·송장 일괄등록·취소/반품/교환)를 같은
|
|
프로젝트에 추가할 예정이다. 그래서 카페24 인증/전송은 상품관리에 종속시키지
|
|
않고 공통 계층으로 분리했다.
|
|
|
|
```
|
|
app/integrations/cafe24/ ← 공통 (상품관리 + 향후 주문관리 공유)
|
|
├─ config.py 환경변수 → Cafe24Config (하드코딩 금지)
|
|
├─ crypto.py 토큰 Fernet 암복호화
|
|
├─ oauth.py 인증 URL / code→token / refresh
|
|
├─ tokens.py TokenService — 저장·만료판정·자동갱신(행 잠금)
|
|
├─ client.py Cafe24Client — 전송·재시도·401/429/5xx·호출간격·API 로그
|
|
├─ products.py 상품 엔드포인트 래퍼 (향후 orders.py 를 형제로 추가)
|
|
└─ errors.py 공통 예외
|
|
|
|
app/modules/cafe24/ ← 상품관리 모듈
|
|
├─ router.py 루트 라우터(prefix=/cafe24) + 서브 라우터 결합
|
|
├─ routes_products.py 2분할 화면 · 편집기 조각 · 적용(쓰기)
|
|
├─ 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
|
|
```
|
|
|
|
**규칙: 라우터에서 `httpx`/`requests` 를 직접 부르지 않는다.** 반드시
|
|
`app.integrations.cafe24` 의 `Cafe24Client` 를 통한다(재시도·로그·토큰 갱신이
|
|
한 곳에 모여 있어야 하기 때문).
|
|
|
|
핸들러는 `async def` 가 아니라 **`def`(동기)** 로 선언한다. 카페24 API·DB 호출이
|
|
블로킹이므로 FastAPI 스레드풀에서 돌게 두는 편이 이벤트 루프를 막지 않는다.
|
|
|
|
---
|
|
|
|
## 2. 화면 / 경로
|
|
|
|
| 경로 | 화면 | 권한 |
|
|
| --- | --- | --- |
|
|
| `GET /cafe24/` | 2분할 화면 (`q`, `display`, `selling`, `selected`) | `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/schedules` | 예약관리 (Phase 5 안내) | `cafe24` |
|
|
| `GET /cafe24/system` | 연결 상태 · API 로그 · 작업 로그 | `cafe24` |
|
|
| `GET /cafe24/system/oauth/start` | 카페24 인증 시작 | **admin** |
|
|
| `GET /cafe24/oauth/callback` | 카페24 콜백 (code→토큰) | **admin** |
|
|
| `POST /cafe24/system/oauth/disconnect` | 저장된 토큰 삭제 | **admin** |
|
|
| `GET /cafe24/health` | 포털 카드 상태 점 | 없음 |
|
|
|
|
---
|
|
|
|
## 3. OAuth 흐름
|
|
|
|
```
|
|
관리자 [카페24 연결]
|
|
↓ state 생성 → 세션 저장
|
|
GET /cafe24/system/oauth/start → 302 카페24 인증 페이지
|
|
↓ 사용자 승인
|
|
GET /cafe24/oauth/callback?code=&state=
|
|
↓ 세션 state 와 대조 (불일치 시 토큰 교환 거부 — CSRF 방어)
|
|
code → access/refresh token 교환
|
|
↓ Fernet 암호화
|
|
cafe24_oauth_tokens 저장
|
|
```
|
|
|
|
- scope 는 `app/integrations/cafe24/config.py` 의 `PRODUCT_SCOPES` =
|
|
`mall.read_product`, `mall.write_product`.
|
|
주문관리 추가 시 `ORDER_SCOPES` 를 합쳐 넘기고, 카페24 개발자센터 앱에서도
|
|
권한을 추가한 뒤 **재인증**하면 된다.
|
|
- access token 은 만료 2분 전부터 자동 갱신된다. 갱신은 토큰 행을
|
|
`SELECT ... FOR UPDATE` 로 잠근 채 수행 — web 컨테이너와 worker 컨테이너가
|
|
동시에 refresh 해서 한쪽 토큰이 무효화되는 것을 막는다(카페24는 refresh
|
|
token 을 회전시킨다).
|
|
- refresh token 이 만료되면 자동 복구가 불가능하므로 화면에 "재연결 필요"를
|
|
표시한다.
|
|
|
|
---
|
|
|
|
### 2-1. 상품관리 화면 구성 (2분할)
|
|
|
|
```
|
|
┌─ 550px ───────────────────┬───────── 남은 폭 전부 ─────────┐
|
|
│ 상품명 검색(한 줄) │ 상품 이름 · 번호 · 진열/판매 │
|
|
│ ☐진열중 ☐판매중 │ PC 상세설명 HTML 편집기 │
|
|
│ ── 목록(전체, 스크롤) ── │ (문법 강조 · 남은 높이 전부) │
|
|
│ 번호 상품명 진열 판매 수정 │ [메모] [복사] [카페24에 적용] │
|
|
│ (제목행 클릭 = 정렬) │ ▸ 모바일 HTML(분리 상품만) │
|
|
│ │ ▸ 버전 이력 │
|
|
└───────────────────────────┴─────────────────────────────────┘
|
|
```
|
|
|
|
- 검색 입력란은 **한 줄 높이로 고정**한다(`height: 32px`). `.cf24-filters` 가 세로
|
|
flex 이므로 `flex-basis` 를 주면 그 값이 **높이**로 적용돼 입력란이 거대해진다.
|
|
실제로 그 사고가 있었다 — flex 방향을 항상 확인할 것.
|
|
|
|
- **왼쪽은 전체 목록**(페이지 없음). `list_all_products` 로 페이지를 넘겨가며 전부
|
|
받는다(1회 100개, 상한 1000개). 필터를 한 페이지에만 적용하면 다음 페이지의
|
|
해당 상품이 빠지기 때문이다.
|
|
- **필터**는 `진열중`/`판매중` 체크박스이며 **중복 선택 시 AND** 다. 문서에 없는 API
|
|
파라미터에 기대지 않고 받아온 뒤 파이썬에서 걸러낸다.
|
|
- **정렬**은 제목행 클릭(오름↔내림 토글). 브라우저에서 처리하므로 전체를 받아둔
|
|
덕분에 목록 전체가 대상이 된다.
|
|
- **상품 클릭 시 오른쪽만 교체**한다(`/pane` 조각을 fetch → 삽입). 목록을 다시 받지
|
|
않으므로 카페24 호출이 1회로 끝난다. JS 실패 시 각 행의 링크로 정상 동작한다.
|
|
- 편집 중 다른 상품을 클릭하거나 페이지를 벗어나면 **저장 안 됨 경고**가 뜬다.
|
|
- `.erp-page` 의 `max-width` 를 이 화면에서만 풀어 편집 영역을 넓게 쓴다. 이때
|
|
`box-sizing: border-box` 를 함께 줘야 한다(안 주면 padding 이 폭에 더해져 문서에
|
|
가로 스크롤이 생긴다).
|
|
|
|
---
|
|
|
|
## 3-1. 상세설명 API 사실 (실물 확인 결과 — 추측 금지)
|
|
|
|
운영 쇼핑몰(`miraskitchen`)에서 직접 확인한 내용이다. 문서에 없는 경로를
|
|
추측해서 쓰지 말 것.
|
|
|
|
- **`/admin/products/{no}/description` 서브리소스는 존재하지 않는다.**
|
|
호출하면 `No API found.` 가 온다. 상세설명은 **상품 리소스의 필드**다.
|
|
|
|
```
|
|
GET /admin/products/{no} → description · mobile_description ·
|
|
separated_mobile_description
|
|
PUT /admin/products/{no} → {"request": {"description": "..."}}
|
|
```
|
|
|
|
- **목록 API(`GET /admin/products`) 응답에는 `description` 이 없다.**
|
|
그래서 상세설명은 상품 1건씩 조회해야 하고, 목록 화면에 미리보기를 뿌리지
|
|
않는다(상품 87개 × 1호출 = 호출 제한 위험).
|
|
|
|
- **PC/모바일 상세설명이 분리되어 있다.** `separated_mobile_description`
|
|
(`'T'`/`'F'`) 이 분리 사용 여부다. `'F'`(미분리) 상품을 수정할 때는
|
|
`description` 과 `mobile_description` 을 같은 HTML 로 함께 맞춘다.
|
|
`'T'` 면 두 값을 따로 관리해야 한다.
|
|
|
|
- 그 밖에 상세 응답에만 있는 참고 필드: `translated_description`(다국어),
|
|
`summary_description`(요약설명), `simple_description`, `shop_no`(멀티쇼핑몰).
|
|
|
|
- **이미지 경로의 한글은 퍼센트 인코딩되어 저장된다.**
|
|
|
|
```
|
|
src="/web/product/big/%EC%9A%A9%EA%B8%B0…(%ED%99%A9%ED%86%A0)_12.gif"
|
|
```
|
|
|
|
사람이 읽을 수 없으므로 화면에서는 `store.decode_html_urls` 로 풀어서 보여주고,
|
|
저장할 때 `store.encode_html_urls` 로 되돌린다. 두 함수는 서로의 역이며
|
|
**왕복이 보존된다**(편집하지 않고 적용해도 저장값이 바뀌지 않는다 — 테스트로 고정).
|
|
안전 규칙: 디코딩은 non-ASCII(`%80`~`%FF`)만, 인코딩은 URL 속성값 안만.
|
|
`%20`·`%3C` 를 풀거나 본문 한글을 인코딩하면 페이지가 깨진다.
|
|
|
|
---
|
|
|
|
### 2-2. 편집기 (문법 강조 · 소스 정리)
|
|
|
|
**문법 강조** — 색칠된 `<pre>` 위에 **투명한 `<textarea>`** 를 정확히 겹쳐 놓는
|
|
방식이다. 외부 라이브러리를 쓰지 않는다(자체 호스팅 원칙).
|
|
|
|
- 두 층의 **폰트·글자크기·줄높이·padding·`white-space`·`tab-size` 가 완전히 같아야**
|
|
글자가 어긋나지 않는다. 줄 번호 칸(`.cf24-gutter`)도 같은 글꼴 지표를 써야 줄이
|
|
맞는다. `cafe24.css` 의 세 선택자를 항상 함께 수정할 것.
|
|
- **크기는 `<pre>` 가 정하고 textarea 가 `inset:0` 으로 그 위를 덮는다.** JS 로 높이를
|
|
계산하지 않는다(textarea 의 `scrollHeight` 는 브라우저가 한두 줄 더 잡아 어긋난다 —
|
|
실측 830 vs 792). 스크롤은 바깥 `.cf24-code` 가 담당한다.
|
|
- **줄바꿈하지 않고 가로로 스크롤한다**(`white-space: pre` + `wrap="off"`).
|
|
줄바꿈을 허용하면 한 논리 줄이 여러 행이 되어 줄 번호가 맞지 않는다.
|
|
- 줄 번호 칸은 `position: sticky; left: 0` 이라 가로로 스크롤해도 왼쪽에 남는다.
|
|
- 20만 자를 넘으면 강조를 끄고 평문으로 보여준다(타이핑마다 재색칠하면 느려짐).
|
|
- 색: 태그 초록 / 속성이름 갈색 / 속성값 남색 / 주석 회색 기울임 / 기호 회색.
|
|
|
|
**소스 정리** — `store.format_html()`. 화면에 보여줄 때와 저장할 때 **같은 함수**를
|
|
쓰므로, 화면에서 본 소스가 그대로 카페24에 저장된다.
|
|
|
|
- 줄을 나누는 것은 **블록 요소 경계에서만** 한다. HTML 에서 공백은 의미가 있어서
|
|
인라인 요소 사이에 줄바꿈을 넣으면 화면에 공백이 생긴다(이미지 사이가 벌어지는
|
|
고전적인 사고). `img`·`br`·`span`·`a` 는 블록 목록에서 **의도적으로 제외**했다.
|
|
- **원문에 이미 있던 줄바꿈은 살리고, 각 줄을 현재 깊이로 들여쓴다.** 상세페이지는
|
|
`<img>` 를 한 줄에 하나씩 적어두는 경우가 많고 그 모양이 저자의 의도다. 줄 앞
|
|
공백은 렌더링에 영향이 없으므로 들여쓰기는 안전하다.
|
|
- 주석은 줄을 강제로 나누지 않는다. `<!-- 대파_타임랩스 --><img ...>` 처럼 바로 뒤
|
|
요소를 설명하는 주석이 많아, 나누면 라벨과 대상이 떨어져 오히려 읽기 나빠진다.
|
|
- 구획용 **빈 줄은 한 줄까지 유지**한다(여러 줄은 하나로 줄인다).
|
|
- `<style>`·`<script>`·`<pre>`·`<textarea>` 안쪽은 한 글자도 건드리지 않는다.
|
|
- 내용이 한 줄뿐인 짧은 블록은 다시 한 줄로 합친다(`<td>1</td>`).
|
|
- **멱등**이다 — 편집하지 않고 다시 적용해도 저장값이 계속 바뀌지 않는다(테스트로 고정).
|
|
- 닫는 태그가 빠진 HTML 이 흔하므로 들여쓰기 깊이에 상한(12)을 둔다. 어떤 이유로든
|
|
실패하면 **원본을 그대로** 돌려준다(정리보다 안 깨지는 게 중요).
|
|
|
|
### 2-3. 상단 공통 홍보 숨기기
|
|
|
|
스킨(`detail.html`)에는 공통 홍보를 지울 상품번호가 박혀 있다.
|
|
|
|
```js
|
|
const numbers = [12,31,32, ...]; // .edb-img-tag-w 를 remove()
|
|
```
|
|
|
|
**카페24 Admin API 로는 이 파일을 고칠 수 없다.** 확인 결과 스킨/테마는 조회만
|
|
가능하고(`GET /admin/themes`), 스킨 HTML 파일을 읽거나 쓰는 엔드포인트가 없다.
|
|
쓸 수 있는 것은 테마 페이지와 스크립트 태그(`/admin/scripttags`)뿐이다.
|
|
|
|
그래서 같은 결과를 **상품 상세설명 안의 CSS** 로 낸다. 상세설명은 우리가 쓸 수 있고,
|
|
상품별로 켜고 끌 수 있으며, 상태가 그 상품 소스에 그대로 보인다.
|
|
|
|
```html
|
|
<style id="cf24-hide-common-promo">/* DBX ERP: 상단 공통 홍보 숨김 */
|
|
.edb-img-tag-w{display:none !important}
|
|
</style>
|
|
```
|
|
|
|
- 편집기 상단 **「상단 공통 홍보 숨기기」** 체크박스로 켜고 끈다. 적용할 때 반영된다.
|
|
- `id` 로 우리 블록만 찾는다 — 사람이 쓴 `<style>` 은 건드리지 않는다.
|
|
- **PC·모바일 모두 적용된다.** 미분리 상품은 같은 HTML 이 양쪽에 들어가고,
|
|
분리 상품은 모바일 본문은 그대로 두고 이 블록만 모바일에도 맞춘다(양쪽에 걸어야
|
|
효과가 있다).
|
|
- 넣기/빼기가 멱등이며 포맷을 거쳐도 상태가 유지된다(테스트로 고정).
|
|
- 스킨의 `numbers` 목록과는 독립이다. 이미 목록에 있는 상품은 그대로 두면 된다
|
|
(스킨은 요소를 제거하고, 이쪽은 CSS 로 숨긴다 — 결과는 같고 충돌하지 않는다).
|
|
|
|
> 스킨의 `numbers` 방식을 그대로 자동화하려면 `/admin/scripttags` 로 전역 스크립트를
|
|
> 주입하는 방법이 있다. 다만 **디자인 쓰기 권한 추가 + 재인증**이 필요하고, 상품별
|
|
> 상태가 한 곳에 몰려 관리가 어려워진다. 지금은 채택하지 않았다.
|
|
|
|
---
|
|
|
|
## 3-2. 편집·적용 규칙 (`POST /products/{no}/apply`)
|
|
|
|
이 순서를 절대 바꾸지 않는다.
|
|
|
|
0. 제출된 HTML 을 `encode_html_urls` → `format_html` 순으로 다듬는다(화면에서 본
|
|
정리된 소스가 그대로 저장된다).
|
|
1. **카페24에서 현재 HTML 을 다시 읽는다.** 로컬 DB 의 마지막 버전을 "지금
|
|
올라간 값"으로 가정하지 않는다(카페24 관리자에서 직접 고쳤을 수 있다).
|
|
2. 그 값으로 **BACKUP revision** 을 남긴다. 유일한 복구 수단이다.
|
|
3. **지문 대조** — 편집 화면을 열 때의 `fingerprint`(sha256 앞 32자)와 지금
|
|
카페24 값의 지문이 다르면 적용을 거부한다. 편집 중 남이 바꾼 내용을 조용히
|
|
덮어쓰는 것을 막는 낙관적 잠금이다.
|
|
4. 내용이 같으면 호출하지 않는다(불필요한 쓰기·API 호출 방지).
|
|
5. PUT 적용 → **MANUAL revision** + 감사로그(`apply_description`).
|
|
|
|
추가 규칙:
|
|
|
|
- 빈 내용은 거부한다(상세페이지 전체를 날리는 실수 방지).
|
|
- **PC/모바일 미분리(`separated_mobile_description='F'`) 상품은 모바일 필드도
|
|
같은 HTML 로 함께 쓴다.** PC 만 바꾸면 모바일이 어긋난다.
|
|
분리(`'T'`) 상품은 모바일을 건드리지 않고, 화면에 "모바일은 따로 반영" 을 알린다.
|
|
- 실패해도 BACKUP 은 이미 남아 있으므로 오류 메시지에 버전 번호를 알려준다.
|
|
|
|
---
|
|
|
|
## 4. 보안 규칙 (반드시 지킬 것)
|
|
|
|
- `client_secret`·토큰을 코드에 하드코딩하지 않는다. 전부 `.env`.
|
|
- **로그·예외 메시지·템플릿에 토큰/시크릿을 절대 출력하지 않는다.**
|
|
`cafe24_api_logs` 에도 Authorization 헤더를 기록하지 않는다.
|
|
- 토큰은 DB 에 Fernet 암호문으로만 저장한다(`CAFE24_TOKEN_SECRET`).
|
|
- 카페24 연결/해제는 `is_admin` 전용.
|
|
- OAuth 콜백은 세션 `state` 대조 후에만 code 를 교환한다.
|
|
- SQL 은 `%s` 플레이스홀더만 사용한다(문자열 조립 금지).
|
|
|
|
---
|
|
|
|
## 5. 설치 / 실행
|
|
|
|
### 5-1. 카페24 개발자센터 앱 등록 (사람이 해야 하는 일)
|
|
|
|
1. <https://developers.cafe24.com> 로그인 → 앱 생성
|
|
2. Redirect URI 를 `.env` 의 `CAFE24_REDIRECT_URI` 와 **정확히 동일하게** 등록
|
|
(운영: `https://dbx.no1king.freeddns.org/cafe24/oauth/callback`)
|
|
3. 권한(Scope)에 `mall.read_product`, `mall.write_product` 체크
|
|
4. 발급된 Client ID / Client Secret 을 `.env` 에 기입
|
|
|
|
### 5-2. DB 초기화 (superuser 로 1회)
|
|
|
|
```bash
|
|
read -s -p "cafe24_app password: " APP_PWD; echo
|
|
docker exec -i postgres-db psql -U postgres \
|
|
-v app_password="$APP_PWD" \
|
|
< scripts/sql/cafe24_db_init.sql
|
|
```
|
|
|
|
### 5-3. .env
|
|
|
|
```
|
|
CAFE24_DB_URL=postgresql://cafe24_app:<APP_PWD>@postgres-db:5432/cafe24_db
|
|
CAFE24_MALL_ID=miraskitchen
|
|
CAFE24_CLIENT_ID=...
|
|
CAFE24_CLIENT_SECRET=...
|
|
CAFE24_REDIRECT_URI=https://dbx.no1king.freeddns.org/cafe24/oauth/callback
|
|
CAFE24_API_VERSION=2026-03-01
|
|
CAFE24_TOKEN_SECRET=<openssl rand -hex 32>
|
|
```
|
|
|
|
### 5-4. 재기동 + 권한 부여
|
|
|
|
```bash
|
|
cd /opt/www/main && docker compose up -d --build web
|
|
```
|
|
|
|
관리자 페이지(`/admin`)에서 직원에게 **카페24 상품관리** 권한을 부여한다.
|
|
|
|
---
|
|
|
|
## 6. 테스트
|
|
|
|
```bash
|
|
python -m app.modules.cafe24.tests.test_cafe24
|
|
```
|
|
|
|
DB·네트워크 없이 암호화 왕복, 토큰 만료/자동갱신, 상태 노출(토큰 미유출),
|
|
재시도 예산, 예약 상태 전이를 검증한다.
|
|
|
|
---
|
|
|
|
## 7. 진행 상태
|
|
|
|
| Phase | 내용 | 상태 |
|
|
| --- | --- | --- |
|
|
| 1 | 공통 Integration · cafe24_db · OAuth 연결 화면 | ✅ 완료 |
|
|
| 2 | 상품 목록·검색·현재 HTML 조회 | ✅ 완료 |
|
|
| 3 | 편집기 · 미리보기 · Diff · 초안 | ◐ 문법 강조 편집기 + 소스 정리 완료. 미리보기·Diff·초안 예정 |
|
|
| 4 | 즉시 적용 · BACKUP · Revision · 감사로그 | ✅ 완료 |
|
|
| 5 | 예약 DB · Worker(compose 서비스) · 예약관리 화면 | 예정 |
|
|
| 6 | 자동 종료/복원 · 롤백 | 예정 |
|
|
| 7 | 일괄 수정 · 일괄 예약 · Rate limit 제어 | 예정 |
|
|
|
|
Phase 5 의 worker 는 `app/modules/cafe24/worker.py` 에 둔다 — `Dockerfile` 이
|
|
`COPY app/ ./app/` 만 하므로 `scripts/` 에 두면 이미지에 포함되지 않는다.
|
|
`docker-compose.yml` 에 같은 이미지로 `dbx-cafe24-worker` 서비스를 추가해
|
|
`python -m app.modules.cafe24.worker --loop 60` 으로 돌린다.
|