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

광고를 붙이기 전에

자리를 먼저 만들어 두면 연결은 콜백 하나로 끝난다

광고 단위를 받기 전에 “광고 보고 이어하기”, “광고 보고 코인 두 배” 버튼을 비활성 상태로 먼저 만들어 뒀다. 나중에 실제 계정이 생겼을 때 버튼에 동작만 달면 끝이었다.

  • 외부 서비스에 의존하는 기능은 자리를 먼저 만들어 두면 붙이는 날이 짧아진다. 그 사이 동선과 문구도 미리 확인된다
  • 자리를 안 만들어 두면 붙이는 날에 화면 설계까지 같이 해야 한다

보상은 끝까지 본 사람에게만 준다

광고를 중간에 닫아도 보상이 나가면 아무도 끝까지 안 본다. 광고를 다 봤을 때만 불리는 신호가 따로 있어서, 보상 지급을 그 안에만 뒀다.

광고가 아직 준비 안 됐으면 그냥 실패로 두고 “잠시 후 다시”만 안내한다.

  • 대가를 주는 조건은 가장 안쪽에 둔다. 바깥에 두면 중간에 빠져나가는 경로가 생긴다
  • 광고가 없다고 게임이 막히면 안 된다. 광고는 부가지 관문이 아니다

개발 중에 실제 광고를 누르면 계정이 정지된다

개발하면서 자기 광고를 클릭하면 부정 클릭으로 잡혀 계정이 정지될 수 있다. 그래서 개발 빌드에서는 테스트용 광고만 뜨고, 실제 것은 출시 빌드에만 나가게 갈랐다.

  • 광고를 붙일 때 제일 먼저 할 일이 이 분리다. 나중에 하면 이미 몇 번 눌러 본 뒤다
  • 광고 단위 이름 자체는 앱 안에 그대로 담기는 공개 값이라 감출 것이 아니다. 감춰야 할 것과 아닌 것을 구분해야 엉뚱한 데 힘을 안 쓴다

빈도를 안 조이면 광고가 이탈을 만든다

전면 광고가 매 판 뜨면 사람이 나간다. 몇 판에 한 번만 뜨게 막고, 광고 제거를 산 사람에게는 아예 안 띄운다.

  • 광고는 넣는 것보다 얼마나 자주 띄우느냐가 더 큰 결정이다. 수익과 이탈이 같은 손잡이에 걸려 있다

서버 없이 하는 것들

하루 몇 번인지 서버 없이 셀 수 있다

“광고 보고 코인 받기”를 하루 세 번으로 막고 싶었다. 서버가 없으니 기기 안에서 판정해야 한다.

기록에 시각이 남아 있으니, 그날 자정 이후의 기록만 세면 된다. 코인을 줄 때 횟수 기록도 같이 남긴다.

  • 서버가 있어야 되는 줄 알았던 것 중 상당수가 기기 안에서 된다. 무엇이 진짜 서버를 요구하는지 먼저 가른다
  • 기기 시각을 믿는 만큼 조작될 수 있다. 이 정도 보상에서는 감수할 만하다고 봤다

가격은 앱에 박지 않는다

결제 상품 가격을 앱에 적어 두면 나라마다 통화가 다르고, 심리적 가격대도 다르다. 스토어가 가격대별로 각 나라 통화로 알아서 바꿔 주므로, 화면에는 스토어가 주는 문자열을 그대로 띄우면 된다.

  • 바깥에서 정해 주는 값을 앱에 복사해 두면 두 곳이 갈라진다. 받아서 그대로 쓰는 편이 낫다

한 번 고친 자리가 다시 샜다

기다릴 대상을 바꿨다

레벨을 이어서 여러 판 하고 나가면 목록에 클리어 표시가 안 떴다. 앞서 한 번 고친 자리인데 또 샜다.

목록은 “게임 화면을 띄우고, 그게 닫히면 새로고침”하는 구조였다. 그런데 다음 레벨로 넘어갈 때는 화면을 새로 얹는 게 아니라 지금 화면을 새 레벨로 갈아 끼운다. 갈아 끼우는 순간 시스템이 치워지는 화면에 “끝났다”는 신호를 보낸다. 그래서 목록은 첫 판에서 이미 새로고침을 해 버리고, 정작 진짜로 돌아왔을 때는 아무도 기다리고 있지 않았다.

기다릴 대상을 “게임이 끝나면”에서 “이 목록으로 돌아오는 순간”으로 바꿨다. 화면 전환을 지켜보는 감시자를 하나 두고 목록이 거기 구독하면, 위에 있던 화면이 닫히고 돌아올 때마다 신호가 온다. 중간에 몇 번을 갈아 끼웠든 상관없다.

  • 같은 자리가 두 번 새면 고친 방법이 아니라 기다리는 대상이 틀린 것이다
  • “무엇이 끝나면”보다 “무엇이 돌아오면”이 안전한 경우가 있다. 앞엣것은 중간 경로가 바뀌면 깨지고 뒤엣것은 안 깨진다

탭을 오가도 자리를 잃지 않게

홈·상점·설정을 아래 탭으로 두면서, 탭을 옮겨도 각 화면의 스크롤 위치가 남아 있게 했다. 돌아왔더니 맨 위로 올라가 있으면 다시 찾아 내려가야 한다.

  • 탭은 화면을 바꾸는 게 아니라 자리를 옮기는 것이다. 돌아왔을 때 그대로여야 그렇게 읽힌다

보여 주는 타이밍

이미 준 것을 늦게 보여 준다

승리 보상 코인은 판을 깬 시점에 이미 지급된다. 그런데 코인 숫자가 처음부터 올라가 있으면 코인이 날아와 쌓이는 느낌이 안 난다.

날아오기 전까지는 보상만큼 뺀 값을 보여 주고, 도착하면 진짜 값으로 올렸다.

  • 계산이 끝난 것과 사람이 그걸 받아들이는 것은 다른 시점이다. 연출은 뒤엣것에 맞춘다
  • 숫자를 늦게 보여 주는 것은 속이는 게 아니다. 어차피 곧 같아진다

색을 자동으로 정할 때

흰색이 그림의 요소인지 먼저 판정한다

초밥 도안에서 밥과 윤기가 회색이고 배경이 거의 흰색이라 어색했다. 색을 줄이는 과정이 배경을 가장 많은 색의 옅은 톤으로 잡는데, 초밥은 그게 흰색에 가까웠다. 밥은 원래 회색이라 그대로 남았고, 결과적으로 흰 배경에 회색 밥이 됐다.

흰색이 그 그림의 실제 요소인지 판정하는 갈래를 넣었다. 가장 많은 색이 흰색에 가깝거나 흰색 계열이 일정 면적을 넘으면, 그 부분을 흰색으로 끌어올리고 배경은 채도 있는 파스텔로 잡는다. 작은 반사광만 있는 그림은 건드리지 않는다.

  • 자동으로 색을 정하는 규칙에는 “이 색이 의미가 있는 색인가”를 묻는 갈래가 필요하다. 평균적으로 맞아도 특정 종류에서 뒤집힌다
  • 생성이 같은 입력에 같은 결과를 내게 해 두면 이런 수정을 겁 없이 할 수 있다. 다시 만들어도 그림과 난이도는 그대로고 색만 바뀐다

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