밑줄을 걷어낸 하루 — 필터 토글은 탭이 아니다
켜진 필터에 밑줄을 긋고 있었습니다. 밑줄은 탭의 말투인데요. 오늘은 기호를 바꾸고, 색 두 칸을 고르고, 58개 파일을 따라다닌 이야기입니다.
밑줄을 걷어낸 하루 — 필터 토글은 탭이 아니다
페칫의 목록 화면 위쪽에는 작은 아이콘 토글들이 있습니다. 별, 등록, 시작, 완료, 취소. 눌러 두면 그 조건에 맞는 것만 남는 필터죠. 오랫동안 이 토글들은 켜지면 아이콘 밑에 밑줄이 그어지는 방식이었습니다. 오늘 그 밑줄을 전부 걷어냈습니다.
1. 기호가 어긋나 있었다
밑줄은 이미 임자가 있는 기호입니다. 탭이요. 브라우저 탭, 앱의 상단 탭 — 밑줄은 “지금 보고 있는 곳”을 가리킵니다. 그리고 탭에는 두 가지 전제가 붙어 있습니다. 하나만 고를 수 있고, 반드시 하나는 골라져 있다.
필터 토글은 정확히 그 반대입니다. 별과 완료를 동시에 켤 수 있고, 전부 꺼 둘 수도 있습니다. 서로 독립인 다중 선택이에요. 배타 선택의 기호를 다중 선택에 쓰고 있었으니, 화면은 “탭 다섯 개에 밑줄이 세 개 그어진” 이상한 상태를 계속 그리고 있었던 겁니다.
기능은 멀쩡했습니다. 눌리면 눌린 대로 동작했고, 아무도 고장이라고 부르지 않았어요. 그런데 이런 종류의 어긋남은 버그 리포트로 오지 않고 “뭔가 어수선한데”라는 인상으로만 옵니다. 원인을 짚기 전까지는 손댈 데를 못 찾죠.
마침 데스크톱 표 뷰에는 이미 답이 있었습니다. 며칠 전 표의 열 필터를 손보면서 “적용 중인 필터는 버튼이 눌린 채로 남는다”는 문법을 세워 뒀거든요. 같은 개념이면 같은 문법이어야 합니다. 필터 토글도 그쪽으로 통일했습니다.
2. 밑줄이 하던 일 하나 — 트랙
그런데 밑줄에는 장점이 하나 있었습니다. 아무것도 안 켜진 상태에서도 밑줄 자리가 컨트롤의 존재를 알려 준다는 것. 눌린 버튼 방식으로 그냥 바꾸면, 전부 꺼진 화면에서는 아이콘 다섯 개가 배경 위에 둥둥 뜬 상태가 됩니다. 여기가 조작할 수 있는 곳인지가 안 보여요.
그래서 묶음을 옅은 트랙 위에 얹었습니다. 회색 알약 하나가 깔리고 그 위에 아이콘들이 앉는 형태요. 트랙은 세 가지를 한꺼번에 합니다. 아무것도 안 켜져도 컨트롤이 보이고, 흩어져 있던 아이콘들이 “하나의 컨트롤”로 읽히고, 눌린 칸이 기댈 배경이 생깁니다.
컴포넌트는 둘로 갈랐습니다. ToggleIconButton(눌리면 배경이 한 칸 어두워지는 껍데기)과 ToggleIconButtonGroup(트랙). 낱개로 서 있는 별 토글도 예외 없이 1개짜리 그룹으로 감쌌습니다 — 화면마다 “여긴 트랙이 있고 저긴 없고”가 되면 방금 만든 문법이 다시 무너지니까요.
3. 색은 두 칸 사이에서만 고를 수 있었다
여기서 시간이 제일 많이 갔습니다. 트랙과 눌린 칸은 서로 구별돼야 하는데, 쓸 수 있는 폭이 생각보다 좁았어요.
위로는 못 올라갑니다. 눌림을 한 단계 더 어둡게(NEUTRAL300) 하면 또렷해질 것 같지만, 등록 체크 아이콘과 취소 X 아이콘이 바로 그 색입니다. 배경과 아이콘이 같은 색이 되면서 아이콘이 통째로 사라집니다. 켜 두면 사라지는 필터라니, 고칠 값이 아니죠.
아래로도 못 내려갑니다. 눌림을 트랙과 같은 밝기로 두면 당연히 눌렸는지 알 수 없습니다.
남은 건 트랙과 눌림이 딱 한 칸 차이로 붙어 있는 조합 하나였습니다. 트랙은 옅게, 눌린 칸은 그보다 한 칸만 어둡게. 밝기 차이로는 16 정도, 크게 대비되지 않지만 나란히 놓이면 확실히 구분됩니다. 데스크톱 hover도 같은 이유로 표 필터의 선례(한 칸 더 어두운 색)를 따르지 못하고, 꺼진 상태에서 눌리면 어떻게 될지를 미리 보여주는 쪽으로 갔습니다.
디자인 토큰이 있는 팔레트를 쓰면 “아무 회색이나 고르면 되지”가 안 됩니다. 각 단계에 이미 다른 의미가 배정돼 있어서, 배경 하나를 고르는 일이 사실은 의미가 비어 있는 칸을 찾는 일이에요.
4. 기각한 세 가지
방향을 정하는 동안 후보가 여럿 올라왔고, 대부분 보고 나서 접었습니다.
밑줄 복귀. 익숙하다는 것 말고는 이유가 없었습니다. 익숙함은 기호가 맞을 때만 자산입니다.
토글마다 개별 테두리. 아이콘 다섯 개에 각각 얇은 테두리를 두르는 안이었는데, 화면이 즉시 시끄러워졌습니다. 테두리는 선이 다섯 배로 늘어나는 장치라서요. 원래 묶음에 둘러 두던 1px 테두리도 이번에 트랙으로 대체하며 같이 지웠습니다.
iOS 세그먼트 방향. 어두운 트랙 위에서 선택된 칸이 밝아지는 형태요. 익숙한 관용구지만, 이것 역시 배타 선택의 기호입니다. 세그먼트 컨트롤은 칸 하나만 밝아지는 물건이고, 우리는 세 칸이 동시에 밝아질 수 있습니다. 밑줄에서 도망친 이유로 그대로 돌아가는 셈이라 접었습니다.
5. 동심 라운드와, 40이 되면서 밀린 것들
작은 것 둘.
라운드는 동심으로 맞췄습니다. 버튼 모서리가 6이면 트랙 여백이 6일 때 바깥 모서리는 12여야 두 곡선이 나란히 흐릅니다. 안팎 라운드가 같은 값이면 여백만큼 어긋나 보여요. 모바일은 여백 6에 바깥 12, 데스크톱은 4에 10입니다.
그리고 파급이 있었습니다. 밑줄 시절 28이던 토글 높이가 트랙 여백을 얻으며 40이 됐습니다. 대부분의 필터 행은 탑바 슬롯이거나 높이가 고정돼 있어 아무 일도 없었는데, 일일·주간 회고 화면 두 곳만 여백 기반이라 행이 36에서 48로 밀렸습니다. 원래 높이 36으로 되돌리는 선택지는 없었습니다 — 트랙 자체가 40이라 여백을 0으로 깎아도 그 밑으로 못 내려가거든요. 두 곳을 다른 필터 행과 같은 50 고정으로 맞췄습니다.
이런 게 UI 값을 바꿀 때의 실제 비용입니다. 바꾼 컴포넌트는 하나인데 그 컴포넌트가 서 있던 자리의 계산이 전부 재검토 대상이 됩니다.
6. 이름에 남은 부채
마지막은 이름이었습니다. 별 토글 컴포넌트의 이름이 StarUnderlineToggleButton이었습니다. 밑줄을 걷어낸 순간, 이름 한가운데의 Underline은 사실이 아닌 단어가 됐습니다.
동작이 안 바뀌니 그냥 둬도 됩니다. 그런데 이런 이름은 조용히 비용을 물립니다. 반년 뒤에 이 코드를 여는 사람은 “밑줄이 어디 있지?” 하고 없는 걸 찾게 되고, 밑줄 표시를 검색하는 사람은 아무 관계 없는 파일 140곳을 만나게 되죠. 이름은 코드가 자기 자신에 대해 하는 주장이고, 틀린 주장은 시간이 갈수록 비싸집니다.
한 번에 정리했습니다. 컴포넌트 140곳, 핸들러 41곳과 18곳. 사소한 요령이 하나 있었는데, 핸들러 두 종류의 이름이 긴 쪽이 짧은 쪽을 통째로 품고 있어서 긴 것부터 치환해야 했습니다. 짧은 것부터 바꾸면 긴 이름이 중간부터 잘려 나가면서 검색이 안 되는 반쪽 이름이 생깁니다.
오늘의 교훈
하나, 기호에는 임자가 있습니다. 밑줄은 “하나만 고르는 것”의 말이라, 다중 선택에 빌려 쓰면 기능이 맞아도 문장이 틀립니다.
둘, 바꿀 때는 바꾸려는 것이 하던 일까지 세어야 합니다. 밑줄을 지우는 일은 표시를 지우는 일이면서 동시에 “여기 컨트롤이 있다”는 안내를 지우는 일이었습니다. 트랙은 그 빈자리를 메우려고 들어온 겁니다.
셋, 팔레트에 단계가 있으면 색 고르기는 취향이 아니라 탐색입니다. 위는 아이콘에 막히고 아래는 트랙에 막혀서, 실제로 고를 수 있는 값은 처음부터 하나였습니다.
기능은 하나도 바뀌지 않은 하루였습니다. 필터는 어제도 잘 걸렸고 오늘도 잘 걸립니다. 다만 이제 화면이 자기가 무슨 일을 하는지를 정확한 말로 말합니다.