대원칙 – 사용자는 관심이 없다!
폼을 입력하는 사용자는 딱 두 가지에만 관심이 있다.
- 이 지겨운 입력은 언제 끝이 나는가?
- 이거만 입력하면 정말 내가 원하는 것을 얻는가?
그밖에는 사용자가 그 어떤 것에도 관심이 없다는 사실을 기억할 필요가 있다. post_title이 required 값인지 내가 이모지를 입력할 수 있는지 없는지 내가 유효한 우편번호를 입력했는지 어떤지 사용자는 알지도 못하고 관심도 없다. 중요하니까 한 번 더 말한다. 사용자는 폼을 입력할 때 기술적인 부분은 전혀 관심 갖지 않는다!!!
이 사실을 순순히 받아들이는 것이 생산성에 도움이 된다. 나도 “아니 왜 사람들은 내가 기껏 다 도움말 부분에 써놓은 걸 안 읽고 지들 멋대로 행동해 놓고서 나한테만 지랄이야” 했는데, 어느 날인가 생각을 바꿔서 “그래 내가 여기서 경고를 쓰고 안내를 쓰고 백날 해봐야 어차피 뻑날 건 또 뻑난다” 하고 현실을 받아들이자 일이 좀더 편해졌다.
기왕 들이닥칠 사용자 불편이라면 좀더 편안한 마음으로 처리하거나 심지어는 예측하고 선대응하고 싶지 않은가? 그래서 “휴먼 에러와의 싸움” 시리즈를 준비했다. 내가 그간 (그리고 지금) 웹애플리케이션 개발 작업을 하다가 “아 맞아 이런 거 있었지” 했던 부분들을 까먹지 않으려고 만든 치트시트이므로, 필요하다 싶은 부분은 체크하시고, 관심 없으면 밑의 ‘추천’만 누르고 지나가시기 바란다.
입력: 인코딩과 위생 처리는 하고 있는가?
문자열의 sanitizing은 웹개발 영역에서 (특히 한국 웹이라면) 한 번씩은 만져보는 똥이다. 웬만한 인젝션 공격 방어는 대다수 웹프레임워크가 해주고 있으니 여기서는 인코딩이 걸릴 만한 것들을 짚어보자.
(그 전에 한 가지만: 설마 아직도 EUC-KR에 미련을 못 버리고 UTF-8을 안 쓰고 있는 건 아니겠지?)
이모지를 입력할 여지가 있는가?
(원래 지금 인터넷 세상은 당연히 UTF-8을 써야 하지만 특히) 사용자가 이모지를 입력할 것 같은 필드는 무조건 UTF-8로 값을 넘겨야 하고, 그걸 저장하는 칼럼은 utf8mb4_* 형식의 콜레이션을 줘야 한다. 처음부터 새로 만드는 거라면 그냥 이렇게 하면 되는데 문제는 과거 레거시가 존재할 때다. 이때는 세 가지 선택이 있다.
- 테이블을 새로 만들고 이때 콜레이션을 바꿔놓는다. 그 다음 새 테이블에 옛 테이블의 데이터를 붓는다.
- 그냥 기존 테이블의 콜레이션을 과감하게 변경한다. 경험상, 평범한 사람말이 채워져 있던 자료들이라면 콜레이션만 utf8에서 utf8mb4로 올리는 것은 별문제 없음.
- 아니면… 대책없는 각종 이모지 처리 플러그인을 도입한 다음 문제가 있을 때마다 붙잡고 싸운다 😂😂😂
위지윅 에디터의 문제
네이버 블로그에게 길들여진 한국인들은 편집기에 관해 다음과 같은 믿음이 있다.
글자를 썼으면 당연히 글자색과 배경색을 입힐 수 있어야지, 그렇게 안 하면 어떻게 글자를 강조해? em태그를 쓰라고? 그게 뭔데?
h1, h2 태그란 게 있어? 몰라 내 알 바 아님. 제목이 필요하다면 36pt짜리 굵은 글씨를 만들면 될 뿐이야.
당신은 이 믿음과 싸우고 싶은가? 위지윅 에디터는 이 질문을 놓고 고르면 된다.
- 싸우고 싶지 않다면(즉, DB에 span style=”어쩌구”를 떡칠해도 괜찮다면) 네이버 Smart Editor를 써주자. 이 편집기가 문제를 일으킬 때는 사람들은 당신에게 따지지 않고 그냥 꾹 참을 것이다.
- 어느 정도 싸우고 싶다면(즉, 좀더 말이 되는 마크업을 쓰고 싶다면) CKEditor나 TinyMCE의 최신 버전이 적당하다. 사람들이 익숙해한다고 해서, 기존 소스가 그렇다고 해서 과거 버전을 계속 써서는 안 된다.
- 싸울 필요가 없다면(즉, 기술 친화적이고 탈조선적이어도 괜찮다면) Summernote를 추천한다. 웬만한 기능은 플러그인 하나 구워서 막 만들어넣을 수 있다.
입력: 기본값은 충분히 주어져 있는가?
몇 번을 강조해도 지나치지 않은 게 기본값이다. 가급적 프론트에서 미리 만들어 제공하도록 하고, 못해도 DB 저장 직전에는 뭔가 만들어야 한다.
주소와 “인원수”의 문제
지역 기반 커뮤니티를 만들 때 그 지역의 주소가 입력될 것이라고 기대되는 경우가 있다. 이때 사람들은 절대 전체 주소를 넣지 않고 상세주소부터만 적는다. 물론 우편번호 입력기를 이용해서 주소를 받는다면 상관 없겠지만 예컨대 단순 문자열을 구글이 처리 가능한 주소로 바꾸려면 반드시 적당한 전체 주소 변환을 해줘야 한다.
비슷하게, ‘인원수’를 입력할 때가 있다. ‘아동’의 수는 0이 될 수 있을지 몰라도 설마 ‘성인’의 인원수까지 0일 리는 없으므로, 인원수를 입력받는 폼은 처음 뜰 때부터 아예 기본값을 그 최소값인 1로 갖고 있는 편이 좋다.
빈 입력의 문제
내 경험에 사람들은 폼 중간쯤의 큰 입력칸(=textarea)과 최하단의 파란 버튼(=submit) 사이에 무엇이 있어도 절대 알아채지 못한다. 따라서 여기 위치한 입력칸들은 사용자가 빈 입력을 넣는 실수를 반드시 저지른다고 보아야 한다. 이런 입력칸들에 대한 기본값/폼검증 처리를 특히 더 유념해야 하는 이유다.
입력: 포맷팅은 충분히 따르기 쉬운가?
input type을 “email”이나 “number”, “url”로 주면 이런 건 대부분의 모던 브라우저들이 알아서 format validation을 해 준다. 하지만 날짜와 같이 특정 포맷이 필요한데 아직 공식 지원은 없는 타입들은 어떨까? 뾰족한 수가 없다.
날짜의 문제
날짜(+시간) 입력은 헛짓거리 하지 말고 순순히 외부 라이브러리의 도움을 받도록 하자.
- 사용자가 선택할 수 있는 날짜와 시간의 범위를 (타임존과 일광절약까지 고려해서) 도출해 입력폼에 넣을 수 있게 해 주는 UI라는 건 매우 잘 만들어진 몇 가지의 바퀴와도 같다. 당신이 그걸 또 발명해서는 안 된다.
- 일부 브라우저는 date type의 input에 대한 입력 UI를 제공한다. 하지만 어떤 브라우저는 그렇지 않다. 다소 불편하고 bulky해 보이더라도, 어느 브라우저에서나 똑같은 모양으로 뿌려지는 일시 선택 UI를 제공하는 편이 불평 불만을 훨씬 줄여준다.
- 문자열을 파싱(PHP의 strtotime() 등)해서 어떻게 때려맞추면 될 것도 같지만 이것은 본질적으로 취약하고 부족하다. 예컨대 사용자가 datetime 포맷을 따라야 하는 칸에 date만 입력했다면 이것은 무슨 의미인가? 사용자가 실수로 ‘2018-02-29’를 입력했을 때 이 함수가 떨굴 exception을 사용자에게 뭐라고 알려줘야 할 것인가? “당신은 올해가 윤년이 아니었다는 걸 몰랐나 봅니다?”
검증: 입력값 검증 결과가 너무 늦게 돌아오지는 않는가?
검증이 언제 이루어져야 하는가의 문제는 실무 웹개발자들을 외롭게 하는 주제 중 하나다. 나로서는 현재 대원칙 하나만을 체득한 상태인데, 최소한 그 결과를 너무 늦게 알려주지는 말아야 한다는 것이다. 예컨대 비밀번호가 유효하지 않을 경우 그 사실은 가입 버튼을 누르기 전에 통지되어야 한다. 사람들은 폼을 제출하는 순간 비밀번호 필드의 값을 까먹기 때문이다.
“플래시 데이터”는 좋은 생각이 아니다
2000년대까지만 해도 모든 폼은 제출된 순간에만 뭔가를 할 수 있었으므로, 그 시대의 프레임워크들은 폼 검증 후 원래 제출값을 그대로 다시 돌려주기 위해 플래시 데이터라는 궁여지책을 만들어냈다. 이것이야말로 안 쓸 수 있다면 쓰지 말아야 할 대표적인 코딩 역사의 유산 중 하나다. 왜냐하면 이게 사용자 입장에서는 폼 작성이 끝난 줄 알았는데 안 끝났다고 우기는 생트집이거든.
기술적으로도 이 직관이 맞는 게, 플래시 데이터를 이용해 폼을 다시 뿌리는 작업은 엄밀히 말하자면 어딘가로 이동을 한 것이기 때문이다. 사용자는 분명 빈 화면을 보면서 “앗 이게 어딘가로 이동하고 있구나 = 폼 작성이 드디어 끝났구나” 하는 기대를 품는데 그 끝에 나오는 내용이 아까 그 폼 그대로라면, 이건 사용자의 기대를 저버리는 일이며 순 헛걸음에 불과하게 된다.
그런 의미에서, 폼이 맥락적으로 아예 다른 페이지로 넘어가야만 하는 (신규 가입과 같은) 경우가 아니라면, 특정 필드를 검증하든 폼 전체를 제출하든 가급적 AJAX를 사용하자. 심지어 폼 제출 자체를 처리할 때도 AJAX는 기존 입력값을 따로 어떻게 할 필요가 없는 반면 전통적인 액션 방식은 각종 세션 지랄 끝에 그 값을 살려서 다시 넘겨야 하는데, 일부러 그렇게 복잡하게 살 이유가 있을까.
제출: submit()은 언제 실행되는가?
서브밋 이벤트가 문제가 된다고? 놀랍게도 문제가 된다.
어떤 폼의 맨 끝에 그냥 붙어 있는 input type=button
↑ 이런 게 있다고 하면, 이걸 누르면 무슨 일이 일어날까? 이론상 아무 일도 일어나지 말아야 한다. 근데 어떤 브라우저들은… 저 버튼을… 누가 시키지도 않았는데… submit 버튼으로 사용한다!!! 왜? 왜?? 모르겠다. 아마 브라우저 개발자들도 엉망진창으로 만들어진 입력폼과 사람들의 불평불만에 넌더리가 났나 보지. 어쩔 수 없다. So they got laundry about it and we gotta deal with it.
엔터 또는 휴대폰 키보드 “전송” 키의 문제
아무 처리도 되어 있지 않은 제목 input과 본문 textarea가 있다고 치자. 사용자가 그 input을 눌러 입력을 하고 엔터를 치면 무슨 일이 일어날까? 폼 전송이 되어 버린다! 휴대폰 키보드에서는 이 경우 아예 엔터키 표시가 “전송”이라고 뜰 것이다!
물론 그건 당신 잘못이 아니고 이건 너무나 부조리한 일이다. 하지만 우리는 사용자와 싸울 이유가 전혀 없으므로, 각종 필드에 required=”required” 속성을 주고 13번 키가 눌렸을 때의 처리를 넣고 폼전송시 e.preventDefault()를 호출하는 등의 안전 울타리를 쳐주는 것이 더 나은 선택이라 할 것이다.
너무 빨리 전송하지 말 것
너무 빨리 전송되어도 문제가 된다고? 놀랍게도 문제가 된다. 정확히는, 사용자에게 한 번 뭔가를 물어보고(컨펌받고) 지나가야 하는 종류의 폼이 존재하기 때문에 그런 폼에 대해서 그 단계를 생략하면 문제가 된다. 물론 모든 폼입력에 대해서 컨펌 프롬프트를 띄우는 것은 옳지 않다. 하지만 때로는, 기획자도 디자이너도 사용자도 누구도 필요하지 않을 거라고 생각하는, 그러나 인류에 대한 믿음 부족 때문에라도 분명히 필요할 게 뻔한 컨펌이 존재한다는 사실을, 개발자들은 잊으면 안 된다.
제출 과정: 필요한 값이 (임시)저장되는가?
사용자들의 인터넷이 너무너무 느려서 요청-응답 통신 자체가 중간에 실패할 수 있다는 여지를 항상 두어야 한다. 이 주제 하나만 가지고 이 시리즈 한 편을 더 쓸 수 있을 정도다. 여기서는 폼에 관해서만 논하자면…
공들여서 쓸 만한 텍스트는 닥치고 저장하기
제목 그대로다. 사람들이 공들여서 오랜 시간 동안 쓸 것 같은 textarea는 무조건 임시 저장 루틴을 구현해 놓도록 하자. 이 구현은 복잡하게 하자면 한없이 복잡해지기는 하지만, 폼 전송간 불의의 데이터 손실 상황을 불필요하게 처벌하지 않으려면 임시 보관 개념 내지 보관함 개념을 고려할 필요가 있다. 예를 들자면 자기 로그인 세션이 만료되는 줄도 모르고 뭔가를 쓴 다음 제출을 하는 경우…가 어디 있냐고? 믿기 힘들겠지만 누군가의 현실이다!!!
제2탄 예고
제 2탄은 파일을 다룰 생각이다. 생성, 수정, 업로드/다운로드 등 대환장 주제가 한둘이 아니다. ㅋㅋ