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

별점과 안내를 언제 띄울까

별점은 앱이 받는 게 아니었다

다른 게임을 하다 보면 별점이나 댓글을 부탁하는 창이 뜬다. 그걸 보고 우리도 게임을 잘 즐기고 있는 사람에게 별점을 달라는 창을 띄워 볼까 싶었다. v1.3.0에 넣기로 하고 알아보니 앱이 자기 화면으로 별점을 받을 수는 없었다. 인앱 리뷰(게임을 나가지 않고 스토어 별점 카드를 띄우는 기능)를 부르면 스토어가 자기 카드를 띄우고 점수도 스토어가 가져간다.

제약이 셋이었다. 앱은 카드가 떴는지도 몇 점인지도 모른다. 부르는 횟수를 스토어가 제한해서 불러도 조용히 안 뜰 수 있다. “재밌으셨나요?”로 먼저 묻고 좋다는 사람만 카드로 보내는 것(리뷰 게이팅)은 정책 위반이다.

남는 건 하나였다. 좋은 순간에, 조건 없이, 한 번 부른다. 불러도 안 뜰 수 있으니 설정에 언제든 별점을 줄 수 있는 줄을 따로 뒀다.

  • 제약을 먼저 모으면 남는 선택지가 몇 개인지 보인다. 이번엔 하나라 고민할 게 없었다
  • 막힐 수 있는 길이 유일하면 안 된다. 대신할 통로를 하나 더 둔다
  • 만족한 사람만 골라 물을 수 없으니 광고가 거슬리는 사람도 걸린다. 할 수 있는 건 판을 깬 직후, 광고가 아닌 순간에 묻는 것뿐이다

선두는 150탄인데 30탄 앞뒤에 있는 사람도 있었다

처음 구상은 50탄쯤이었다. 충분히 해 본 사람에게 묻는 게 맞아 보였다.

그런데 앞선 TIL 33에서 저장해 둔 쿼리로 보니 29탄·38탄에 있는 사람이 있었고, 선두는 150탄까지 가 있었다. 50탄이면 선두 한 명에게만 묻는다. 그래서 별점은 30탄쯤에 묻자고 했다. 표본은 여전히 아는 사람 몇 명이지만 기준을 어디에 둘지 가늠하는 데는 충분했다.

  • 물어보는 기능은 물어볼 수 있는 사람이 몇 명인지가 기준을 먼저 정한다. “충분히 해 본 사람”은 그다음 조건이다
  • 데이터를 안 봤으면 50으로 냈을 것이다

클라우드 저장 안내도 15탄으로 당겼다

진행 상황 클라우드 저장(폰을 바꾸거나 다시 깔아도 이어서 하게 해 주는 기능)을 권하는 안내는 30탄에 떴다. 별점을 30탄쯤에 물을 거라 이 안내는 좀 당기자고 했다. 15탄으로 당기고, 그때 연결 안 한 사람에게는 60탄에서 한 번 더 권하기로 했다.

이 안내가 실제로 하는 일은 “쌓인 것을 지킨다”보다 “여기서부터 안전해진다”에 가깝다. 일찍 연결할수록 지켜지는 양이 많고, 연결하면 주는 보상도 코인이 모자란 초반에 값어치가 크다.

15·30·60탄을 이미 지나친 사람은 안내 셋이 한꺼번에 걸린다. 연달아 띄우면 한 판 깨고 전체 화면을 세 번 보게 되니, 한 번에 하나만 띄우고 나머지는 다음 판으로 미룬다.

  • 안내 시점은 무엇을 막아 주는지보다 언제부터 효과가 나는지로 잡는다
  • 안내가 겹치는 자리를 따로 본다. 하나씩은 괜찮아도 한꺼번에 오면 방해가 된다

버벅임을 재다가

그리는 횟수를 줄였더니 더 끊겼다

플레이하다 보니 게임 화면이 버벅였다. Claude에게 그렇게 말하고 프레임 시간(화면 한 장을 그리는 데 걸린 시간)을 재 보기로 했다. 앞선 TIL 27에서 재려다 도구가 게임 화면을 못 봐서 못 잰 그 값이다. 이 폰은 화면을 초당 120번 새로 그려서(120Hz) 한 장에 쓸 수 있는 시간(프레임 예산)이 8.3ms인데, 실제로는 한 장에 30ms 남짓 들고 있었다.

