이 글은 Today I Learn 시리즈의 95번째 기록입니다. (총 125개)

팀 소통 허브(첫 화면 게시판)와 텍스트 내보내기를 붙이면서 판단이 필요했던 것들을 모았다.


2열로 놨더니 읽는 순서가 어긋났다

설명서 목차를 2열로 바꿨더니 원하던 모양이 아니었다. 1·2·3·4가 왼쪽에 쭉 있고 5·6·7이 오른쪽에 오기를 바랐는데, 나온 건 1·2가 첫 줄, 3·4가 둘째 줄로 가로로 채워지는 2열이었다. 칸 수는 맞는데 읽는 순서가 반대였다.

“2열”이라는 말에는 몇 칸이냐만 들어 있고 어느 방향으로 흐르느냐가 빠져 있다. 목록을 여러 열로 나눌 때 정해야 하는 건 칸 수가 아니라 읽는 순서다. 가로로 채우는 격자(그리드 grid — 행과 열의 칸에 순서대로 채우는 배치)와 세로로 채운 뒤 옆으로 넘어가는 단(다단 column — 신문처럼 한 단을 아래까지 채우고 다음 단으로 가는 배치)은 아예 다른 도구라, 원하는 순서를 먼저 말해야 맞는 쪽이 나온다.

곁들여, 다단으로 두면 항목 하나가 단 경계에서 잘려 두 열에 걸칠 수 있다. 잘리지 않게 붙잡는 설정(break-inside)이 따로 필요하다.


글씨가 배경에 묻혔다 — 색에도 용도가 있다

목차 글씨에 테마 강조색이 들어갔는데 노란 글씨가 배경에 묻혀 읽히지 않았다. 그 색은 버튼 바탕처럼 밝은 면에 깔라고 만들어 둔 색이었고, 비슷하게 밝은 배경 위의 글씨로 쓰니 대비가 사라진 것이었다.

디자인 토큰(색·간격 같은 값에 이름을 붙여 여기저기 재사용하는 것)에는 “어디에 쓸 값인지”가 함께 붙어 있다. 이름이 그럴듯하다고 아무 자리에나 가져다 쓰면 안 되고, 특히 배경용 색을 글씨색으로 돌려쓰면 대비가 무너진다.

이건 만든 쪽 화면에서는 잘 안 걸린다. 무슨 색을 의도했는지 알고 보기 때문이다. 색 대비는 모르고 보는 눈이 한 번 지나가야 잡힌다.


열었을 때 무엇이 먼저 보이나

단톡방을 없애기로 하면서 공지와 기능 요청을 남길 게시판이 필요해졌고, 그걸 첫 화면에 두기로 했다. 그때까지 앱은 열면 곧장 콘티 작업 화면이었다.

앱을 열었을 때 처음 보이는 화면은 “이게 무엇을 하는 곳인가”를 정한다. 작업 화면이 먼저 뜨면 혼자 쓰는 도구가 되고, 소식과 입구가 있는 홈이 먼저 뜨면 팀이 모이는 곳이 된다. 기능을 하나 더 붙인 게 아니라 도구의 성격을 바꾼 결정이었다.

바깥에 있던 대화 창구를 앱 안으로 들여올 때, 그 창구를 어디에 두느냐가 그대로 사용자의 첫인상이 된다.


긴 목록은 끊어서 보여준다

곡이 900곡 가까이 쌓이자 목록이 감당이 안 됐다. 스무 개씩 끊어 보여주고 번호와 「다음」으로 넘기게 하기로 정했다.

목록은 길어지면 스크롤만으로는 버티지 못한다. 이때 정할 것은 끊는 단위와 이동 수단 두 가지이고, 그 둘만 정해지면 나머지는 따라온다.

