- 발행일
신청 항목 셋 중 한 건이면 제출 — 조건이 단계를 가로지르면 안내도 단계 밖에 둔다
신청 항목 셋 중 한 건이면 제출
표준데이터 등록 신청서는 다섯 단계입니다. 기본정보 → 데이터 엘리먼트 → 데이터 프레임 → 메시지 → 신청 사유. 가운데 세 단계가 실제 항목을 쓰는 자리예요.
제출 조건은 메시지 한 건이었습니다. 엘리먼트와 프레임은 0건이어도 되고, 메시지만 정확히 한 건 있으면 제출됐어요. 이 조건이 9월 9일에 풀렸습니다. 엘리먼트·프레임·메시지를 통틀어 최소 한 건이면 제출됩니다. 엘리먼트 하나만 등록하려는 신청도 있으니까요.
바꾸는 일 자체는 조건문 한 줄입니다. 그 조건문이 몇 군데서 말해지고 있었는지가 문제였어요.
판정 조건을 셋 중 하나로
같은 날 오후, 팀원이 제출 확인창과 서버 POST가 판정 함수 submission_errors() 하나를 쓰도록 묶어 둔 상태였습니다. 그 안의 조건은 "메시지가 있나"였고, 저는 그 조건을 has_any_item()으로 떼어 셋 중 하나로 바꿨어요.
def has_any_item(application):
"""엘리먼트·프레임·메시지 중 한 건이라도 작성됐는지."""
document = normalized_document(application.document)
return bool(document["elements"] or document["frames"] or document["message"])
팀원이 9월 초에 만들어 둔 normalized_document()가 앞에 있는 게 중요합니다. 신청 항목은 JSON 스냅샷 한 덩어리로 저장되는데, 메시지 자리에 리스트나 빈 사전이 들어와 있을 수 있어요. 이 함수는 메시지가 사전일 때만 메시지로 인정합니다.
def normalized_document(document):
source = document if isinstance(document, dict) else {}
return {
"elements": list(source.get("elements") or []),
"frames": list(source.get("frames") or []),
"message": (
source.get("message") if isinstance(source.get("message"), dict) else None
),
}
이게 없으면 메시지 자리에 이름만 든 리스트 같은 값이 있어도 참으로 세어져 제출이 통과합니다. 판정 입구가 이미 하나라 이 방어도 그대로 따라왔어요.
단계 정의에서는 메시지 단계의 optional을 True로 바꿨습니다. 이제 세 단계가 전부 선택이에요. 제출 조건은 단계별 필수가 아니라 셋을 가로지르는 조건이라, 작성 화면의 (선택) 표시와 [다음 단계] 활성 조건이 셋 다 같아집니다.
보완 링크도 4단계에서 2단계로 옮겼어요. 뭘 써야 할지 모르는 사람을 가장 작은 단위로 보내는 게 맞습니다.
안내를 단계 밖으로 꺼냈다
그날 오후 팀원이 붙여 둔 안내는 메시지 단계 본문 안에 있었습니다.
{% if step.code == "MSG" and not items %}
<p class="...">
메시지는 필수 항목입니다. [+ 항목 추가]로 메시지 한 건을 작성하고 저장해야 제출할 수 있습니다.
</p>
{% endif %}
작성 화면의 2~4단계 본문은 서버가 그린 조각이고, 저장·수정·삭제 때마다 그 단계 조각만 통째로 갈아 끼워집니다. 그래서 조건이 한 단계 안에 갇혀 있을 때는 이 자리가 맞았어요. 메시지를 저장하면 메시지 조각이 다시 그려지면서 안내가 사라집니다.
조건이 세 단계를 가로지르는 순간 이 배치가 깨집니다. 2단계에서 엘리먼트를 하나 넣으면 제출 조건은 충족되는데, 다시 그려지는 건 2단계 조각뿐이에요. 4단계에 붙어 있는 "메시지는 필수" 안내는 낡은 채로 그대로 남습니다.
그래서 안내를 세 단계 위 한 줄로 옮겼습니다.
{# 제출 조건은 단계별 필수가 아니라 세 단계를 가로지른다 — 한 단계 안에 붙이면
다른 단계에서 항목을 넣어도 그 조각만 다시 그려져 안내가 낡은 채로 남는다 #}
<p class="rounded-lg border border-primary bg-primary/10 px-4 py-3 text-sm text-gray-700">
데이터 엘리먼트·데이터 프레임·메시지 중 최소 한 건은 작성해야 제출할 수 있습니다.
</p>
안내가 사는 자리는 조건이 걸치는 범위를 따라갑니다. 부분 갱신이 있는 화면에서는 이게 취향이 아니라 정확성 문제예요.
안내창은 사유와 방법을 갈랐다
제출 버튼을 누르면 확인창이 뜨고, 조건을 못 채웠으면 보완 사유가 나옵니다. 팀원이 오후에 붙인 사유는 한 문장이었어요.
메시지는 필수 항목입니다. 4. 메시지 단계에서 [+ 항목 추가]로 메시지 한 건을 작성하고 저장해 주세요.
이걸 무엇이 잘못됐나와 어디서 무엇을 하나로 나눴습니다. 앞은 굵은 경고색, 뒤는 회색 설명이에요. 한 덩어리로 붙여 놓으면 긴 문장 하나로 읽혀서 어느 단계로 가야 할지가 안 보입니다.
errors.append(
{
"step": 2,
"message": _("데이터 엘리먼트·데이터 프레임·메시지 중 최소 한 건은 작성해야 합니다."),
"guide": _("2~4단계에서"),
"guide_button": _("+ 항목 추가"),
"guide_tail": _("로 한 건을 작성하고 저장해 주세요."),
}
)
버튼 이름을 따로 뗀 이유는 화면에서 실제 버튼 모양으로 그리기 위해서입니다. 대괄호로만 감싸면 문장에 묻혀요. 창 폭도 24rem에서 28rem으로 넓혔는데, 보완 사유가 붙으면 좁은 폭에서 문장이 너덜너덜 접히기 때문입니다.
서버가 POST를 막을 때는 반대로 보완 방법을 안 붙입니다.
# 보완 방법(guide)은 붙이지 않는다 — 곧바로 그 단계로 보내고,
# 거기 상단 안내가 무엇을 해야 하는지 이미 말한다. 여기까지 이으면
# 경고 한 줄이 세 문장짜리 붉은 덩어리가 된다.
어차피 그 단계로 보내고, 도착한 화면 위에 방금 옮겨 놓은 안내 한 줄이 서 있으니까요. 같은 말을 두 번 하지 않습니다.
75분 뒤, 이용안내가 아직 거짓말을 하고 있었다
같은 날 밤 이용안내를 열어 봤더니 표준데이터 탭 4단계 설명이 이랬습니다.
- 메시지: 교환되는 최상위 단위입니다. … 신청서 한 건에 메시지 하나이며 이 단계는 필수입니다.
75분 전에 제가 푼 조건이었어요. 같은 커밋에서 이용안내의 1단계 용어(식별 이름 → 표준데이터 이름)까지 고쳐 놓고, 세 줄 아래 4단계 문장을 놓쳤습니다. 규칙 문장은 화면 단위가 아니라 문장 단위로 찾아야 했어요. 그 자리도 결국 하나의 화면이고, 사용자는 오히려 여기를 먼저 읽어요.
- 메시지: … 신청서 한 건에 메시지 하나이며, 2~4단계를 통틀어 최소 한 건을 작성해야 제출할 수 있습니다.
이 규칙을 말하는 자리가 네 곳이라는 걸 이때 셌습니다. 서버 판정(has_any_item), 작성 화면 상단 안내, 제출 확인창, 이용안내 본문. 판정은 한 곳에 있지만 문장은 여전히 셋이고, 그 셋은 코드가 맞춰 주지 않습니다. 화면이 없어진 기능을 설명하고 있던 8월 일과 같은 계보예요. 그때는 몇 달 묵은 거짓이었고 이번엔 75분짜리였다는 차이뿐입니다.
용어도 같이 바꿨다
신청 1단계의 식별 이름을 표준데이터 이름으로 고쳤습니다. 신청·심사 화면, 확인 화면 항목 표, 심사 목록 검색창 안내문, 이용안내, 작성 예시 PDF까지요.
모델 필드명과 verbose_name은 그대로 뒀습니다. 마이그레이션이 필요해서 별도로 다뤄야 하거든요. 화면 라벨과 저장 필드명이 갈린 상태가 남았고, 이건 문서 정본에 적어 뒀습니다.
검증
새로 쓴 테스트는 둘입니다.
test_element_alone_opens_submit_dialog— 엘리먼트만 있을 때와 프레임만 있을 때를subTest로 각각 돌려 제출창이 열리는지 봅니다. 이번 변경으로 새로 통과해야 하는 경로예요.test_authoring_page_explains_one_of_three_requirement— 작성 화면 상단에 "셋 중 최소 한 건" 안내가 서는지.
나머지는 팀원이 같은 날 오후에 걸어 둔 잠금을 새 조건에 맞춰 고친 것입니다.
test_empty_document_explains_block_in_submit_dialog— 셋 다 비면 막히는지를 보던 확인창 테스트에, 보완 링크가?step=2로 가는지와 버튼 이름·앞뒤 문장이 다 나오는지를 기대값으로 바꿔 넣었습니다.test_direct_submit_without_any_item_shows_error_and_preserves_draft— 화면을 오래 열어 뒀거나 직접 POST한 경우입니다. 메시지 자리에 비정상 값 네 가지를 넣어 돌리고, 기록 상태·작성 내용·심사 이력이 남고 제출 알림이 안 나가는지 단언하는 설계는 팀원 것 그대로예요. 엘리먼트가 있으면 이제 통과하니 문서의 엘리먼트를 비우고, 초안 보존을 확인할 값을 따로 넣었습니다.- 팀원이 만든 브라우저 검증 스크립트도 2단계 기준으로 기대값을 고쳤습니다.
남은 것 · 한계
- 규칙을 말하는 문장이 여전히 세 곳입니다. 판정은 함수 하나에 있지만 안내 문구는 작성 화면·확인창·이용안내에 따로 적혀 있어요. 이번에 이용안내가 75분 늦은 것처럼, 다음에 조건이 또 바뀌면 같은 일이 납니다. 문구까지 한 곳에서 만들어 세 화면이 받아 쓰게 하는 게 맞지만 이번엔 안 했습니다.
- 모델 필드명은 아직
identifier_name입니다. 화면은 "표준데이터 이름", DB는 "식별 이름"이에요. 마이그레이션을 미룬 대가로 둘이 갈린 상태를 안고 갑니다. - 저장은 항목이 없어도 됩니다. 초안 보존이 목적이라 그대로 뒀는데, 저장과 제출을 혼동하는 사람에게는 "저장했는데 왜 제출이 안 되나"가 여전히 남아요. 기존 '저장 후 확인' 안내에 기대고 있습니다.
- 메시지는 여전히 한 건까지입니다. 필수만 풀었지 개수 제한은 그대로예요. 신청서 한 건에 메시지 하나라는 구조 자체는 이번 범위가 아니었습니다.
관련 글: 화면이 없어진 기능을 설명하고 있었다 · 제출 단계 칩에 스튜어드가 찍혀 있었다 · 신청서 단계 구획이 로딩 중에 전부 보였다 · 신청서 항목 모달에서 후보 조회 20회를 2회로 줄였다