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

정리해 보자고 했는데 올라가 있는 게 없었다

앱스토어에 내기까지 뭐가 남았는지 마일스톤으로 정리해 보자고 했다. 목록을 만들려면 지금 어디까지 왔는지부터 알아야 하니 상태를 훑었는데, 클라우드에 데이터베이스도 파일 저장소도 서버도 하나 없었다.

두 달 넘게 만들었고 그동안 앱은 잘 돌았다. 노트북에서 서버를 띄우면 로그인이 되고 곡이 흘렀으니 다 된 줄 알았다. 그런데 그건 wrangler(Cloudflare를 다루는 명령줄 도구)의 로컬 개발 모드가 클라우드 서비스를 노트북 안에서 흉내 내고 있었던 것이다. 곡 7GB도 그 안에만 있었다.

로컬에서 돌아가는 것과 남이 접속할 수 있는 것 사이에는 통째로 한 단계가 있다. 그 단계는 기능이 아니라서 만드는 동안 한 번도 부딪히지 않고, 그래서 다 됐다고 착각하기 쉽다. 첫 고객을 받기까지의 일정을 짤 때 이 단계를 항목으로 세워 두지 않으면 없는 셈이 된다.

정리를 시켜 보길 잘했다. 물어보지 않았으면 스토어 제출 직전에 알았을 것이다.


검수 방식을 바꿨더니 병목이 저절로 빠졌다

지난주에 제일 큰 걸림돌은 곡이었다. 검수를 기다리는 곡이 442개였고 하나에 1분씩 걸렸다. 곡 수는 줄일 수 없는 숫자라 답이 없어 보였다.

줄일 수 있는 건 곡당 걸리는 시간이었다. 1초 듣고 2초 건너뛰기를 곡 끝까지 반복하는 훑어듣기를 붙였다. 4분짜리가 80초에 끝나고, 판정을 숫자키로 눌러 바로 다음 곡으로 넘어간다.

일주일 뒤 결과가 나왔다. 442곡이 15곡이 됐다. 오늘 마일스톤을 정리하려고 세어 보기 전까지는 그렇게 줄어든 줄도 몰랐다.

여기서 두 가지를 가져간다. 하나는 막힌 일을 볼 때 「일이 몇 개인가」와 「하나에 얼마나 걸리는가」를 나눠 봐야 한다는 것이다. 앞엣것이 못 줄이는 숫자여도 뒤엣것은 대개 줄일 수 있고, 곱해지는 값이라 효과가 크다. 다른 하나는 병목이 옮겨 다닌다는 것이다. 지난주 1순위가 오늘은 목록 끝에 있었다. 순서를 정하기 전에 다시 세지 않으면 이미 끝난 일을 계속 1순위로 붙들게 된다.


쓰려던 주소를 남이 이미 갖고 있었다

Cloudflare Pages(정적 사이트와 서버 코드를 함께 올리는 호스팅)에 배포하면 프로젝트 이름을 그대로 쓴 기본 주소가 따라온다. 그런데 우리가 쓰려던 이름을 열어 보니 동명의 한국어 일기 앱이 이미 서비스 중이었다.

이 기본 주소는 내 계정 안에서 고유한 게 아니라 그 서비스를 쓰는 전 세계에서 이름이 하나뿐이다. 먼저 만든 쪽이 가진다. 우리 계정에 그 이름이 없다는 것만 확인하고 비어 있다고 판단하면 틀린다.

다행히 큰 문제는 아니었다. 실제 서비스 주소는 우리가 산 도메인의 하위 주소로 붙일 거라서, 기본 주소는 내부용 이름표에 지나지 않는다. 프로젝트 이름만 바꾸면 끝난다.

여기서 배운 건 남의 이름 공간에 서비스 주소를 두면 언제든 선점당할 수 있다는 것이다. 내 도메인이 있으면 그 위에 얼마든지 하위 주소를 만들 수 있고 누가 가져갈 수도 없다. 도메인 하나 사 두는 값이 이런 데서 돌아온다.

이름이 겹치는 서비스가 있다는 사실 자체는 따로 적어 뒀다. 성격이 전혀 달라 지금 부딪힐 일은 없지만, 제품명으로 상표를 낼 생각이 서면 그때는 먼저 조회해야 한다.


옛 화면을 지우지 않고 배포에서만 뺐다

배포하면 주소의 첫 화면에 7월에 만든 웹 관리 콘솔이 뜨게 돼 있었다. 그 콘솔은 7월 22일 이후로 손대지 않았고, 아직 역할이 관리자와 매장 둘뿐인 줄 안다. 매장 역할은 9월에 없앴다.

선택지를 받았다. 그대로 배포하거나, 첫 화면을 비우고 앱만 접속하게 하거나.