여기서 하나 더 배운 건, 페이지 번호를 전부 늘어놓으면 그 번호 줄이 또 하나의 긴 목록이 된다는 것이다. 지금 자리 주변만 보여주는 편이 낫다. 그리고 검색이나 필터로 목록 자체가 바뀌면 페이지를 첫 장으로 되돌려야 한다 — 안 그러면 결과는 세 개인데 7페이지에 머물러 빈 화면을 보게 된다.


가리는 것과 지우는 것은 다르다

이메일을 감추기로 했는데 원하는 것이 두 갈래였다. 관리자 외에는 다른 사람 이메일을 못 보게 하는 것, 그리고 테스트 계정 이메일은 아예 지우는 것. 같은 감추기처럼 들리는데 요구가 갈렸다.

안 보이게 하는 것(마스킹 — 값의 일부를 별표 등으로 덮어 보여주는 것)과 없애는 것은 층이 다르다. 마스킹은 보여주는 쪽만 손대므로 저장된 값은 그대로 남아 있고, 정말 남으면 안 되는 값은 저장소에서도 비워야 한다. 둘을 같은 요구로 뭉뚱그리면 “지웠다”고 생각한 값이 데이터베이스에 그대로 있게 된다.

그리고 누가 원문을 볼 수 있는지는 권한으로 가른다. 여기서 한 가지 — 가리는 일은 화면이 아니라 데이터를 내려주는 서버에서 해야 한다. 원문을 다 보내 놓고 화면에서만 가리면 열어 보는 사람에게는 다 보이므로, 그건 가린 게 아니다.


알림은 볼 게 있는 사람에게만

홈에 알림 줄을 만들어 두고 열어 보니 알림이 없어서 안 뜬 건지 고장인지 알 수가 없었다. 실제로 처리할 것이 하나도 없어서 줄이 통째로 비어 있던 상태였다.

알림 자리를 늘 띄워 두고 내용만 비우면 그 자리가 소음이 된다. 볼 게 없으면 줄 자체를 감추는 편이 낫고, 각 항목도 그것을 처리할 권한이 있는 사람에게만 센다. 미처리 요청 수는 그걸 처리할 사람에게나 알림이지, 일반 사용자에게는 그냥 숫자다.

“빈 상태를 어떻게 보여줄 것인가”가 알림 설계의 절반이다. 채워졌을 때만 보고 만들면 대부분의 시간 동안 비어 있는 화면을 방치하게 된다.


남의 파일을 만들 것인가, 남이 읽는 걸 줄 것인가

현장에 자막기가 새로 들어와서, 우리 콘티를 그 자막기 파일로 바로 내보낼 수 있는지부터 판단하기로 순서를 잡았다. 실물 파일을 열어 보니 그 프로그램만 읽는 독자 규격의 바이너리 포맷(사람이 읽는 글자가 아니라 프로그램용 이진 데이터로 저장된 형식)이었고, 안에는 텍스트로는 채울 수 없는 3차원 장면 데이터까지 들어 있었다. 우리가 유효한 파일을 만들어내는 건 사실상 불가였다.

그런데 매뉴얼을 뒤지니 그 도구에는 외부 텍스트 파일을 읽어 화면 요소에 줄 단위로 끌어오는 기능(데이터 링크)이 있었다. 관건은 그다음이었다 — 제목과 가사는 자막 디자인이 다른데 그걸 한 파일로 넣을 수 있나. 화면 요소를 둘 두고 같은 파일에 각자 몇 번째 줄을 볼지 지정하면, 서식이 다른 두 영역도 한 텍스트로 채울 수 있었다.

외부 도구에 붙일 때 먼저 볼 것은 “그 포맷을 흉내 낼 수 있나”가 아니라 “그 도구가 무엇을 읽어들이나”다. 남의 저장 형식을 재현하는 건 상대가 버전을 올리면 깨지는 접합면이고, 그쪽이 스스로 열어 둔 입력 통로는 얕고 오래 간다.

