지난 편에서 폼의 뼈대를 세우고 알맞은 입력 타입을 고르는 법을 익혔습니다. 이번에는 한 걸음 더 나아가, 사용자가 넣은 값이 우리가 원하는 규칙에 맞는지 확인하는 유효성 검사를 다룹니다. 아무리 입력창을 잘 만들어도 사용자는 빈칸을 그냥 제출하거나, 형식에 맞지 않는 값을 넣거나, 너무 짧거나 긴 값을 넣기 마련입니다. 이런 실수를 미리 걸러 내는 장치가 유효성 검사입니다.
제가 초보 시절에는 모든 검증을 서버에 도착한 뒤에야 처리했습니다. 그러다 보니 사용자는 폼을 다 채워 제출한 뒤에야 무엇이 틀렸는지 알게 되어, 다시 처음으로 돌아가는 불편을 겪었습니다. 브라우저가 제공하는 검증 속성을 알고 나서는, 사용자가 제출 버튼을 누르는 순간 그 자리에서 바로 오류를 알려 줄 수 있게 되었습니다. 이번 편에서는 이 속성들을 하나씩 살펴보겠습니다.
1. 왜 브라우저 검증부터인가
먼저 분명히 해 둘 것이 있습니다. 브라우저가 해 주는 검증은 편의를 위한 1차 관문이지, 보안을 위한 최종 방어선이 아닙니다. 브라우저 검증은 사용자가 실수로 잘못된 값을 보내는 것을 막아 주지만, 악의를 가진 사람은 브라우저를 거치지 않고 서버에 직접 값을 보낼 수 있습니다. 그래서 서버 검증은 언제나 별도로 반드시 있어야 합니다.
그렇다면 브라우저 검증은 왜 필요할까요. 대다수의 평범한 사용자에게 즉각적인 피드백을 주기 위해서입니다. 값을 서버로 보내고 응답을 기다리는 왕복 없이, 그 자리에서 바로 이 칸을 채워 주세요, 형식이 맞지 않습니다 같은 안내가 뜨면 사용자는 훨씬 빠르게 문제를 고칩니다. 서버 왕복이 줄어드니 서버 부하도 덜립니다.
브라우저 검증의 큰 장점은 우리가 자바스크립트를 거의 짜지 않아도 된다는 점입니다. 몇 가지 속성만 입력창에 붙이면 브라우저가 알아서 값을 검사하고, 표준적인 오류 안내를 띄우고, 제출을 막아 줍니다. 직접 검증 코드를 짜면 실수가 끼어들 여지가 많은데, 브라우저에 맡기면 그런 위험이 사라집니다.
다만 브라우저가 띄우는 기본 오류 안내는 모양과 문구를 우리가 마음대로 바꾸기 어렵습니다. 디자인을 세밀하게 통제해야 하는 경우에는 기본 검증 안내를 끄고, 우리가 직접 오류 메시지를 그려야 할 때도 있습니다. 그럴 때도 검증 규칙 자체는 속성으로 선언해 두고, 표시만 우리가 맡는 방식이 깔끔합니다.
제가 실무에서 정착한 방식은, 검증 규칙은 속성으로 선언하고 서버에서도 같은 규칙을 다시 검사하는 이중 구조입니다. 브라우저는 대다수 사용자에게 빠른 피드백을 주고, 서버는 모든 값을 최종적으로 지킵니다. 이 두 겹이 있어야 사용성과 안전을 동시에 잡을 수 있습니다.
정리하면 브라우저 검증은 사용자 편의를 위한 앞단이고, 서버 검증은 안전을 위한 뒷단입니다. 둘 중 하나만 있으면 반쪽짜리입니다. 앞으로 소개할 속성들은 모두 이 앞단을 손쉽게 만들어 주는 도구라고 이해하시면 됩니다.
2. 필수 입력 표시
가장 기본적인 검증은 이 칸을 반드시 채워야 한다는 표시입니다. 이 표시를 붙인 입력창을 비운 채로 제출하려 하면, 브라우저가 제출을 막고 이 칸을 채워 달라고 안내합니다. 회원가입에서 이메일이나 비밀번호처럼 없으면 안 되는 값에 이 표시를 다는 것이 첫걸음입니다.
이 필수 표시는 눈으로 보는 사용자에게도 신호를 주는 것이 좋습니다. 다만 필수 표시 속성 자체는 화면에 아무 모양도 만들지 않으므로, 이 칸이 필수라는 것을 사용자가 알도록 이름표 옆에 별표나 필수라는 글자를 따로 보여 주어야 합니다. 속성만 붙이고 시각 표시를 빠뜨리면 사용자는 왜 제출이 안 되는지 몰라 당황합니다.
주의할 점은 동그라미 선택 항목 묶음에 필수 표시를 달 때입니다. 같은 이름으로 묶인 선택지 중 하나에만 이 표시를 달아도, 그 무리 전체에서 하나는 반드시 골라야 하는 것으로 동작합니다. 반면 여럿을 고르는 네모 선택 항목은 각각 독립적이라, 필수 표시를 단 항목은 그 항목 자체를 반드시 켜야 합니다. 약관 필수 동의가 대표적인 예입니다.
필수 표시를 남발하는 것도 경계해야 합니다. 정말 없으면 안 되는 값에만 필수를 걸고, 있으면 좋지만 없어도 되는 값은 선택으로 두는 것이 사용자를 존중하는 태도입니다. 불필요하게 많은 필드를 강제하면 사용자는 폼 채우기를 중도에 포기합니다. 저는 필수 항목의 수를 최소로 줄이는 것을 늘 고민합니다.
제가 겪은 사례로, 회원가입 폼에서 선택해도 되는 항목까지 필수로 걸어 두었다가 가입 이탈이 높았던 적이 있습니다. 정말 필요한 항목만 필수로 남기고 나머지를 선택으로 풀자 이탈이 눈에 띄게 줄었습니다. 필수는 사용자에게 부담이므로, 최소한으로 쓰는 것이 좋다는 교훈을 얻었습니다.
아래는 필수 표시를 붙인 입력창의 예시입니다. 이름표에 시각적 필수 표시도 함께 둔 점을 보세요.
<label for="email">이메일 <span>(필수)</span></label>
<input type="email" id="email" name="email" required>
3. 길이와 범위 제약
값의 형식뿐 아니라 크기도 검사할 수 있습니다. 글자 수에는 최소 길이와 최대 길이를 정할 수 있어서, 예를 들어 비밀번호는 여덟 글자 이상이어야 한다거나, 별명은 스무 글자를 넘지 못한다는 규칙을 속성으로 걸 수 있습니다. 사용자가 이 범위를 벗어나면 브라우저가 제출을 막고 몇 글자여야 하는지 안내합니다.
숫자를 받는 입력창에는 최솟값과 최댓값을 정할 수 있습니다. 나이는 영보다 커야 하고 백오십을 넘지 못한다거나, 수량은 하나 이상 백 이하여야 한다는 식입니다. 여기에 증가 단위를 정하면 사용자가 위아래 화살표로 값을 조절할 때 그 단위만큼 오르내립니다. 소수점이 필요한 값에는 이 단위를 소수로 정하면 됩니다.
날짜를 받는 입력창에도 같은 최솟값과 최댓값 개념이 적용됩니다. 예약 날짜는 오늘 이후여야 한다거나, 생년월일은 오늘을 넘을 수 없다는 제약을 걸 수 있습니다. 이렇게 하면 달력에서 아예 선택 불가능한 날짜가 흐리게 표시되어, 사용자가 잘못된 날짜를 고를 여지 자체가 사라집니다.
여기서 중요한 점은, 최대 길이 제약은 아예 그 이상 타이핑되지 않게 막는 반면, 최소 길이 제약은 제출할 때 검사한다는 차이입니다. 이 차이를 모르면 왜 어떤 제약은 입력을 막고 어떤 제약은 제출을 막는지 헷갈립니다. 동작 방식을 정확히 이해하고 써야 사용자에게 일관된 경험을 줄 수 있습니다.
제가 실무에서 얻은 요령은, 길이나 범위 제약을 걸 때 그 규칙을 사용자에게 미리 보여 주라는 것입니다. 비밀번호 칸 아래에 여덟 글자 이상이라고 안내 문구를 두면, 사용자는 제출 전에 규칙을 알고 맞춰 입력합니다. 제약만 걸고 안내를 빠뜨리면 사용자는 제출해 보고 나서야 규칙을 알게 되어 답답해합니다.
아래는 길이와 범위 제약을 건 예시입니다. 최소 길이와 숫자 범위가 어떻게 지정되는지 보세요.
<input type="password" name="pw" minlength="8" maxlength="64">
<input type="number" name="qty" min="1" max="100" step="1">
4. 형식 검사와 정규식
타입만으로는 검사할 수 없는 세밀한 형식이 있습니다. 예를 들어 별명은 영문과 숫자만 허용하고 특수문자는 막고 싶다거나, 우편번호는 정확히 다섯 자리 숫자여야 한다는 규칙은 특정 타입이 아니라 형식 패턴으로 정의해야 합니다. 이럴 때 쓰는 것이 패턴 속성으로, 값이 따라야 할 규칙을 정규식이라는 형식으로 적어 둡니다.
정규식은 글자의 종류와 개수, 순서를 기술하는 일종의 문법입니다. 숫자만, 영문자만, 또는 이들의 조합을 몇 글자에서 몇 글자까지 허용한다는 식으로 규칙을 촘촘하게 정할 수 있습니다. 사용자가 이 패턴에 맞지 않는 값을 제출하려 하면 브라우저가 막아 줍니다. 강력한 도구지만 규칙이 복잡해지면 읽기 어려워지니 적당히 쓰는 것이 좋습니다.
패턴 속성을 쓸 때 반드시 함께 챙겨야 할 것이 있습니다. 이 값이 어떤 형식이어야 하는지를 사람 말로 설명하는 안내입니다. 패턴만 걸어 두면 브라우저가 형식이 맞지 않는다고만 말할 뿐, 어떤 형식이어야 하는지는 알려 주지 않아 사용자가 막막해합니다. 그래서 저는 패턴을 걸 때마다 반드시 설명 문구를 함께 답니다.
정규식을 쓸 때 흔한 함정은 지나치게 엄격하게 만드는 것입니다. 예를 들어 이메일이나 전화번호를 정규식으로 완벽하게 검사하려다 보면, 실제로는 유효한데도 우리 패턴에 맞지 않아 거부되는 값이 생깁니다. 세상에는 우리가 예상한 것보다 훨씬 다양한 형식이 존재하므로, 형식 검사는 너무 조이지 않는 편이 안전합니다.
제가 배운 교훈은, 정말 형식이 엄격히 정해진 값에만 패턴을 쓰라는 것입니다. 우편번호나 사업자 번호처럼 자릿수와 형식이 확실한 값은 패턴이 잘 맞습니다. 반면 이름이나 주소처럼 형식이 자유로운 값에 패턴을 걸면 오히려 정상적인 사용자를 막게 됩니다. 형식이 확실한 곳에만 쓰는 절제가 필요합니다.
아래는 패턴과 설명을 함께 단 예시입니다. 다섯 자리 숫자만 허용하는 규칙과 그 설명을 보세요.
<input type="text" name="zip" pattern="[0-9]{5}" title="숫자 5자리를 입력하세요">
5. 검증 실패를 접근성 있게 알리기
검증에서 자주 놓치는 마지막 조각이 있습니다. 값이 규칙에 맞지 않을 때 그 사실을 소리로 듣는 사용자에게도 제대로 전하는 것입니다. 화면에 빨간 글씨로 오류를 표시해도, 그 표시가 입력창과 정식으로 연결되어 있지 않으면 화면 낭독 프로그램은 그것을 읽어 주지 못합니다.
이를 위해 먼저 오류가 난 입력창에 지금 이 값이 유효하지 않다는 상태 표시를 붙입니다. 그러면 화면 낭독 프로그램은 그 입력창에 초점이 갈 때 값이 잘못되었음을 안내합니다. 이 상태 표시는 오류가 해결되면 다시 정상으로 바꿔 주어야, 사용자가 문제가 풀렸다는 것을 알 수 있습니다.
다음으로 오류 메시지 자체를 입력창과 연결합니다. 오류 문구에 고유한 식별자를 주고, 입력창이 그 식별자를 설명으로 지목하게 하면, 화면 낭독 프로그램은 입력창을 읽을 때 그 오류 문구까지 함께 읽어 줍니다. 이렇게 하면 소리로 듣는 사용자도 무엇이 왜 틀렸는지 정확히 알게 됩니다.
오류 메시지의 문구도 중요합니다. 값이 올바르지 않습니다 같은 막연한 말보다, 이메일에는 골뱅이 기호가 있어야 합니다처럼 무엇을 어떻게 고쳐야 하는지 알려 주는 문구가 사용자에게 훨씬 도움이 됩니다. 저는 오류 메시지를 쓸 때 사용자가 이 문장만 읽고 바로 고칠 수 있는가를 기준으로 삼습니다.
오류가 여러 개 났을 때는 사용자를 첫 번째 오류 지점으로 데려가는 배려도 좋습니다. 제출을 눌렀는데 여러 칸이 틀렸다면, 화면을 맨 처음 틀린 칸으로 옮겨 사용자가 어디부터 고쳐야 할지 알게 하는 것입니다. 오류가 화면 밖에 있으면 사용자는 왜 제출이 안 되는지 몰라 헤매게 됩니다.
이번 편에서는 필수 표시부터 길이와 범위, 형식 패턴, 그리고 오류를 접근성 있게 알리는 법까지 폼 검증의 여러 속성을 살펴봤습니다. 다음 편에서는 화면에 소리와 영상을 담는 멀티미디어로 넘어가겠습니다. 사용자에게 정보를 입력받는 법을 익혔으니, 이제 사용자에게 풍부한 미디어를 전하는 법을 배울 차례입니다.