모래 게임에 초당 120번은 필요 없어 보여 60번으로 걸러 봤다. 숫자로는 평균이 좋아졌는데, 플레이해 보니 모래가 뚝뚝 끊기며 움직였다. 이런 끊김을 jank(프레임 간격이 고르지 않아 화면이 끊겨 보이는 것)라고 한다. 기록을 보니 가장 느린 프레임들은 오히려 더 느려져 있었다. 일을 절반으로 나눈 게 아니라, 아무것도 안 하는 프레임과 두 배로 몰아서 하는 프레임으로 갈라 놓은 것이었다. 되돌렸다.

  • 매끄러움은 평균이 아니라 가장 느린 프레임이 정한다. 눈은 빠른 것보다 고른 것을 매끄럽다고 느낀다
  • 횟수를 줄이는 건 값을 옮기는 것이지 없애는 게 아니다. 한 장에 30ms가 들면 초당 몇 번을 그리든 끊긴다

고친 쪽이 이겼는데 판이 달랐다

남은 길은 한 장의 값을 줄이는 것이라, 그림 영역을 그리는 방식을 바꿨다. 고친 것과 안 고친 것을 나란히 쟀더니 고친 쪽이 이겼다.

결과를 보니 두 번의 플레이가 서로 다른 판이었다. 하나는 어려움, 하나는 보통. 판마다 통과 색의 개수가 달라 그릴 양 자체가 다르다. 숫자 차이가 고쳐서인지 판이 쉬워서인지 가를 수가 없었다.

그런데 이 게임은 지나간 판을 다시 못 한다. 같은 판을 두 번 잴 방법이 애초에 없었다. 재는 빌드에서만 지나간 판을 다시 할 수 있게 열고 같은 74탄으로 다시 쟀다. 한 장이 30.5ms에서 20.6ms로 줄어 있었다.

  • 비교 결과가 마음에 들수록 조건이 같았는지 다시 본다
  • 제품이 막아 둔 것이 측정도 막는다. 같은 조건을 어떻게 만들지가 재는 방법보다 먼저다

배너 하나가 8ms였다

앞선 TIL 30에서 배너를 한 번만 만들게 고쳤지만, 떠 있는 것만으로 드는 값은 따로 있었다. 남은 값을 더 찾으려고 하단 배너 광고를 꺼 봤다. 20.6ms가 10~15ms로 떨어졌다. 배너는 앱이 아니라 시스템이 따로 그리는 화면(플랫폼 뷰)이라, 떠 있으면 게임 화면을 한 번 더 베껴 합쳐야 하고 그 값이 매 프레임 든다. 그림 영역을 뜯어고쳐 번 게 10ms인데 배너 하나가 8ms를 먹고 있었다. 배너를 두면 초당 60장의 예산(16.7ms) 안에 못 들어오고, 빼면 들어온다.

배너는 남기기로 했다. 광고 수익과 부드러움을 저울에 올리는 문제고, 기술로 정할 일이 아니다. 되돌리는 비용이 거의 없어서 수익 데이터를 보고 다시 정할 수 있다. 그때 선택지는 셋이다 — 그대로 두기, 아예 빼기, 게임 화면에서만 빼고 목록·상점에는 두기.

  • 큰 값은 예상 밖에 있을 수 있다. 고칠 곳부터 정하고 재면 못 본다. 하나씩 꺼 보는 게 가장 확실하다
  • 잰 숫자가 결정을 대신하지는 않는다. 숫자는 대가를 보여 줄 뿐이고, 무엇을 택할지는 다른 저울이다
  • 되돌리기 싼 결정은 지금 정하고, 다시 볼 선택지를 같이 적어 둔다

뜨겁다는 사람과 괜찮다는 사람

그 뒤 테스트해 준 사람이 폰이 뜨거워진다고 했다. 그런데 해 본 다른 사람은 자기 폰은 괜찮다고 해서, 말이 엇갈리는 채로 발열은 아직 풀지 못하고 남겨 뒀다.