판단 자체도 실물이 있어야 빨랐다. 파일 한 개와 매뉴얼을 받아 보는 게 “될까요 안 될까요”를 추측으로 주고받는 것보다 훨씬 빨리 결론에 닿았다.


요약

  • “2열”에는 칸 수만 있고 읽는 순서가 없다. 여러 열로 나눌 땐 순서를 먼저 정한다 — 가로로 채우는 격자와 세로로 채우는 단은 다른 도구다.
  • 색 토큰에는 용도가 붙어 있다. 배경용을 글씨색으로 돌려쓰면 대비가 무너지고, 이건 만든 쪽 눈에는 안 걸린다.
  • 열었을 때 먼저 보이는 화면이 그 도구의 성격을 정한다. 작업 화면이면 도구, 소식과 입구가 있는 홈이면 모이는 곳이다.
  • 긴 목록은 끊는 단위와 이동 수단만 정하면 된다. 페이지 번호도 다 늘어놓으면 또 하나의 긴 목록이 되고, 목록이 바뀌면 첫 장으로 되돌린다.
  • 가리는 것과 지우는 것은 층이 다르다. 마스킹은 저장값을 남기므로, 남으면 안 되는 값은 저장소에서도 비운다. 가리는 일은 화면이 아니라 서버에서 한다.
  • 알림은 볼 권한이 있는 사람에게 볼 게 있을 때만. 빈 상태를 어떻게 둘지가 설계의 절반이다.
  • 외부 도구와 붙일 땐 그 포맷을 흉내 내려 하지 말고 그 도구가 읽어들이는 통로를 찾는다. 그리고 실물 파일과 매뉴얼이 추측보다 빠르다.

Series: Today I Learn

