생성과 편집
작성 맥락과 저장 상태를 유지하며 복잡한 데이터를 편집합니다.
목표
사용자가 필요한 가치를 이해한 뒤 최소한의 정보로 새 대상을 만들고, 편집 중인 값과 저장된 값을 혼동하지 않으며, 오류나 이동이 발생해도 작업 맥락을 잃지 않게 한다.
생성과 편집을 구분한다
- 생성 화면은 결과물의 가치와 필요한 최소 입력을 먼저 보여준다.
- 편집 화면은 현재 대상, 저장된 상태, 변경된 상태를 먼저 보여준다.
- 생성과 편집이 같은 form을 공유해도 title, 주 행동 레이블, 취소 목적지는 구분한다.
- 복제는 생성의 한 형태다. 어떤 값이 복사되고 무엇을 새로 정해야 하는지 명시한다.
정보 구조
PageHeader: 대상 또는 생성 목적, 저장 상태, 주 행동- 필요하면
EnvironmentBanner: preview와 실제 운영 차이 Section: 한 번에 하나의 개념FormField: label, 입력 전 설명, 오류- page 또는 workflow action: 저장, 검증, 다음 단계
긴 폼은 데이터 모델 순서가 아니라 사용자가 답하기 쉬운 순서로 배치한다. 이미 시스템이 아는 값은 채우고 확인만 받거나 아예 묻지 않는다.
입력 판단 규칙
- 지금 단계에 꼭 필요한 값만 요구한다.
- 사용자가 3~5초 안에 답하기 어려운 질문에는 예시, 기준 또는 선택지를 제공한다.
- 구현 방식이나 내부 상태를 선택하게 하지 않는다.
- 기본값은 가장 흔한 값이 아니라 현재 사용자의 맥락에서 안전한 값이어야 한다.
- 민감한 정보는 필요한 이유, 사용 범위, 보관 방식을 입력 전에 설명한다.
- placeholder를 label로 사용하지 않는다.
저장 모델
명시적 저장
- 여러 필드가 하나의 일관된 변경 단위일 때 사용한다.
- 주 행동은
저장보다초안 저장,설정 저장처럼 대상을 말한다. - 변경이 없으면 button을 비활성화할 수 있으나 이유를 명확히 이해할 수 있어야 한다.
자동 저장
- 작은 변경을 자주 하고 충돌 가능성이 낮을 때 사용한다.
저장 중,저장됨,저장 실패를 같은 위치에서 보여준다.- 저장 실패를 성공처럼 조용히 유지하지 않는다.
- 사용자가 페이지를 닫아도 되는 시점을 알 수 있게 한다.
검증
- 입력 전에 알 수 있는 제약은 description으로 제공한다.
- 입력 형식 오류는 field 가까이에 표시한다.
- 여러 field의 관계 오류는 관련 section 또는 form summary에도 연결한다.
- submit 후 첫 오류로 focus를 이동하고 오류 수를 알린다.
- 서버 오류가 발생해도 입력값을 보존한다.
- 보안상 원인을 자세히 말할 수 없어도 사용자가 수정할 수 있는 문제인지, 기다리거나 문의해야 하는지 구분한다.
이탈과 충돌
- 미저장 변경이 있을 때만 이탈 보호를 사용한다.
- 취소나 뒤로 가기의 목적지를 분명히 표시한다.
- 다른 사용자의 최신 변경이 있으면 자동 덮어쓰지 않는다. 현재 변경과 최신본의 차이, 다시 불러오기, 별도 저장 경로를 제공한다.
- 저장 성공 후 목록으로 이동할지 상세에 남을지 작업 목적에 따라 일관되게 결정한다.
상태
- initial loading: form 구조를 유지하는 skeleton 또는 전체 loading
- ready/pristine: 저장된 값과 변경 없음
- dirty: 수정됨, 아직 저장되지 않음
- validating: 입력 유지, 검증 중
- saving: 중복 submit 방지, 진행 레이블
- saved: 저장 시각 또는 짧은 성공 feedback
- validation error: field와 summary 연결
- save error: 입력 보존, 같은 요청 재시도
- conflict: 최신본과 충돌, 사용자의 명시적 해결 필요
- permission changed: 작성 내용을 보존하고 가능한 복사·문의 경로 제공
좁은 화면
- 2열 form은 한 열로 전환하되 논리적 입력 순서를 유지한다.
- 고정 action bar가 마지막 field와 오류를 가리지 않게 safe spacing을 둔다.
- outline+editor 구조는 outline 선택 뒤 editor로 이동하는 순차 흐름으로 바꾼다.
- 긴 도움말은 접기 전에 입력 판단에 필수인 핵심 문장을 항상 보인다.
접근성
- 모든 control에 programmatic label을 제공한다.
- required는 시각적 표식과 실제
required또는aria-required를 함께 사용한다. - 오류는
aria-invalid,aria-errormessage또는aria-describedby로 연결한다. - form submit 실패 시 오류 summary와 첫 오류 focus 전략을 정의한다.
- disabled field가 제출에서 제외되는지 read-only와 구분한다.
- keyboard만으로 입력, 도움말 확인, 저장, 오류 복구가 가능해야 한다.
- 자동 저장 상태는 polite live region으로 전달하되 입력마다 반복 낭독하지 않는다.
예제
<PageScaffold>
<PageHeader
title="새 커리큘럼"
description="학습자가 처음 만나게 될 목적과 기본 순서를 정해요."
actions={
<Button form="curriculum-form" type="submit">
초안 만들기
</Button>
}
/>
<form id="curriculum-form" onSubmit={submit} noValidate>
<Section title="기본 정보" description="목록과 학습 시작 화면에 함께 보여요.">
<FormField id="name" label="커리큘럼 이름" error={errors.name} required>
<Input name="name" required />
</FormField>
</Section>
</form>
</PageScaffold>피해야 할 사용
- 결과 가치를 설명하기 전에 긴 가입·입력 요구
- 시스템이 이미 아는 값을 반복해서 질문
- placeholder로 label 대체
- 저장 실패 시 입력값 초기화
- 변경이 없는데 모든 이동에 확인 dialog 표시
- 서로 다른 저장 단위를 하나의 거대한 submit에 묶기
- 자동 저장 실패를 작은 색상 점으로만 표시
검토 체크
- 사용자가 생성 결과의 가치를 입력 전에 이해한다.
- 지금 꼭 필요한 값만 질문한다.
- 질문 순서가 사용자가 아는 정보 순서와 맞다.
- label, description, error의 역할이 분리돼 있다.
- pristine, dirty, saving, saved, error, conflict가 정의돼 있다.
- 저장 실패와 권한 변경에서도 입력을 보존한다.
- 미저장 변경이 있을 때만 이탈을 보호한다.
- 좁은 화면의 field와 action 순서가 논리적이다.
- submit 오류 focus와 summary가 정의돼 있다.