feat(cafe24): 편집기에서 PC/모바일 구분 제거 — 항상 같은 내용으로 반영

요청대로 단순화했다. 모바일 상세설명 보기 영역과 「모바일도 함께」 체크박스를 없애고,
적용 시 description 과 mobile_description 에 **항상 같은 HTML** 을 쓴다.
separated_mobile_description 값과 무관하므로 한쪽만 바뀌어 어긋나는 사고가 없어진다.

다만 분리 사용 상품의 모바일 내용이 PC 와 달랐다면 그 내용을 덮어쓰게 된다. 그래서
쓰기 전에 **모바일 내용도 BACKUP revision 으로 따로 남긴다** — 백업이 없으면 되찾을
방법이 없다. 기존 BACKUP 은 PC 값만 담고 있었다.

부수 정리: 편집기 컨텍스트에서 html_mobile 제거, mobile_html 의 None 분기 제거,
감사로그 표기를 "PC·모바일 동시 반영"으로 고정, 라벨을 "상세설명 HTML · PC/모바일
공통"으로 변경.

검증: 유닛테스트 51개 통과. 분리+내용상이 상품으로 편집기를 렌더해 모바일 보기 영역·
체크박스가 사라지고 공통 문구·버전 이력·다시 읽기가 남은 것을 확인.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
2026-08-14 15:46:32 +09:00
parent 34476ba611
commit 39dc28d664
7 changed files with 38 additions and 71 deletions
+20 -18
View File
@@ -17,6 +17,9 @@
MANUAL 버전 + 감사로그
로컬 DB 의 마지막 버전을 "지금 카페24에 올라간 값"으로 가정하지 않는다.
PC/모바일은 구분하지 않는다 — 적용 시 `description` 과 `mobile_description` 에
같은 HTML 을 쓴다(운영 방침). 분리 사용 상품이어도 한쪽만 바뀌는 일이 없다.
이미지 경로의 한글은 카페24에 퍼센트 인코딩으로 저장돼 있다. 편집기에는
`store.decode_html_urls` 로 풀어서 보여주고, 저장할 때 `encode_html_urls` 로
되돌린다(왕복 보존 — store.py 주석 참고).
@@ -139,9 +142,6 @@ def _editor_ctx(st: Any, product_no: int) -> dict[str, Any]:
# (2) 태그마다 줄을 나눠 정리해서 보여준다.
# 저장할 때 같은 정리를 거친 값을 카페24에 쓴다(화면과 저장값이 같다).
"html_pc": store.format_html(store.decode_html_urls(desc.description)) if desc else "",
"html_mobile": (
store.format_html(store.decode_html_urls(desc.mobile_description)) if desc else ""
),
# 지문은 **인코딩된 원본**으로 만든다(적용 직전 카페24 값과 비교하므로).
"fingerprint": store.fingerprint(desc.description) if desc else "",
"revisions": st.list_revisions(product_no, limit=20),
@@ -241,7 +241,6 @@ def product_apply(
base_fingerprint: str = Form(""),
memo: str = Form(""),
list_query: str = Form(""),
apply_mobile: str = Form(""),
):
"""편집한 HTML 을 카페24에 즉시 적용한다.
@@ -251,8 +250,8 @@ def product_apply(
3) 편집 시작 시점의 지문과 비교해 충돌이면 거부한다
4) 쓰고, MANUAL 버전과 감사로그를 남긴다
PC/모바일 미분리 상품은 모바일 필드 같은 HTML 로 맞춘다. 분리 상품
모바일을 건드리지 않는다(화면에 별도 반영 안내를 띄운다).
PC/모바일을 구분하지 않는다 — 두 필드 같은 HTML 을 쓴다. 분리 사용 상품
모바일 내용이 PC 와 달랐다면 덮어쓰기 전에 그 내용도 BACKUP 으로 남긴다.
"""
checked = guard(request)
if not isinstance(checked, tuple):
@@ -290,6 +289,16 @@ def product_apply(
memo="적용 직전 자동 백업",
created_by=actor,
)
# 모바일 내용이 PC 와 달랐다면 그것도 따로 남긴다. 아래에서 모바일을 PC 와 같게
# 덮어쓰므로, 백업하지 않으면 그 내용을 되찾을 방법이 없다.
if current.mobile_description and current.mobile_description != current.description:
st.add_revision(
product_no=product_no,
html_content=current.mobile_description,
revision_type=store.REVISION_BACKUP,
memo="적용 직전 자동 백업 (모바일 — PC와 달랐던 내용)",
created_by=actor,
)
if base_fingerprint and base_fingerprint != store.fingerprint(current.description):
st.log_audit(
@@ -301,17 +310,11 @@ def product_apply(
status_code=303,
)
# 미분리 상품은 모바일도 함께 맞춘다 — PC 만 바꾸면 모바일 상세가 어긋난다.
# 분리 상품은 화면의 「모바일도 함께」 체크에 따른다(두 내용이 같으면 기본 체크,
# 다르면 기본 해제 — 일부러 다르게 만든 모바일 페이지를 덮어쓰지 않기 위해).
if current.separated_mobile:
mobile_html = submitted if apply_mobile else None
else:
mobile_html = submitted
# PC/모바일을 구분하지 않는다 — 항상 같은 내용으로 함께 쓴다(운영 방침).
# 분리 사용 상품이어도 모바일에 같은 HTML 을 넣으므로 한쪽만 바뀌는 일이 없다.
mobile_html = submitted
if submitted == current.description and (
mobile_html is None or mobile_html == current.mobile_description
):
if submitted == current.description and mobile_html == current.mobile_description:
return RedirectResponse(url=f"{back}&msg=변경된 내용이 없어 적용하지 않았습니다.", status_code=303)
try:
products.update_descriptions(
@@ -338,8 +341,7 @@ def product_apply(
st.log_audit(
actor=actor, action="apply_description", product_no=product_no,
revision_id=revision_id, result="SUCCESS",
detail=f"{len(submitted)}자 적용 (백업 {backup_id}"
+ (", 모바일 동시 반영)" if mobile_html is not None else ")"),
detail=f"{len(submitted)}자 적용 (백업 {backup_id}, PC·모바일 동시 반영)",
)
logger.info("카페24 상품 %s 상세설명 적용 (%s)", product_no, actor)
return RedirectResponse(
@@ -31,20 +31,7 @@
{% if desc %}
<p class="cf24-note">
{% if desc.separated_mobile %}
<strong>PC/모바일 분리 사용 상품입니다.</strong>
모바일({{ desc.mobile_description | length }}자)은 아래 <strong>「모바일도 함께」</strong>
체크박스로 같은 내용을 반영할 수 있습니다.
{% if desc.mobile_differs %}
<span class="cf24-warn">지금 모바일 내용이 PC와 달라 기본 해제되어 있습니다</span>
체크하면 모바일이 PC와 같은 내용으로 덮어써집니다.
{% else %}
현재 두 내용이 같아 기본 체크되어 있습니다.
{% endif %}
{% else %}
모바일은 PC와 동일 설정이라 적용 시 <strong>함께 반영</strong>됩니다.
{% endif %}
{% if desc.mobile_differs %}<span class="cf24-warn">현재 PC/모바일 내용이 다릅니다.</span>{% endif %}
적용하면 <strong>PC·모바일 상세설명이 같은 내용으로</strong> 반영됩니다.
<br />
소스는 <strong>태그마다 줄을 나눠 정리</strong>해서 보여주며, <strong>적용하면 정리된 소스가
그대로 카페24에 저장</strong>됩니다. 줄바꿈·들여쓰기만 바뀌고 태그 구조는 그대로이며,
@@ -62,17 +49,7 @@
<input type="hidden" name="list_query" value="{{ list_query }}" />
<div class="cf24-editor-bar">
<span class="cf24-muted">
PC 상세설명 HTML · {{ desc.description | length }}자
{% if desc.separated_mobile %}
<label class="cf24-check-inline"
title="분리 사용 상품이라 기본적으로 PC 만 바뀝니다. 체크하면 모바일 상세설명도 같은 내용으로 함께 반영합니다.">
<input type="checkbox" name="apply_mobile" value="1"
{% if not desc.mobile_differs %}checked{% endif %} />
모바일도 함께
</label>
{% endif %}
</span>
<span class="cf24-muted">상세설명 HTML · {{ desc.description | length }}자 · PC/모바일 공통</span>
<span class="cf24-editor-bar-right">
<input class="cf24-memo" type="text" name="memo" maxlength="200"
placeholder="변경 메모 (버전 이력에 남습니다)" />
@@ -98,17 +75,6 @@
</div>
</form>
{% if desc.separated_mobile or desc.mobile_differs %}
<details class="cf24-details">
<summary>모바일 상세설명 HTML 보기 (읽기 전용 · {{ desc.mobile_description | length }}자)</summary>
<textarea id="cf24-html-mo" class="cf24-html" rows="12" readonly
spellcheck="false">{{ html_mobile }}</textarea>
<div class="cf24-actions">
<button type="button" class="erp-btn erp-btn-outline" data-copy="cf24-html-mo">복사</button>
</div>
</details>
{% endif %}
<details class="cf24-details">
<summary>버전 이력 {% if revisions %}({{ revisions | length }}건){% endif %}</summary>
{% if revisions %}
@@ -1,7 +1,7 @@
{% extends "erp_base.html" %}
{% block head_extra %}
<link rel="stylesheet" href="/static/cafe24.css?v=20260814k" />
<link rel="stylesheet" href="/static/cafe24.css?v=20260814m" />
{% endblock %}
{% block content %}
@@ -1,7 +1,7 @@
{% extends "erp_base.html" %}
{% block head_extra %}
<link rel="stylesheet" href="/static/cafe24.css?v=20260814k" />
<link rel="stylesheet" href="/static/cafe24.css?v=20260814m" />
{% endblock %}
{% block content %}
@@ -1,7 +1,7 @@
{% extends "erp_base.html" %}
{% block head_extra %}
<link rel="stylesheet" href="/static/cafe24.css?v=20260814k" />
<link rel="stylesheet" href="/static/cafe24.css?v=20260814m" />
{% endblock %}
{% block content %}
@@ -1,7 +1,7 @@
{% extends "erp_base.html" %}
{% block head_extra %}
<link rel="stylesheet" href="/static/cafe24.css?v=20260814k" />
<link rel="stylesheet" href="/static/cafe24.css?v=20260814m" />
{% endblock %}
{% block content %}