폼 디자인의 정석: 모든 입력란은 하나의 질문이다
폼은 인터페이스가 무언가를 보여주기를 멈추고 묻기 시작하는 순간입니다. 모든 입력란은 지금 다른 일을 하고 싶어 하는 사람에게 던지는 질문이며, 그래서 불필요한 입력란은 하나하나가 강요이고 헷갈리는 입력란은 작은 배신입니다. 폼을 다루는 기술은 머릿속에 담아 둘 수 있을 만큼 짧은 정석으로 압축됩니다. 질문은 최대한 적게, 한 열로, 레이블은 항상 보이게 둡니다. 답은 사람이 자연스럽게 적는 그대로 받습니다. 검증은 타이핑 도중이 아니라 사용자가 하나의 생각을 끝냈을 때 실행합니다. 오류 메시지는 문제를 어떻게 고치면 되는지 알려 주는 문장으로 씁니다. 그리고 어떤 상황에서도 사용자가 입력한 내용을 버리지 않습니다. 현장에서 마주치는 폼의 마찰은 대부분 이 중 하나를 어긴 결과입니다. {.answer-block}
TL;DR
- 폼은 대화이므로 사람이 묻듯이 물어야 합니다: 질문은 가능한 한 적게, 순서가 있는 한 열에, 주제별로 묶습니다. 그리고 모든 입력란은 묻는 데 드는 비용에 견주어 정당화해야 합니다.
- 레이아웃은 이미 결론이 났습니다: 단일 열, 레이블은 입력란 위, 필드 너비는 답의 길이를 암시합니다. 다중 열 폼과 플로팅 레이블 같은 기교는 실제 이해도를 세련돼 보인다는 착각과 맞바꿉니다.
- 답은 사람이 주는 형태 그대로 받으십시오. 군더더기 공백은 잘라 내고, 전화번호는 어떤 형식이든 받아들이며, 세 개의 상자로 쪼개지 말고 한 칸으로 유지합니다. 정규화는 소프트웨어의 일이지 사용자의 일이 아닙니다.
- 검증은 키 입력마다가 아니라 blur 시점에 실행하고, 오류는 가르치는 문장으로 씁니다: 무엇이 잘못됐고 어떻게 고치면 되는지를 해당 입력란 바로 옆에 적습니다.
- 사용자의 입력은 신성합니다. 전송에 실패했다고 폼을 비워 버리거나 이유를 말해 주지 않는 비활성 버튼은, 협조할 마음이 있던 참여자를 떠나간 참여자로 바꿔 놓습니다.
왜 모든 입력란이 질문인가
폼을 면접 기록이라고 생각하면 그 품질이 눈에 보이기 시작합니다. 유능한 면접관이 습관처럼 팩스 번호를 묻겠습니까? 이유도 설명하지 않은 채 생년월일을 요구하겠습니까? 이메일 주소를 부르는 도중에 끼어들어 유효하지 않다고 선언하겠습니까? 우편번호에 공백이 들어갔다는 이유로 방금 들은 이야기를 전부 잊어버리겠습니까? 이 모두에는 실제로 서비스되고 있는 폼에서의 대응물이 있고, 사용자는 그것을 면접관을 대할 때와 똑같이 받아들입니다. 무례함으로 말입니다.
이 관점은 레이아웃과 스타일링에 앞서는 첫 번째이자 가장 중요한 규칙도 끌어냅니다. 모든 입력란은 묻는 데 드는 비용에 견주어 자신의 존재를 정당화해야 합니다. 질문이 하나 늘 때마다 이탈은 늘어납니다. 수집한 답은 이제 저장하고 보호하고 책임져야 하는 데이터입니다. 폼 설계에서 가장 강력한 수는 삭제입니다. 없앤 입력란은 거기에 들일 수 있었던 어떤 다듬기보다 좋은 성과를 냅니다. 널리 인용되는 근거는 Expedia의 사례입니다. 예약 폼에서 ‘Company(회사명)’라는 선택 입력란 하나를 지운 것이 연간 약 1,200만 달러의 가치가 있었다고 보고됐습니다. 고객들이 거기에 거래 은행 이름을 적었고, 그 탓에 주소 인증에 실패하고 있었던 것입니다. 정직하게 따져 물은 입력란 하나가 어떤 재설계보다 나았습니다. ‘선택’은 정당화가 되지 못합니다. 조금 덜한 강요일 뿐 여전히 강요입니다. 거래를 마치는 데 필요한 것만 묻고, 드물게 있는 선택 항목에는 선택이라고 표시하고, 나머지는 관계가 그것을 물을 자격을 얻은 뒤로 미루십시오. (반대 진영은 대신 모든 필수 항목에 별표를 붙입니다. 거의 전부가 필수라면 그 별표는 벽지에 지나지 않습니다.)
레이아웃 규칙
폼 레이아웃은 근거가 사실상 정리된, 디자인에서 몇 안 되는 영역입니다. 그래서 여기서 벗어나는 것은 사용자를 거스르는 선택이 됩니다.
한 열로 놓습니다. 폼은 질문의 연속이고, 단일 열은 그 순서를 모호하지 않게 만듭니다. 답하고, 아래로 내려가고, 끝냅니다. 다중 열 레이아웃은 행마다 읽는 순서를 판단하게 만듭니다. 옆으로인가, 아래로인가. 사용자는 이 판단을 일관되게 내리지 못하고, 결국 보지도 못한 입력란을 건너뜁니다. 가장 잘 알려진 시선 추적 비교에서는 같은 항목을 단일 열에 놓았을 때가 두 열로 나눴을 때보다 약 15초 빨리 끝났습니다. 예외는 하나의 답으로 읽히는 진짜 복합 항목입니다. 한 줄에 놓인 시/도/우편번호, 세 부분으로 나뉜 날짜가 그렇습니다. 이것은 입력란 세 개를 걸친 하나의 질문이지 세 개의 질문이 아닙니다.
레이블은 입력란 위에, 언제나 보이게. 입력란 옆에 붙인 레이블은 시선 이동을 들쭉날쭉하게 만듭니다. 입력란 안에 넣은 레이블, 즉 플레이스홀더를 레이블로 쓰는 방식은 사용자가 타이핑을 시작하는 순간 사라집니다. 하필 그것이 필요해지는 바로 그 순간에 말입니다. 긴 폼의 중간쯤 가면 채워진 입력란은 모두 ‘이게 뭐였더라’ 싶은 정체불명의 상자가 됩니다. 애니메이션으로 절충한 플로팅 레이블은 포커스 이후에도 살아남지만 캡션 크기로 줄어들고, 빈 입력란이 채워진 것처럼 보이게 만듭니다. 같은 거래의 조금 부드러운 판본이며, 치르는 대가는 여전히 이해도입니다. 플레이스홀더는 형식 힌트(‘[email protected]’)를 위한 것이지 질문 자체를 위한 것이 아닙니다.
필드 너비는 정보입니다. 주소 입력란만큼 넓은 우편번호 입력란은 답의 모양에 대해 거짓말을 합니다. 입력란을 예상되는 내용에 맞춰 우편번호는 짧게, 주소는 길게 재는 일은 간격으로 묶음을 표현하는 일과 같은 기술입니다. 기하가 조용히 의사소통을 하고 있는 것입니다.
주제별로 묶고, 묶는 일은 여백에 맡기십시오. 연락처, 배송, 결제. 관련된 질문의 덩어리들 사이에 분명한 이음매를 두되, 그 이음매는 상자나 선이 아니라 여백으로 만듭니다. 세 개의 작은 주제로 읽히는 폼은 같은 항목을 구분 없는 한 덩어리로 늘어놓은 폼보다 심리적으로 작게 느껴집니다.
입력 규칙
입력 설계의 주제는 한 문장입니다. 정규화는 소프트웨어의 일입니다. 형식을 맞추는 부담이 무엇이든 그것은 기계가 집니다. 그 형식에 신경 쓰는 쪽이 기계이기 때문입니다.
- 엉성한 답을 받아들이십시오. 공백은 잘라 냅니다. 자동 완성된 이메일 주소 끝에 붙은 공백 하나는 그럴 자격도 없이 수많은 로그인을 실패시켜 왔습니다. 전화번호는 하이픈, 점, 공백, 괄호가 있든 아무것도 없든 받습니다. 카드 번호는 띄어쓰기가 있어도 없어도 받습니다. 파싱할 수 있으면 파싱하십시오.
5558675309를 원했다는 이유로555 867 5309를 거부하는 것은, 여러분의 문자열 포매팅 코드를 사용자가 손으로 대신 실행하게 만드는 일입니다. - 사용자가 하나라고 여기는 것을 쪼개지 마십시오. 상자 세 개로 나눈 전화번호, 드롭다운 세 개로 나눈 날짜, 직접 구현한 포커스 이동이 붙은 한 글자짜리 칸 여섯 개의 인증 코드. 하나같이 머릿속의 답 하나를 내비게이션 퍼즐로 바꿔 놓고, 대개 붙여넣기를 망가뜨립니다. 붙여넣기는 사용자가 가진 가장 효율적인 입력 수단인데도 말입니다. (레이아웃 규칙의 복합 항목 예외는 그대로 유효합니다. 날짜를 직접 입력하는 세 부분으로 나누는 것은 괜찮습니다. 죄는 드롭다운이라는 절차와 빼앗기는 포커스이지 나란히 놓인 것 자체가 아닙니다. 코드의 경우 오래가는 답은
autocomplete="one-time-code"를 붙인 입력란 하나입니다.) - 알맞은 키보드를 불러오십시오. 터치 기기에서
type="email",inputmode="numeric"같은 지정은 그 용도로 설계된 키보드로 주소를 입력하느냐, 기호 레이어를 헤치며@를 찾아다니느냐를 가릅니다. 비용은 속성 하나입니다. - 브라우저가 돕게 하십시오. 올바른
autocomplete토큰은 열두 칸짜리 결제 과정을 재방문 사용자에게 두 번의 탭으로 줄여 줍니다. 주소와 결제 입력란에서 자동 완성을 끄는 것은 대개 실제로는 열린 적 없는 보안 검토에서 물려받은 미신이며, 폼에서 얻을 수 있는 가장 큰 단일 속도 향상을 내다 버리는 행위입니다. - 손가락이 닿는 곳에서 받으십시오. 입력란과 그 버튼, 그 밖에 탭할 수 있는 모든 것은 플랫폼의 최소 터치 영역을 지킵니다. iOS는 44pt, Android는 48dp입니다. 엄지로 안정적으로 누를 수 없는 작고 우아한 입력란은 휴대폰 옷을 입은 데스크톱 폼일 뿐입니다.
키보드와 자동 완성 규칙을 합쳐도 속성 몇 개면 끝납니다.
<input type="tel" autocomplete="tel"> <!-- phone keypad, autofilled -->
<input inputmode="numeric" autocomplete="one-time-code"> <!-- digit pad, code autofills -->
검증 규칙
폼이 가장 자주 적대적으로 구는 지점이 검증 타이밍이며, 규칙은 단순합니다. 사용자가 하나의 생각을 끝냈을 때 반응합니다. 키를 누를 때마다 검증을 돌리는 것은 이메일을 네 글자쯤 입력한 사람에게 “잘못된 이메일입니다!”라고 소리치는 일이고, 문장이 끝나기도 전에 그 문장을 비평하는 일입니다. 반대편 학파는 전송할 때만 검증합니다. 여기에는 진지한 옹호자가 있습니다. GOV.UK의 디자인 시스템이 정확히 이 방식을 쓰며, 전송과 함께 페이지 상단에 오류 요약을 배치합니다. 요약은 스크린 리더가 읽어 줄 수 있고, 키보드 사용자에게는 수정을 시작할 지점을 한 곳으로 모아 주기 때문입니다. 접근성이 결정적 제약인 서비스에 맞춰 조정된, 일관된 입장입니다. 그럼에도 대부분의 제품 폼에 대해서는 저는 blur 쪽을 지지합니다. 사용자가 입력란을 마치고 다음으로 넘어가는 지점, 생각과 생각 사이의 이음매에서, 아직 생각이 식지 않았을 때 피드백이 돌아오기 때문입니다. (한 가지 보완이 있습니다. 이미 무효로 표시된 입력란은 키 입력마다 다시 검증해도 좋습니다. 그래야 수정이 들어간 즉시 붉은 상태가 사라지고, 한 칸씩 늦게 따라오지 않습니다.)
오류 문장 쓰기도 같은 대화 시험을 통과해야 합니다. 오류는 판결이 아니라 수리 지시입니다. ‘잘못된 입력입니다’는 시험에 떨어집니다. 무엇이 어떻게 잘못됐다는 말입니까? ‘이 이메일 주소에는 @가 없습니다’는 통과합니다. (그리고 그 지시는 실제 규칙에 비추어 참이어야 합니다. 카드 번호는 12자리에서 19자리까지 정당하게 존재하므로 ‘16자리여야 합니다’는 오류 메시지가 아니라 Amex 사용자를 전부 걷어내는 검증 버그입니다.) 메시지는 해당 입력란 옆에 텍스트로 놓습니다. 색만으로는 색각 이상 사용자를 배제하고, 텍스트는 아무것도 렌더링하지 않는 독자가 볼 수 있는 유일한 통로이기 때문입니다. 그리고 aria-describedby로 입력란과 프로그램적으로 묶습니다. 그래야 보조 기술이 오류를 해당 입력란과 함께 읽어 주고, 화면 위에 홀로 남겨 두지 않습니다. 어조는 사실에 충실하게 유지하십시오. 폼의 임무는 사용자를 끝까지 통과시키는 것이지 잘못을 가리는 것이 아닙니다. 규칙 전체는 이 한 쌍으로 드러납니다.
<!-- before: a verdict, visually nearby, programmatically stranded -->
<label for="email">Email</label>
<input id="email" type="email">
<span class="error">Invalid input</span>
<!-- after: a repair instruction, announced with its field -->
<label for="email">Email</label>
<input id="email" type="email"
aria-invalid="true" aria-describedby="email-err">
<span id="email-err">This email address is missing its @</span>
구조에 관한 두 가지 규칙이 이 묶음을 마무리합니다. 검증 전략으로 전송 버튼을 비활성화하지 마십시오. 설명 없는 죽은 버튼은 퍼즐이고, 사용자의 다음 수는 떠나는 것입니다. 전송하게 두고, 무엇을 손봐야 하는지 정확히 보여 주십시오. (이중 결제를 막으려고 전송이 진행되는 동안 비활성화하는 것은 다른 이야기입니다. 그것은 판단이 아니라 상태입니다.) 그리고 이 정석에서 가장 깊은 법칙이 있습니다. 전송이 실패해도 사용자가 친 모든 글자는 그대로 남습니다. 오류가 났다고 내용을 비우는 폼은 한 사람이 몇 분에 걸쳐 들인 노력을 본인 눈앞에서 태워 버린 것입니다. 어떤 시각적 다듬기로도 거기서 회복되지 않습니다.
체크리스트로서의 정석
위의 모든 것을 실무에 쓸 수 있게 정리하면 다음과 같습니다. 어떤 폼이든 출시 전에 대어 보십시오.
| 규칙 | 막아 주는 위반 |
|---|---|
| 모든 입력란을 정당화하거나 삭제한다 | 호기심으로 던진 질문이 사들인 이탈 |
| 한 열, 복합 항목만 예외 | 읽는 순서가 모호해 생기는 입력 누락 |
| 레이블은 위에, 언제나 보이게 | 폼 중간의 정체불명 상자, 레이블 대신 쓴 플레이스홀더 |
| 필드 너비가 답의 모양과 맞는다 | 예상 입력에 대해 기하가 하는 거짓말 |
| 주제별로 묶고 이음매는 여백으로 | 구분 없는 한 덩어리 |
| 파싱 가능한 형식은 모두 받는다 | 포매팅 코드를 사용자가 손으로 대신 실행하는 일 |
| 답 하나에 입력란 하나(복합 항목은 예외) | 망가진 붙여넣기, 포커스 이동 퍼즐 |
| 알맞은 키보드와 autocomplete | 터치에서의 기호 찾기, 두 번이면 될 열두 번의 탭 |
| 플랫폼 터치 영역(44pt/48dp) | 엄지가 닿지 않는 우아한 입력란 |
| blur에서 검증하고, 오류는 입력란 옆에서 가르치며 aria로 묶는다 | 타이핑 도중의 꾸중, 보조 기술에서 떨어져 나온 오류 |
| 검증을 이유로 전송을 비활성화하지 않는다 | 죽은 버튼 퍼즐 |
| 입력은 실패에서 살아남는다 | 비워진 폼, 그리고 다시 오지 않는 사용자 |
디자인 시스템 안에서 이 규칙들은 폼 컴포넌트 자체로 굳습니다. 레이블 슬롯을 위에, 오류 슬롯을 아래에 두고 검증 타이밍을 내장한 채로 출고되는 텍스트 필드처럼 말입니다. 그러면 정석은 기본값으로 지켜지고, 거기서 벗어나는 쪽에 노력이 듭니다. 이것은 모션 토큰과 똑같은 시스템화 논리입니다. 결정을 한 번만 부호화하든지, 아니면 기능마다 다시 다투든지 둘 중 하나입니다.
자주 묻는 질문
폼은 한 열이어야 합니까, 두 열이어야 합니까?
한 열입니다. 단일 열은 답하는 순서를 모호하지 않게 만들고 완료 시간도 측정 가능한 수준으로 단축합니다. 다중 열 레이아웃에서는 사용자가 읽는 순서를 제각각 해석하기 때문에 입력을 빠뜨립니다. 예외는 시/도/우편번호 같은 복합적인 답이며, 이는 나란히 놓인 입력란으로 표현된 하나의 질문입니다.
폼 검증은 언제 실행해야 합니까?
blur 시점, 즉 사용자가 입력란을 떠날 때입니다. 키 입력마다 실행하지 않고, 대부분의 제품 폼에서는 전송할 때까지 모아 두지도 않습니다. 키 입력 검증은 아직 끝나지 않은 답을 비평합니다. 전송 시에만 하는 검증은 모든 실패를 한꺼번에 들이밉니다. 다만 스크린 리더 안내가 결정적 제약인 곳에서는 GOV.UK의 전송 시 오류 요약이 옳은 선택입니다. 이미 무효로 표시된 입력란은 키 입력마다 다시 확인해도 좋습니다. 그래야 고치는 즉시 오류가 사라집니다.
플레이스홀더 텍스트를 입력란 레이블로 써도 됩니까?
아닙니다. 레이블 대신 쓴 플레이스홀더는 사용자가 타이핑을 시작하는 순간, 다시 말해 질문을 떠올려야 하는 바로 그 순간에 사라지고, 검토할 때는 채워진 입력란을 전부 이름 없는 데이터로 만들어 버립니다. 레이블은 입력란 위에 보이게 두고, 플레이스홀더는 형식 예시로만 쓰십시오.
폼이 유효해질 때까지 전송 버튼을 비활성화해 두어야 합니까?
아닙니다. 설명 없는 비활성 전송 버튼은 사용자가 스스로 원인을 진단해야 하는 막다른 길입니다. 계속 활성 상태로 두고, 전송 시점에 해결되지 않은 입력란마다 구체적이고 안내가 되는 오류를 그 옆에 드러내십시오. 그러면서 사용자가 이미 입력한 내용은 모두 보존해야 합니다.