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

승인이 났는데 아직 아무 일도 안 일어났다

심사 통과 소식이 왔다. 그런데 스토어에 가 보니 아무것도 없었다.

승인과 공개가 다른 단계였다. 심사를 통과해도 출시 버튼을 따로 눌러야 사람들이 받을 수 있다. 그 사이에는 앱이 승인된 채로 가만히 있다.

그 틈이 중요하다. 심사가 끝났다고 생각하고 심사용 계정 설정을 되돌리려던 참이었는데, 아직 공개 전이라 심사자가 다시 볼 수도 있는 상태였다. 건드렸으면 그때 막혔을 것이다.

  • 「통과」와 「나갔다」 사이에 내가 눌러야 할 것이 있는지 확인한다. 자동으로 이어지지 않는 단계가 있다.
  • 그 틈에서는 심사에 쓴 것을 원래대로 돌리지 않는다. 아직 끝난 게 아니다.

버튼을 누르기 전에 물어본 것

누르기 직전에 한 가지가 걸렸다. 지금 돌고 있는 서버가 심사에 낸 앱과 같은 판인가.

앱은 며칠 전에 만들어 제출했고, 그 뒤로도 서버를 여러 번 고쳤다. 앱이 기대하는 응답과 서버가 주는 응답이 어긋나 있으면, 공개하는 순간 받는 사람마다 깨진 앱을 쓰게 된다.

확인해 보니 맞았다. 제출한 번들을 만든 시점 이후로 서버 코드가 한 줄도 안 바뀌어 있었다.

  • 앱과 서버는 따로 나간다. 앱은 심사 기간만큼 시간이 멈춰 있고 서버는 그동안 계속 움직인다.
  • 공개는 되돌리기 어려운 조작이다. 되돌리기 어려운 것 앞에서는 한 번 더 묻는 게 싸다.

기뻤는데, 그다음에 할 일이 없었다

눌렀고, 페이지가 열렸다. 기뻤다.

그리고 바로 「이걸 어떻게 알리지」가 왔다. 할 수 있는 게 없었다. 주소가 생겼다는 것 말고는 준비해 둔 게 하나도 없었다.

몇 달 동안 잡은 일정이 전부 만드는 일정이었다. 곡을 몇 곡까지 채우고, 결제를 붙이고, 심사를 통과하고. 알리는 일은 「출시하면 그때」로 미뤄 뒀는데, 막상 그때가 되니 그 자리에 아무것도 없었다.

  • 만드는 일정과 알리는 일정은 따로 잡아야 한다. 하나가 끝나면 다른 하나가 시작되는 게 아니라, 둘 다 미리 있어야 한다.
  • 출시일은 끝나는 날이 아니라 다른 일이 시작되는 날이다. 그날 할 일이 비어 있으면 준비가 덜 된 것이다.

되돌리기로 적어 둔 일을 되돌리지 않기로 했다

심사용 계정에는 전곡을 들을 수 있게 권한을 열어 뒀었다. 심사자가 미리듣기 30초만 듣고 「반쪽짜리 앱」이라고 판단하면 안 되기 때문이다.

그리고 「심사 끝나면 되돌린다」고 적어 뒀다. 운영에 권한 열린 시험 계정을 남기지 않는다는 원칙이었다.

그런데 막상 그 시점이 되니 전제가 달라져 있었다. 심사는 한 번이 아니었다. 앱을 고쳐 올릴 때마다 다시 받는다. 그때도 심사자는 같은 계정으로 들어와 전곡을 들어야 한다. 지금 끄면 다음 판 낼 때 켜는 걸 잊기 쉽고, 잊으면 「앱에 접근할 수 없다」로 반려된다.

켜 둔 채 통계에서만 빼기로 했다. 실제 돈이 오가는 것도 아니고 표시 하나다.

  • 적어 둔 규칙도 전제가 바뀌면 다시 본다. 「끝나면 되돌린다」는 끝이 한 번일 때 맞는 말이었다.
  • 잊어서 사고가 나는 구조는 피한다. 사람이 매번 기억해야 도는 절차는 언젠가 안 돌아간다.

안 한다고 적은 것도 약속이다

판매 페이지에 「세금계산서는 발행되지 않는다」는 문장이 있었다. 읽다가 걸렸다.

