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

잠금을 넣으려니 잠긴 사람은 누가 풀어 주나

비밀번호를 여러 번 틀리면 한동안 로그인을 막기로 했다. 비밀번호를 무작정 대입해 보는 공격을 막는 기본 장치다. 매장 회원 쪽은 쉽게 정했는데, 운영자 계정에서 선택지가 갈렸다.

  • 한 번 잠기면 사람이 풀어 줄 때까지 계속 잠금
  • 일정 시간 뒤 저절로 풀림

앞의 것이 더 단단해 보인다. 그런데 운영자 계정이 잠기면 풀어 줄 사람도 운영자다. 잠긴 채로는 관리 화면에 못 들어가니 데이터베이스를 직접 건드려야 풀린다. 막으려던 공격자가 운영자를 내쫓는 수단이 된다.

자동 해제를 골랐다. 자동으로 풀려도 정해진 시간에 시도할 수 있는 횟수는 얼마 안 돼서, 사람이 외워 쓰는 비밀번호라도 그 속도로는 뚫리지 않는다. 단단함은 거의 그대로 두고, 풀어 줄 사람이 필요 없는 쪽을 택했다.

보안 장치는 「얼마나 막나」만 보면 가장 센 것을 고르게 된다. 한 번 더 물을 것은 「잠겼을 때 누가 푸나」다. 푸는 사람과 잠기는 사람이 같으면, 사람 손을 거쳐야 풀리는 장치는 그 사람을 겨누는 장치가 된다.


필요한 기능인데 지금은 안 만들기로 했다

같은 자리에서 인증번호 메일로 비밀번호를 다시 정하는 기능도 같이 만들자고 했다. 잠금이 생기면 비밀번호를 잊은 사람이 빠져나갈 길이 필요하니 짝으로 보였다.

만들려고 보니 먼저 정할 것이 있었다. 메일을 무엇으로 보낼지다. 쓰고 있는 Cloudflare의 메일 발송은 요금제를 올려야 하고, 다른 발송 서비스를 쓰면 가입과 설정이 따로 든다. 비용과 서비스를 고르는 일이 기능보다 앞에 섰다.

그래서 「지금 말고 출시 전에만」 하기로 미뤘다. 지금은 가입한 매장이 없어 비밀번호를 잊을 사람이 없다. 이 기능이 쓸모 있어지는 날은 출시일이고, 그 전까지는 만들어 두어도 아무도 쓰지 않는다. 그 사이에 발송 서비스 선택지는 열어 둔 채로 남는다.

할 일 목록에는 「필요한 것」과 「지금 필요한 것」이 섞여 있다. 필요하다는 이유만으로 지금 만들면 아무도 안 쓰는 기간에 결정만 먼저 굳는다. 쓰이기 시작하는 날을 기준으로 줄을 세우면, 미룬 것은 잊는 게 아니라 날짜가 붙은 약속이 된다. 그래서 체크리스트에 「출시 전」 칸을 따로 두고 넣었다.


할 일이 머릿속에만 있어서 과부하가 왔다

출시까지 남은 일이 앱, 서버, 플레이 콘솔, 결제사, 로그스톤 샵 조항, 스토어 그래픽까지 여러 갈래로 흩어져 있었다. 그중 절반은 코드가 아니라 내가 콘솔에 들어가 설정해야 하는 일이었다. 무엇이 끝났고 무엇이 남았는지 떠올리는 것만으로 지쳐서 체크리스트처럼 정리해 달라고 했다.

일이 줄어든 게 아니었는데 한 장으로 보니 다음에 할 한 가지가 보였다. 과부하의 원인은 일의 양보다, 일 목록을 머릿속에 들고 다니는 데 쓰는 힘이었다.

페이지에서 바로 체크하고 저장되게 한 것도 쓸모가 있었다. 오늘 두 기기 동시 재생을 확인하자마자 그 자리에서 체크했다. 나중에 무엇을 확인했는지 대화 기록을 뒤질 필요가 없다. 목록이 「해야 할 것」을 보여 주는 동시에 「끝낸 것」의 기록이 된다.


시안이 마음에 들어 「이대로」라고 했더니

Design 기능으로 보통의 음악 앱을 참고한 화면 시안을 만들어 보라고 시켰다. 웹 판매 페이지와 같은 황동색으로 맞춘 안을 골랐고, 결과가 예뻐서 이대로 바꾸고 싶다고 했다.