앞선 TIL 27에서 성능 항목을 보류로 내리며 다시 볼 조건으로 “발열 불만이 실제로 들어올 때”를 적어 뒀는데, 그게 온 셈이다.

  • 보류할 때 적어 둔 조건이 오면 그때 다시 연다. 말이 엇갈려도 조건은 이미 채워졌다

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 5 82 모래게임 Sandrop TIL 4 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 모래게임 Sandrop TIL 11 99 소리꽃 KeyBloom TIL 11 100 여둘까 Office Layout TIL 5 101 모래게임 Sandrop TIL 12 102 모래게임 Sandrop TIL 13 103 콘티온 ContiOn TIL 4 104 모래게임 Sandrop TIL 14 105 소리꽃 KeyBloom TIL 12 106 습관만들기 Pawbit TIL 14 107 모래게임 Sandrop TIL 15 108 모래게임 Sandrop TIL 16 109 소리꽃 KeyBloom TIL 13 110 온실 GreenHouse TIL 1 111 콘티온 ContiOn TIL 5 112 모래게임 Sandrop TIL 17 113 소리꽃 KeyBloom TIL 14 114 온실 GreenHouse TIL 2 115 콘티온 ContiOn TIL 6 116 모래게임 Sandrop TIL 18 117 콘티온 ContiOn TIL 7 118 모래게임 Sandrop TIL 19 119 소리꽃 KeyBloom TIL 15 120 모래게임 Sandrop TIL 20 121 모래게임 Sandrop TIL 21 122 소리꽃 KeyBloom TIL 16 123 소리꽃 KeyBloom TIL 17 124 소리꽃 KeyBloom TIL 18 125 여둘까 Office Layout TIL 6 126 소리꽃 KeyBloom TIL 19 127 소리꽃 KeyBloom TIL 20 128 콘티온 ContiOn TIL 8 129 콘티온 ContiOn TIL 9 130 로그스톤 상표 셀프 출원 TIL 1 131 소리꽃 KeyBloom TIL 21 132 로그스톤 샵 LogStone Shop TIL 1 133 소리꽃 KeyBloom TIL 22 134 로그스톤 샵 LogStone Shop TIL 2 135 소리꽃 KeyBloom TIL 23 136 모래게임 Sandrop TIL 22 137 모래게임 Sandrop TIL 23 138 소리꽃 KeyBloom TIL 24 139 로그스톤 샵 LogStone Shop TIL 3 140 모래게임 Sandrop TIL 24 141 소리꽃 KeyBloom TIL 25 142 모래게임 Sandrop TIL 25 143 MiniMacro TIL 1 144 모래게임 Sandrop TIL 26 145 콘티온 ContiOn TIL 10 146 Astro가 무엇인지, 왜 옮기는지 147 모래게임 Sandrop TIL 27 148 소리꽃 KeyBloom TIL 26 149 MiniMacro TIL 2 150 로그스톤 샵 LogStone Shop TIL 4 151 메트로놈 Simple Metronome TIL 8 152 모래게임 Sandrop TIL 28 153 여둘까 Office Layout TIL 7 154 메트로놈 Simple Metronome TIL 9 155 블로그 Astro 이관 TIL 156 여둘까 Office Layout TIL 8 157 로그스톤 샵 LogStone Shop TIL 5 158 모래게임 Sandrop TIL 29 159 오픈데이 Openday TIL 1 160 로그스톤 샵 LogStone Shop TIL 6 161 모래게임 Sandrop TIL 30 162 오픈데이 Openday TIL 2 163 모래게임 Sandrop TIL 31 164 Unreal Engine MCP TIL 1 165 모래게임 Sandrop TIL 32 166 소리꽃 KeyBloom TIL 27 167 콘티온 ContiOn TIL 11 168 오픈데이 Openday TIL 3 169 로그스톤 샵 LogStone Shop TIL 7 170 모래게임 Sandrop TIL 33 171 소리꽃 KeyBloom TIL 28 172 오픈데이 Openday TIL 4 173 여둘까 Office Layout TIL 9 174 오픈데이 Openday TIL 5 175 ScorePlayer TIL 1 176 모래게임 Sandrop TIL 34 읽는 중 177 BuildingHub TIL 1 178 ScorePlayer TIL 2 179 여둘까 Office Layout TIL 10 180 BuildingHub TIL 2 181 ScorePlayer TIL 3 182 Unreal Engine MCP TIL 2 183 궁 미로 PalaceMaze TIL 1 184 메트로놈 Simple Metronome TIL 10 185 메트로놈 Simple Metronome TIL 11 186 오픈데이 Openday TIL 6 187 BuildingHub TIL 3 188 Obsidian vault에서 코드만 빼기 — directory junction 189 ScorePlayer TIL 4 190 여둘까 Office Layout TIL 11 191 오픈데이 Openday TIL 7 192 URL은 바뀔 수 있는 것에 묶지 않는다 193 도구는 기능이 아니라 내 일에 맞는지로 고른다 194 돌이키기 힘든 변경은 작게 먼저 확인한다 195 궁 미로 PalaceMaze TIL 2 196 궁 미로 PalaceMaze TIL 3 197 소리꽃 KeyBloom TIL 29 198 오픈데이 Openday TIL 8 199 BuildingHub TIL 4 200 ScorePlayer TIL 5 201 로그스톤 샵 LogStone Shop TIL 8 202 여둘까 Office Layout TIL 12 203 오픈데이 Openday TIL 9 204 BuildingHub TIL 5 205 MiniMacro TIL 3 206 소리꽃 KeyBloom TIL 30 207 오픈데이 Openday TIL 10 208 여둘까 Office Layout TIL 13 209 소리꽃 KeyBloom TIL 31 210 궁 미로 PalaceMaze TIL 4 211 로그스톤 샵 LogStone Shop TIL 9 212 로그스톤 샵 LogStone Shop TIL 10 213 소리꽃 KeyBloom TIL 32