결제를 대신 처리하는 업체가 판매자 역할을 맡는 구조라, 매장 입장에서 거래 상대가 우리가 아닐 수 있다. 그러면 서류도 그쪽이 낸다. 우리가 「발행하지 않는다」고 못 박아 두면, 실제로는 받을 수 있는 서류가 있을 때 우리 페이지만 틀린 말을 하게 된다.

게다가 대부분의 매장에겐 필요 없는 정보였다. 카드 결제 기록만으로도 비용 처리가 된다.

문장을 뺐다. 「영수증은 결제를 처리하는 곳에서 발급된다」만 남겼다.

  • 안 한다고 적는 것도 약속이다. 지킬 수 있는지 모르면 지키겠다는 말도, 안 하겠다는 말도 하지 않는다.
  • 파는 글에서 「~하지 않습니다」는 사실이어도 손해를 보게 한다. 사실이 아니면 더 나쁘다.

두 가지가 각각은 괜찮고 겹치면 나빴다

판매 페이지가 검색에 잡히는 상태였다. 그리고 웹 결제가 열려 있었다.

각각은 문제가 아니다. 검색에 잡히는 건 팔려면 당연하고, 결제가 열린 것도 팔려면 당연하다.

그런데 결제 경로를 우리가 아직 한 번도 끝까지 안 밟아 봤다. 그러면 「처음 밟는 사람이 손님」이 된다. 검색으로 누가 들어와 결제를 눌렀다가 실패하면 첫 손님을 그렇게 잃는다.

결제를 닫았다. 검색 노출은 그대로 둬도 앱은 정상으로 받을 수 있으니 손해가 적다.

  • 위험은 항목마다 보면 안 보인다. 두 상태가 겹쳤을 때 무슨 일이 생기는지를 본다.
  • 겹침을 풀 때는 손해가 적은 쪽을 끊는다. 둘 다 끄는 게 아니라 하나만 끊어도 된다.

되니까 옛길을 걷어냈다

비밀번호 재설정을 인증번호 방식으로 바꿔 끝까지 돌려 봤다. 되는 걸 확인하고 옛 링크 방식을 전부 지웠다.

남겨 둘까 잠깐 생각했다. 새 방식이 잘못되면 돌아갈 데가 있으니까. 그런데 길이 둘이면 고칠 때도 둘이고, 문제가 생겼을 때 어느 길로 들어온 건지부터 가려야 한다.

지운 건 새 길을 실제로 한 바퀴 돌아 본 뒤였다. 그 전에 지웠으면 안전망 없이 걷는 셈이었다.

  • 새 길이 되는 걸 확인하기 전에 옛길을 지우지 않는다. 확인한 뒤에는 미루지 않는다.
  • 안전망으로 남겨 둔 것은 쓰이지 않으면서 관리 비용만 낸다. 언제 걷을지 정해 두고 남긴다.

합치자는 말에 이유를 물었다

메일 설정을 정리하다가, 받는 것까지 한 곳으로 합칠까 생각했다. 이미 한쪽에 돈을 내고 있으니 거기로 모으는 게 깔끔해 보였다.

따져 보니 그 이유가 약했다. 옮겨도 돈이 안 굳는다 — 양쪽 다 이 용도로는 공짜다. 얻는 건 한 군데서 본다는 정돈감뿐이었다.

그런데 잃는 게 있었다. 지금은 메일함이 있어서 받은 게 거기 쌓이는데, 옮기면 전달만 하고 보관은 안 한다. 받는 쪽에 문제가 생기면 그 메일은 사라진다. 게다가 옮기는 순간이 유일한 위험 구간인데 하필 출시 직후였다.

안 옮기기로 하고, 대신 조건을 달아 뒀다 — 한쪽이 유료로 바뀌거나 관리가 실제로 번거로워지면 그때.

  • 합치는 이유가 정돈감뿐이면 합치지 않는다. 비용이 줄거나 못 하던 일이 되어야 옮길 이유다.
  • 「이미 돈을 내고 있으니」는 이유처럼 들리지만 이유가 아니다. 이미 낸 돈은 어느 쪽을 골라도 그대로다.

시험 계정 하나가 손님 수를 늘린다