「이대로」는 모습에 대한 말이었는데, 옮기다 보니 시안에 없는 결정이 계속 나왔다.

  • 재생목록 머리에 「담기」 버튼이 그려져 있었는데, 누르면 무엇을 하는지는 시안에 없었다. 곡 전부를 내 재생목록에 넣는 동작으로 만드는 쪽이 정해서 알려 왔고, 다르게 원하면 말하라고 했다.
  • 넓은 화면에서 오른쪽 재생 패널이 위에 붙지 않고 세로 가운데에 떠 있었다. 시안은 내용이 꽉 찬 한 장이라 내용이 짧을 때의 모습이 없었다.
  • 지금 곡은 글자 색만 바뀌어서 목록에서 잘 안 보였다. 시안에서는 눈에 띄었는데, 곡이 수백 개인 실제 목록에서는 묻혔다.

시안은 정해진 크기와 예쁜 예시 데이터로 그린 한 장면이다. 버튼이 무엇을 하는지, 내용이 짧거나 길 때, 목록이 수백 줄일 때는 담기지 않는다. 「이대로」라고 하면 그 빈칸은 만드는 쪽이 채운다. 채운 결과가 틀리진 않았지만 내가 고른 것은 아니었다. 다음에는 시안을 받으면 모습 말고 버튼마다 「누르면 무엇이」를 한 줄씩 먼저 묻고, 옮긴 뒤 실제 데이터로 한 바퀴 쓰면서 고칠 것을 한 번 더 모으는 시간을 따로 잡는다.


버튼 글자를 빼도 되는 자리

재생목록 머리에 「재생」, 「셔플」, 「일시정지」 글자가 붙어 있었다. 글자가 없어도 된다고 했다. 재생 삼각형, 일시정지 두 줄, 셔플 교차 화살표는 음악 앱을 써 본 사람이면 누구나 안다. 글자가 더해 주는 정보가 없고 버튼만 넓어졌다.

같은 날 반대 방향으로 고친 버튼이 있었다. 다른 기기에서 재생 중일 때 뜨는 버튼을 「가져오기」에서 「이 기기에서 재생」으로 바꿨다. 이건 음악 앱이면 다 있는 동작이 아니라 룸톤에만 있는 상황이라, 아이콘 하나로는 뜻이 전해지지 않고 글자도 누르면 일어나는 일을 그대로 말해야 했다.

글자를 뺄지는 버튼이 흔한 동작인지로 가른다. 누구나 아는 동작은 아이콘으로 충분하고, 이 앱에만 있는 상황의 버튼은 글자가 필요하다.


더 빠르게 할 수 있는데 미뤘다

재생 시작이 느려서 운영 환경(실제 서비스가 도는 서버)과 폰에서 구간별로 재고 원인마다 줄였다. 체감으로 확실히 빨라진 뒤에도 수단이 하나 남았다. 지금 곡이 나오는 동안 다음 곡의 재생 주소를 미리 받아 두면, 다음 곡으로 넘길 때 주소를 받는 기다림이 없어진다.

