fix(cafe24): 예약 시각을 datetime-local 대신 날짜+시간 별도 입력으로

datetime-local 단일 입력은 한국어 로캘에서 "2026. 08. 14. 오후 07:00" 형태로
렌더링되는데 브라우저·로캘마다 표시 폭이 달라 잘리는 사고가 있었다(실제 발생,
직전 커밋에서 칸 폭을 넓혀 임시로 막았지만 근본 원인은 남아 있었다).

type=date + type=time 두 개의 별도 input 으로 바꿨다. 각각 폭이 고정이라 잘릴
여지가 없다. 제출 직전 JS 가 두 값을 "YYYY-MM-DD" + "T" + "HH:MM" 로 합쳐 hidden
scheduled_at 에 넣는다 — 이 형식은 예전 datetime-local 이 만들던 값과 동일해서
서버(store.parse_schedule_at)는 손대지 않았다. date/time 두 입력에 required 를
둬서, 브라우저의 기본 폼 검증이 빈 값 제출을 막는다(hidden 필드는 타입 특성상
required 검증 대상이 아니라 시각 입력 쪽에 걸었다).

검증: 유닛테스트 61개 통과(백엔드 무변경이라 영향 없음). 브라우저 실측 — 두 입력
각 170px 로 내부 잘림 없음(scrollWidth==clientWidth), 가로 스크롤 없음, 실제 제출
이벤트를 발생시켜 2026-08-20 19:30 입력이 hidden 필드에 "2026-08-20T19:30" 으로
정확히 합쳐지는 것을 확인.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
2026-08-14 17:24:42 +09:00
parent 8bdf8695f9
commit 0862c5572b
6 changed files with 42 additions and 8 deletions
@@ -87,10 +87,19 @@
<input type="hidden" name="list_query" value="{{ list_query }}" />
<input type="hidden" name="html" id="cf24-schedule-html" />
{# datetime-local 은 로캘·브라우저마다 표시 폭이 달라 잘리는 사고가 있었다(실제
발생). 날짜/시간을 별도 input 으로 나누면 각각 폭이 고정이라 잘릴 일이 없다.
제출 직전 JS 가 두 값을 합쳐 hidden scheduled_at 에 넣는다 — 서버(store.
parse_schedule_at)는 예전과 같은 'YYYY-MM-DDTHH:MM' 형식을 그대로 받으므로
백엔드는 변경하지 않았다. #}
<input type="hidden" name="scheduled_at" id="cf24-schedule-at" />
<div class="cf24-schedule-grid">
<label class="cf24-schedule-datetime">
<span>예약 시각</span>
<input class="cf24-schedule-input" type="datetime-local" name="scheduled_at" required />
<span class="cf24-schedule-datetime-row">
<input class="cf24-schedule-input" type="date" id="cf24-schedule-date" required />
<input class="cf24-schedule-input" type="time" id="cf24-schedule-time" required />
</span>
</label>
<label>
<span>진열</span>
@@ -1,7 +1,7 @@
{% extends "erp_base.html" %}
{% block head_extra %}
<link rel="stylesheet" href="/static/cafe24.css?v=20260814t" />
<link rel="stylesheet" href="/static/cafe24.css?v=20260814u" />
{% endblock %}
{% block content %}
@@ -235,6 +235,7 @@
}
// 예약 폼 — 편집기 내용을 hidden 에 복사해 함께 보낸다(폼이 서로 형제라서).
// 날짜·시간도 두 개의 별도 input 값을 합쳐 서버가 기대하는 형식으로 만든다.
var schedForm = pane.querySelector("#cf24-schedule-form");
if (schedForm) {
schedForm.addEventListener("submit", function (e) {
@@ -242,6 +243,13 @@
var holder = document.getElementById("cf24-schedule-html");
var wantHtml = schedForm.querySelector('[name="apply_html"]').checked;
if (holder && box) holder.value = wantHtml ? box.value : "";
var dateEl = document.getElementById("cf24-schedule-date");
var timeEl = document.getElementById("cf24-schedule-time");
var atHolder = document.getElementById("cf24-schedule-at");
// date/time 이 필수(required)라 브라우저가 빈 값이면 여기까지 오지 않는다.
if (atHolder && dateEl && timeEl) atHolder.value = dateEl.value + "T" + timeEl.value;
if (!window.confirm(schedForm.dataset.confirm)) { e.preventDefault(); return; }
// 예약 등록으로 화면을 떠나므로 편집 중 경고를 끈다.
dirty = false;
@@ -1,7 +1,7 @@
{% extends "erp_base.html" %}
{% block head_extra %}
<link rel="stylesheet" href="/static/cafe24.css?v=20260814t" />
<link rel="stylesheet" href="/static/cafe24.css?v=20260814u" />
{% endblock %}
{% block content %}
@@ -1,7 +1,7 @@
{% extends "erp_base.html" %}
{% block head_extra %}
<link rel="stylesheet" href="/static/cafe24.css?v=20260814t" />
<link rel="stylesheet" href="/static/cafe24.css?v=20260814u" />
{% endblock %}
{% block content %}
+14 -3
View File
@@ -620,11 +620,22 @@
grid-column: 1 / -1;
}
/* datetime-local 은 한국어 로캘에서 "2026. 08. 14. 오후 07:00" 형태로 길어져
auto-fit minmax(160px, 1fr) 칸에 잘렸다(실측). 이 칸만 최소폭을 넓힌다. */
/* 날짜/시간을 별도 input 으로 나눈다 — datetime-local 은 한국어 로캘에서
"2026. 08. 14. 오후 07:00" 형태로 길어져 auto-fit minmax(160px, 1fr) 칸에
잘렸다(실측). date/time 은 각각 폭이 고정이라 잘릴 일이 없다. */
.cf24-schedule-datetime {
grid-column: span 2;
min-width: 240px;
min-width: 220px;
}
.cf24-schedule-datetime-row {
display: flex;
gap: var(--sp-8, 8px);
}
.cf24-schedule-datetime-row .cf24-schedule-input {
flex: 1 1 0;
min-width: 0;
}
.cf24-schedule-input {
+7 -1
View File
@@ -174,7 +174,13 @@ app/modules/cafe24/ ← 상품관리 모듈
단건 적용과 같은 다듬기(URL 인코딩 → 소스 정리)를 거치므로 화면에서 본 값이 저장된다.
- 예약 폼은 적용 폼과 **형제**로 둔다(HTML 은 폼 중첩을 허용하지 않는다). 편집기 내용은
JS 가 hidden 에 복사해 함께 보낸다.
- **시각은 KST 로 해석**한다(`datetime-local` 은 타임존이 없다). 과거 시각은 거부하되
- **입력은 날짜/시간 별도 input** 이다(`type=date` + `type=time`). `datetime-local`
단일 입력은 한국어 로캘에서 표시 폭이 브라우저마다 달라 잘리는 사고가 있었다(실제
발생 — "2026. 08. 14. 오후 07:00" 형태). 두 input 은 각각 폭이 고정이라 잘릴 일이
없다. 제출 직전 JS 가 두 값을 `"YYYY-MM-DD" + "T" + "HH:MM"` 로 합쳐 hidden
`scheduled_at` 에 넣는다 — 서버(`store.parse_schedule_at`)는 예전과 같은 형식을
받으므로 백엔드는 그대로다.
- **시각은 KST 로 해석**한다. 과거 시각은 거부하되
폼을 채우는 동안 시간이 흐른 경우를 위해 1분 여유를 둔다.
- **실행은 `app/modules/cafe24/worker.py`** — compose 서비스 `dbx-cafe24-worker`
`--loop 60` 으로 돈다. 웹 요청 안에서 기다리는 방식은 프록시 타임아웃·재기동에