1 C++ 자료형(Data Type) 2 MD5 vs pHash 3 C++에서 함수의 선언과 정의 4 Tkinter padx, pady 5 메모리와 포인터 변수 6 Call by Value, Call by Reference, Call by Pointer 비교 7 const 8 Gemfile — Jekyll 프로젝트의 의존성 파일 9 kramdown-parser-gfm — Jekyll의 GFM 파서 10 파서(Parser) 11 AHU vs OHU 12 I might try it vs I'll try it 뉘앙스 차이 13 SESSION_EXPIRE_AT_BROWSER_CLOSE=True 14 configuration key 15 Git stash vs discard 16 subprocess.Popen으로 Windows 탐색기에 명령어를 전달 17 Post 잔디 분석하기 18 Google Sheets Sync 최적화 19 DSL (Domain Specific Language)과 GPL (General Purpose Language) 20 마크다운 표 그리는 방법 21 쿼리 파라미터(Query Parameter). 기존 QR코드 재활용 22 Django 보안 취약점 점검 및 수정 23 OOP Object-Oriented Programming 객체 지향 프로그래밍 24 Fernet 대칭 암호화 25 Jekyll 코드블록 안의 Liquid 태그 26 insertOnConflictUpdate vs DoUpdate(target) 27 세션 필터 28 아코디언(Accordiaon) UI를 펼친상태로 만들기 29 input의 step 30 Word Cloud 31 Google Sheets를 데이터 버스로(with AppSheet) 32 Django 모델 텍스트 필드 자동 수집 패턴 33 localStorage로 섹션 토글 상태 유지 34 순차 ID 생성(`select_for_update()` + `max()` 조합) 35 역참조 검색과 distinct() 36 xlsx 다운로드와 로딩 오버레이 충돌 37 Android 파일 공유 MIME 타입 38 AssetManifest — Flutter 빌드 타임 asset 목록 런타임 조회 39 UTF-8 BOM과 PowerShell 파일 쓰기 40 소리꽃 KeyBloom TIL 1 41 소리꽃 KeyBloom TIL 2 42 소리꽃 KeyBloom TIL 3 43 메트로놈 Simple Metronome TIL 1 44 소리꽃 KeyBloom TIL 4 45 메트로놈 Simple Metronome TIL 2 46 소리꽃 KeyBloom TIL 5 47 메트로놈 Simple Metronome TIL 3 48 정적 블로그 SEO 정비와 Pagefind 검색 도입 49 메트로놈 Simple Metronome TIL 4 50 안드로이드 AudioTrack 연속 재생, 실측 피커 정렬, 카메라 토치 플래시 51 메트로놈 Simple Metronome TIL 5 52 모래게임 Sandrop TIL 1 53 메트로놈 Simple Metronome TIL 6 54 모래게임 Sandrop TIL 2 55 모래게임 Sandrop TIL 3 56 메트로놈 Simple Metronome TIL 7 57 모래게임 Sandrop TIL 4 58 모래게임 Sandrop TIL 5 59 모래게임 Sandrop TIL 6 60 소리꽃 KeyBloom TIL 6 61 모래게임 Sandrop TIL 7 62 모래게임 Sandrop TIL 8 63 소리꽃 KeyBloom TIL 7 64 모래게임 Sandrop TIL 9 65 소리꽃 KeyBloom TIL 8 66 모래게임 Sandrop TIL 10 67 소리꽃 KeyBloom TIL 9 68 소리꽃 KeyBloom TIL 10 69 소리꽃 KeyBloom TIL 11 70 소리꽃 KeyBloom TIL 12 71 소리꽃 KeyBloom TIL 13 72 온실 GreenHouse TIL 1 73 소리꽃 KeyBloom TIL 14 74 온실 GreenHouse TIL 2 75 소리꽃 KeyBloom TIL 15 76 소리꽃 KeyBloom TIL 16 77 소리꽃 KeyBloom TIL 17 78 소리꽃 KeyBloom TIL 18 79 소리꽃 KeyBloom TIL 19 80 소리꽃 KeyBloom TIL 20 81 로그스톤 상표 셀프 출원 TIL 1 82 소리꽃 KeyBloom TIL 21 83 소리꽃 KeyBloom TIL 22 84 소리꽃 KeyBloom TIL 23 85 소리꽃 KeyBloom TIL 24 86 MiniMacro TIL 1 87 Astro가 무엇인지, 왜 옮기는지 88 MiniMacro TIL 2 89 메트로놈 Simple Metronome TIL 8 90 메트로놈 Simple Metronome TIL 9 91 블로그 Astro 이관 TIL 92 오픈데이 Openday TIL 1 93 오픈데이 Openday TIL 2 94 Unreal Engine MCP TIL 1 95 콘티온 Conti On TIL 11 읽는 중 96 오픈데이 Openday TIL 3 97 오픈데이 Openday TIL 4 98 오픈데이 Openday TIL 5 99 ScorePlayer TIL 1 100 BuildingHub TIL 1 101 ScorePlayer TIL 2 102 BuildingHub TIL 2 103 ScorePlayer TIL 3 104 Unreal Engine MCP TIL 2 105 궁 미로 PalaceMaze TIL 1 106 메트로놈 Simple Metronome TIL 10 107 메트로놈 Simple Metronome TIL 11 108 오픈데이 Openday TIL 6 109 BuildingHub TIL 3 110 Obsidian vault에서 코드만 빼기 — directory junction 111 ScorePlayer TIL 4 112 오픈데이 Openday TIL 7 113 URL은 바뀔 수 있는 것에 묶지 않는다 114 도구는 기능이 아니라 내 일에 맞는지로 고른다 115 돌이키기 힘든 변경은 작게 먼저 확인한다 116 궁 미로 PalaceMaze TIL 3 117 궁 미로 PalaceMaze TIL 2 118 소리꽃 KeyBloom TIL 29 119 오픈데이 Openday TIL 8 120 BuildingHub TIL 4 121 ScorePlayer TIL 5 122 BuildingHub TIL 5 123 MiniMacro TIL 3 124 소리꽃 KeyBloom TIL 30 125 궁 미로 PalaceMaze TIL 4