처음엔 보안 문제로 보였는데 아니었다. 콘솔을 빼든 안 빼든 서버는 똑같이 열려 있고, 관리자로 들어가려면 어차피 비밀번호가 필요하다. 실제로 달라지는 건 첫 화면에 뭐가 뜨느냐뿐이다.

진짜 이유는 다른 데 있었다. 앱 밖에서 팔기로 했으니 웹 가입·결제 페이지를 만들어야 하는데, 들어갈 자리가 바로 그 첫 화면이다. 지금 옛 콘솔을 올려두면 어차피 몇 주 뒤에 걷어낸다. 그래서 비우기로 했다.

이유를 바로 잡는 게 왜 중요하냐면, 보안 문제로 알았으면 「위험이 낮으니 그냥 두자」가 답이 됐을 것이다. 실제로 위험은 낮았다. 자리를 비워 두는 판단은 위험이 아니라 다음 일정에서 나온다.

콘솔 코드 자체는 지우지 않고 저장소에 남겼다. 급할 때 노트북에서 띄워 쓰는 예비 수단이라고 적어 뒀으니, 배포에서만 빼면 목적이 둘 다 지켜진다. 안 쓰는 것을 지울지 말지 물으면 「배포에서 뺀다」가 「삭제한다」보다 대개 낫다.


출시일이 앱이 아니라 계정 종류로 갈린다

플레이스토어 제출 준비를 항목으로 적다가 알게 된 것이다. 개인 개발자 계정으로 앱을 내려면 테스터 열몇 명을 모아 14일 동안 연속으로 테스트를 돌려야 한다. 사업자로 등록된 계정에는 이 의무가 없다.

앱이 다 만들어져 있어도 계정 종류에 따라 출시가 2주 늦어진다는 뜻이다. 코드와 아무 상관이 없고, 마지막에 알면 되돌릴 수도 없다.

플랫폼에 물건을 올릴 때는 기능 목록만 볼 게 아니라 그 플랫폼이 요구하는 절차와 자격을 먼저 확인해야 한다. 심사 기간, 필요한 계정 종류, 미리 준비해야 하는 문서 같은 것들이다. 이것들은 개발 일정과 나란히 흐르지 않고 일정 끝에 통째로 붙는다.

같은 자리에서 되돌릴 수 없는 게 하나 더 나왔다. 앱 식별자는 한 번 올리면 영원히 못 바꾼다. 지금 임시로 지어 둔 이름이 그대로 굳는 셈이라, 제출 전이 유일하게 고칠 기회다.


용량은 지금이 아니라 몇 달 뒤로 센다

음원 저장소는 무료로 10GB를 쓸 수 있다. 지금 곡이 7GB니까 들어간다. 여기서 멈추면 안 된다.

새 곡이 한 달에 60곡쯤 들어오고 그게 0.5GB쯤 된다. 남은 3GB를 나누면 6개월이다. 즉 지금 결정은 「들어간다」가 아니라 「반년 뒤에 유료로 넘어간다」이다.

용량이나 한도를 볼 때는 현재 값과 한계선만 비교하지 말고 늘어나는 속도를 같이 넣어야 한다. 그래야 언제 돈이 들기 시작하는지가 나오고, 그 시점을 알면 미리 정리할지 그냥 낼지 고를 수 있다. 모르면 어느 날 갑자기 막힌다.


요약

  • 로컬에서 다 돌아가도 클라우드에 올리는 단계는 통째로 남아 있다. 기능이 아니라서 만드는 동안 안 부딪히고, 일정에 항목으로 없으면 없는 셈이 된다.
  • 막힌 일은 「몇 개인가」와 「하나에 얼마 걸리는가」로 나눠 본다. 개수를 못 줄여도 단위 시간은 대개 줄일 수 있다.
  • 병목은 옮겨 다닌다. 순서를 정하기 전에 다시 센다.
  • 남의 이름 공간에 둔 주소는 선점당한다. 내 도메인 위에 두면 흔들리지 않는다.
  • 안 쓰는 것을 정리할 때는 「삭제」보다 「배포에서 빼기」가 대개 낫다. 예비 수단은 남기고 자리만 비운다.
  • 자리를 비워 두는 판단은 위험이 아니라 다음 일정에서 나온다. 이유를 바로 잡아야 답이 안 뒤집힌다.
  • 플랫폼 출시는 절차와 자격을 먼저 본다. 개발과 나란히 가지 않고 끝에 통째로 붙는다.
  • 되돌릴 수 없는 것은 제출 전에 확정한다. 앱 식별자가 그렇다.
  • 용량은 현재 값이 아니라 늘어나는 속도로 센다. 언제 돈이 드는지가 나온다.

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