재생 통계에서 우리 쪽 계정을 빼기로 했다. 손님이 아닌 것들을 골라 제외 목록에 넣었다.

그런데 그날 시험하려고 계정을 하나 더 만들어 뒀었다. 그건 목록에 없었다. 남겨 두기로 한 계정이라 그대로 두면 손님으로 세어진다.

넣었다. 그러면서 알았다 — 시험 계정을 만들 때마다 이 목록에 더해야 한다. 안 그러면 시험할 때마다 가짜 손님이 하나씩 는다.

  • 「우리 것 빼기」는 한 번 하고 끝나는 일이 아니다. 우리 것이 늘어날 때마다 같이 해야 한다.
  • 잊기 쉬운 자리는 코드와 문서 양쪽에 적어 둔다. 한 군데만 적으면 다른 걸 보는 사람이 놓친다.

새 기능이 오래된 고장을 꺼냈다

인증번호를 보내려고 메일 발송을 켰다. 그 과정에서 우리 도메인에 발신 서버 목록이 아예 없다는 걸 알았다. 그래서 업무 메일을 보내면 받는 쪽이 통째로 거부하고 있었다.

오늘 생긴 문제가 아니다. 계속 그랬고, 아무도 몰랐다. 우리가 그 주소로 메일을 자주 보낼 일이 없어서 눈에 안 띈 것이다.

새 기능을 붙이려고 같은 자리를 건드리자 그제야 드러났다.

  • 새 기능을 붙일 때 옆에 있던 것이 원래 고장 나 있는 경우가 있다. 내가 방금 깨뜨린 건지 원래 그랬던 건지부터 가른다.
  • 자주 안 쓰는 경로는 고장 나도 조용하다. 조용한 건 멀쩡한 것과 다르다.

요약

  • 「통과」와 「나갔다」 사이에 내가 눌러야 할 단계가 있는지 본다. 그 틈에서는 심사에 쓴 것을 되돌리지 않는다.
  • 앱과 서버는 따로 나간다. 되돌리기 어려운 조작 앞에서는 같은 판인지 한 번 더 묻는다.
  • 만드는 일정과 알리는 일정은 따로 잡는다. 출시일은 끝나는 날이 아니라 다른 일이 시작되는 날이다.
  • 적어 둔 규칙도 전제가 바뀌면 다시 본다. 사람이 매번 기억해야 도는 절차는 언젠가 안 돌아간다.
  • 안 한다고 적는 것도 약속이다. 지킬 수 있는지 모르면 아무 말도 하지 않는다.
  • 위험은 항목마다 보면 안 보인다. 겹쳤을 때를 보고, 풀 때는 손해가 적은 쪽을 끊는다.
  • 새 길을 한 바퀴 돌기 전에 옛길을 지우지 않고, 돌아 본 뒤에는 미루지 않는다.
  • 합치는 이유가 정돈감뿐이면 합치지 않는다. 「이미 돈을 내고 있으니」는 이유가 아니다.
  • 「우리 것 빼기」는 우리 것이 늘 때마다 같이 해야 한다.
  • 자주 안 쓰는 경로는 고장 나도 조용하다. 조용한 건 멀쩡한 것과 다르다.

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 10 79 | 습관만들기 Pawbit TIL 11 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 | 로그스톤 샵 LogStone Shop TIL 11 231 | 룸톤 Roomtone TIL 17 232 | 룸톤 Roomtone TIL 18 233 | 메트로놈 Simple Metronome TIL 12 234 | 콘티온 ContiOn TIL 12 235 | 소리꽃 KeyBloom TIL 33 236 | 온실 GreenHouse TIL 3 237 | 로그스톤 샵 LogStone Shop TIL 12 238 | 룸톤 Roomtone TIL 19 239 | 모래게임 Sandrop TIL 35 240 | 여러 세션을 INBOX 하나로 굴리기 241 | 궁 미로 PalaceMaze TIL 5 242 | CS2 Practice Host TIL 1 243 | 룸톤 Roomtone TIL 20 244 | 여둘까 Office Layout TIL 14 245 | 로그스톤 샵 LogStone Shop TIL 13 246 | 룸톤 Roomtone TIL 21 읽는 중