걸리는 곳이 있었다. 룸톤은 곡 재생 주소를 받아 가는 순간을 재생 한 번으로 센다(앞선 TIL 1에서 정하고 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 습관만들기 Pawbit TIL 1 27 insertOnConflictUpdate vs DoUpdate(target) 28 세션 필터 29 아코디언(Accordiaon) UI를 펼친상태로 만들기 30 input의 step 31 습관만들기 Pawbit TIL 2 32 습관만들기 Pawbit TIL 3 33 Word Cloud 34 Google Sheets를 데이터 버스로(with AppSheet) 35 Django 모델 텍스트 필드 자동 수집 패턴 36 localStorage로 섹션 토글 상태 유지 37 순차 ID 생성(`select_for_update()` + `max()` 조합) 38 역참조 검색과 distinct() 39 xlsx 다운로드와 로딩 오버레이 충돌 40 Android 파일 공유 MIME 타입 41 AssetManifest — Flutter 빌드 타임 asset 목록 런타임 조회 42 크레센도 Phaser + TypeScript TIL 1 43 크레센도 Phaser + TypeScript TIL 2 44 크레센도 Phaser + TypeScript TIL 3 45 크레센도 Phaser + TypeScript TIL 4 46 크레센도 Phaser + TypeScript TIL 5 47 여둘까 Office Layout TIL 1 48 습관만들기 Pawbit TIL 4 49 여둘까 Office Layout TIL 2 50 여둘까 Office Layout TIL 3 51 크레센도 Phaser + TypeScript TIL 6 52 UTF-8 BOM과 PowerShell 파일 쓰기 53 소리꽃 KeyBloom TIL 1 54 습관만들기 Pawbit TIL 5 55 여둘까 Office Layout TIL 4 56 소리꽃 KeyBloom TIL 2 57 소리꽃 KeyBloom TIL 3 58 메트로놈 Simple Metronome TIL 1 59 소리꽃 KeyBloom TIL 4 60 메트로놈 Simple Metronome TIL 2 61 소리꽃 KeyBloom TIL 5 62 습관만들기 Pawbit TIL 6 63 크레센도 Phaser + TypeScript TIL 7 64 메트로놈 Simple Metronome TIL 3 65 소설 타임라인 관리 도구 Lumior TIL 1 66 습관만들기 Pawbit TIL 7 67 정적 블로그 SEO 정비와 Pagefind 검색 도입 68 크레센도 Phaser + TypeScript TIL 8 69 메트로놈 Simple Metronome TIL 4 70 습관만들기 Pawbit TIL 8 71 안드로이드 AudioTrack 연속 재생, 실측 피커 정렬, 카메라 토치 플래시 72 메트로놈 Simple Metronome TIL 5 73 모래게임 Sandrop TIL 1 74 습관만들기 Pawbit TIL 9 75 메트로놈 Simple Metronome TIL 6 76 모래게임 Sandrop TIL 2 77 모래게임 Sandrop TIL 3 78 습관만들기 Pawbit TIL 11 79 습관만들기 Pawbit TIL 10 80 메트로놈 Simple Metronome TIL 7 81 모래게임 Sandrop TIL 4 82 모래게임 Sandrop TIL 5 83 습관만들기 Pawbit TIL 12 84 모래게임 Sandrop TIL 6 85 소리꽃 KeyBloom TIL 6 86 모래게임 Sandrop TIL 7 87 모래게임 Sandrop TIL 8 88 소리꽃 KeyBloom TIL 7 89 모래게임 Sandrop TIL 9 90 소리꽃 KeyBloom TIL 8 91 모래게임 Sandrop TIL 10 92 소리꽃 KeyBloom TIL 9 93 콘티온 ContiOn TIL 1 94 소리꽃 KeyBloom TIL 10 95 콘티온 ContiOn TIL 2 96 습관만들기 Pawbit TIL 13 97 콘티온 ContiOn TIL 3 98 룸톤 Roomtone TIL 1 99 모래게임 Sandrop TIL 11 100 소리꽃 KeyBloom TIL 11 101 여둘까 Office Layout TIL 5 102 모래게임 Sandrop TIL 12 103 모래게임 Sandrop TIL 13 104 콘티온 ContiOn TIL 4 105 모래게임 Sandrop TIL 14 106 소리꽃 KeyBloom TIL 12 107 습관만들기 Pawbit TIL 14 108 모래게임 Sandrop TIL 15 109 모래게임 Sandrop TIL 16 110 소리꽃 KeyBloom TIL 13 111 온실 GreenHouse TIL 1 112 콘티온 ContiOn TIL 5 113 모래게임 Sandrop TIL 17 114 소리꽃 KeyBloom TIL 14 115 온실 GreenHouse TIL 2 116 콘티온 ContiOn TIL 6 117 모래게임 Sandrop TIL 18 118 콘티온 ContiOn TIL 7 119 모래게임 Sandrop TIL 19 120 소리꽃 KeyBloom TIL 15 121 룸톤 Roomtone TIL 2 122 모래게임 Sandrop TIL 20 123 모래게임 Sandrop TIL 21 124 소리꽃 KeyBloom TIL 16 125 소리꽃 KeyBloom TIL 17 126 소리꽃 KeyBloom TIL 18 127 여둘까 Office Layout TIL 6 128 소리꽃 KeyBloom TIL 19 129 소리꽃 KeyBloom TIL 20 130 콘티온 ContiOn TIL 8 131 콘티온 ContiOn TIL 9 132 로그스톤 상표 셀프 출원 TIL 1 133 소리꽃 KeyBloom TIL 21 134 로그스톤 샵 LogStone Shop TIL 1 135 소리꽃 KeyBloom TIL 22 136 로그스톤 샵 LogStone Shop TIL 2 137 룸톤 Roomtone TIL 3 138 소리꽃 KeyBloom TIL 23 139 모래게임 Sandrop TIL 22 140 모래게임 Sandrop TIL 23 141 소리꽃 KeyBloom TIL 24 142 로그스톤 샵 LogStone Shop TIL 3 143 모래게임 Sandrop TIL 24 144 소리꽃 KeyBloom TIL 25 145 모래게임 Sandrop TIL 25 146 MiniMacro TIL 1 147 룸톤 Roomtone TIL 4 148 모래게임 Sandrop TIL 26 149 콘티온 ContiOn TIL 10 150 Astro가 무엇인지, 왜 옮기는지 151 룸톤 Roomtone TIL 5 152 룸톤 Roomtone TIL 6 153 모래게임 Sandrop TIL 27 154 소리꽃 KeyBloom TIL 26 155 MiniMacro TIL 2 156 로그스톤 샵 LogStone Shop TIL 4 157 메트로놈 Simple Metronome TIL 8 158 모래게임 Sandrop TIL 28 159 여둘까 Office Layout TIL 7 160 룸톤 Roomtone TIL 7 161 메트로놈 Simple Metronome TIL 9 162 블로그 Astro 이관 TIL 163 여둘까 Office Layout TIL 8 164 로그스톤 샵 LogStone Shop TIL 5 165 모래게임 Sandrop TIL 29 166 오픈데이 Openday TIL 1 167 로그스톤 샵 LogStone Shop TIL 6 168 모래게임 Sandrop TIL 30 169 오픈데이 Openday TIL 2 170 모래게임 Sandrop TIL 31 171 Unreal Engine MCP TIL 1 172 모래게임 Sandrop TIL 32 173 소리꽃 KeyBloom TIL 27 174 콘티온 ContiOn TIL 11 175 오픈데이 Openday TIL 3 176 로그스톤 샵 LogStone Shop TIL 7 177 모래게임 Sandrop TIL 33 178 소리꽃 KeyBloom TIL 28 179 오픈데이 Openday TIL 4 180 여둘까 Office Layout TIL 9 181 오픈데이 Openday TIL 5 182 ScorePlayer TIL 1 183 모래게임 Sandrop TIL 34 184 BuildingHub TIL 1 185 ScorePlayer TIL 2 186 여둘까 Office Layout TIL 10 187 BuildingHub TIL 2 188 ScorePlayer TIL 3 189 Unreal Engine MCP TIL 2 190 궁 미로 PalaceMaze TIL 1 191 메트로놈 Simple Metronome TIL 10 192 메트로놈 Simple Metronome TIL 11 193 오픈데이 Openday TIL 6 194 BuildingHub TIL 3 195 Obsidian vault에서 코드만 빼기 — directory junction 196 ScorePlayer TIL 4 197 여둘까 Office Layout TIL 11 198 오픈데이 Openday TIL 7 199 URL은 바뀔 수 있는 것에 묶지 않는다 200 돌이키기 힘든 변경은 작게 먼저 확인한다 201 도구는 기능이 아니라 내 일에 맞는지로 고른다 202 궁 미로 PalaceMaze TIL 2 203 궁 미로 PalaceMaze TIL 3 204 소리꽃 KeyBloom TIL 29 205 오픈데이 Openday TIL 8 206 룸톤 Roomtone TIL 8 207 BuildingHub TIL 4 208 ScorePlayer TIL 5 209 로그스톤 샵 LogStone Shop TIL 8 210 여둘까 Office Layout TIL 12 211 오픈데이 Openday TIL 9 212 BuildingHub TIL 5 213 MiniMacro TIL 3 214 룸톤 Roomtone TIL 10 215 룸톤 Roomtone TIL 9 216 소리꽃 KeyBloom TIL 30 217 오픈데이 Openday TIL 10 218 여둘까 Office Layout TIL 13 219 룸톤 Roomtone TIL 11 220 소리꽃 KeyBloom TIL 31 221 궁 미로 PalaceMaze TIL 4 222 룸톤 Roomtone TIL 12 223 로그스톤 샵 LogStone Shop TIL 9 224 룸톤 Roomtone TIL 13 읽는 중 225 로그스톤 샵 LogStone Shop TIL 10 226 룸톤 Roomtone TIL 14 227 소리꽃 KeyBloom TIL 32 228 룸톤 Roomtone TIL 15 229 룸톤 Roomtone TIL 16 230 룸톤 Roomtone TIL 17