룸톤 Roomtone TIL 12
만나서 계약하는 게 아니라고 했더니 할 일 목록이 다시 줄을 섰다
그동안 문서와 결정에는 「매장을 직접 만나 계약하는 B2B」라는 전제가 깔려 있었다. 오늘 그게 틀렸다고 바로잡았다. 매장이 스토어에서 앱을 찾아 알아서 가입하고 결제해 쓰는 구조다.
한 문장을 고쳤을 뿐인데 그 위에 올라가 있던 판단들이 줄줄이 흔들렸다.
- 30초 미리듣기가 「있으면 좋은 것」에서 필수가 됐다. 직접 만나면 들려주면 되지만, 혼자 들어온 사람에게는 체험이 곧 판매 창구다.
- 가입한 매장끼리 데이터를 가르는 일이 「두 번째 고객 받기 전에」에서 출시 차단 요소로 올라갔다. 고객을 한 명씩 들이면 두 번째가 언제 올지 우리가 정하지만, 셀프 가입이면 정할 수 없다. 출시 전에 막았다.
- 앱 안 결제 동선이 필요해졌다. 「만나서 계약하니 앱에서 결제할 일이 없다」가 앱 밖 판매를 정한 근거였기 때문이다.
- 스토어 검색에서 이름이 겹치는 것이 신경 쓸 일이 됐다. 직접 파는 구조였을 땐 검색이 의미가 없었다.
기술 결정은 대개 사업 전제 위에 서 있는데, 그 전제는 문서에 한 줄로 적혀 있어서 틀려도 눈에 잘 안 띈다. 전제를 고칠 때는 그 문장을 근거로 삼은 결정이 어디어디 있는지 같이 찾아야 한다. 오늘은 문서와 예전 로그에서 「직접 만나」라는 표현을 전부 찾아 하나씩 다시 봤다.
결제는 두 곳인데 왜 세 갈래냐고 물었다
앞선 전제가 무너지니 결제를 어디서 받을지를 다시 열어야 했다. 앞선 TIL 9에서는 수수료와 세금계산서를 따져 「앱 밖에서 팔면 스토어 결제 규칙에 안 걸린다」로 정했다. 그런데 앱에 누를 수 있는 결제 버튼을 아예 두지 않아도 된다고 본 데에는 「만나서 계약한다」가 함께 깔려 있었다.
선택지를 펼쳐 놓고 「다 하자」고 정했다. 그런데 정리된 표에 「세 갈래」라고 적혀 있어서 되물었다. Creem(해외 세금·정산을 대신 처리하는 결제 대행사) 아니면 앱 안 결제, 두 개 아니냐고.
맞는 말이었다. 돈을 받는 곳은 둘이다.
| 경로 | 돈을 받는 곳 | 결제를 시작하는 곳 | 수수료 |
|---|---|---|---|
| 웹 직접 | Creem | 웹 페이지 | 0% |
| 앱 → 웹 링크 | Creem | 앱의 링크 | 구독 약 10% + 거래 보고 |
| 앱 안 결제 | Google Play | 앱 | 10~15% |
셋으로 센 이유는 위 두 줄이 같은 Creem 결제인데도 앱에서 출발하면 플레이가 수수료를 떼기 때문이었다. 결제처로 세면 둘, 비용으로 세면 셋이다. 앞선 TIL 9에서 「무엇으로 받느냐가 아니라 어디서 받느냐」라고 적었던 바로 그 차이인데, 표에는 무엇을 기준으로 센 숫자인지가 빠져 있어서 헷갈렸다.
같은 목록도 무엇을 기준으로 세느냐에 따라 개수가 달라진다. 세 갈래를 다 연 이유는 결제처가 셋이어서가 아니라, 매장마다 원하는 게 달라서다. 세금계산서가 필요한 곳은 웹으로, 편한 걸 원하는 곳은 앱 안 결제로 스스로 고르게 했다.
플레이 결제를 넣었더니 약관의 무환불과 부딪혔다
앱 안 결제를 붙이고 나서 게시된 약관과 개인정보처리방침을 다시 대조했다. 약관에는 「이미 결제한 금액은 환불하지 않는다」가 적혀 있다. 무환불은 연 선불 분쟁을 막으려고 일부러 넣은 조항이다.
그런데 플레이로 산 구독은 환불을 우리가 정하지 않는다. 구글이 자기 환불 정책에 따라 사용자 요청을 받아 처리한다. 우리 약관이 「환불 안 됨」이라고 해도 플레이 쪽에서는 환불이 일어날 수 있고, 두 문서가 서로 다른 말을 하게 된다.
그래서 무환불 문장을 웹(Creem) 결제에 한정하고, 플레이 결제는 플레이 환불 정책을 따른다고 적어 달라고 약관을 관리하는 쪽에 요청했다. 해지 방법도 경로마다 다르다(웹은 결제 확인 메일의 관리 링크, 플레이는 스토어의 구독 메뉴).
결제 경로를 하나 늘리면 그 경로를 운영하는 회사의 규칙이 우리 약관 안으로 들어온다. 수수료만 보고 경로를 늘리면 안 되고, 환불·해지·개인정보 조항이 그 경로에서도 참인지 같이 확인해야 한다. 같은 이유로 개인정보처리방침에도 새로 모으는 정보(플레이 구매 기록, 동시 재생을 판정하는 기기 번호, 약관 동의 시각)를 넣어야 했다. 스토어 심사는 이 문서와 앱이 실제로 모으는 정보를 대조한다.
소리는 나는데 재생 바가 비어 있었다
추천 재생목록을 틀었더니 하단 재생 바에 「재생 중이 아닙니다」가 떴다. 그렇게 알렸더니 소리가 나긴 하냐고 되물어 왔고, 소리는 난다고 답했다. 이 대답 하나로 원인이 확 좁혀졌다. 재생은 멀쩡하고, 「지금 무슨 곡인지」를 화면에 알리는 쪽만 고장 난 것이다.
원인은 재생 라이브러리(just_audio — Flutter 앱에서 소리를 내는 패키지)의 재생 명령이 이름과 달리 「재생이 시작되면」이 아니라 「곡이 끝나거나 멈추면」 끝나는 명령이었다는 데 있었다. 앱은 그 명령이 끝나기를 기다린 다음에야 지금 곡을 적고 있었고, 그래서 곡이 끝날 때까지 바가 비어 있었다.
더 중요한 건 같은 원인이 조용히 망가뜨리고 있던 게 더 있었다는 점이다.
- 둘째 곡부터 다음 곡 버튼이 안 먹었다.
- 첫 곡에서는 일시정지가 안 먹었다.
- 정해진 시각에 재생목록을 바꾸는 정시 전환 뒤에, 다음 전환 예약이 그 곡이 끝날 때까지 걸리지 않았다.
마지막 것은 예전에 「정시 전환 된다」고 확인했던 기능이다. 그때는 전환되는 순간만 봤고, 전환 뒤에도 다음 전환이 제때 걸리는지는 보지 않았다.
여기서 두 가지를 가져간다. 하나는 증상을 알릴 때 「무엇이 되고 무엇이 안 되는지」를 갈라 말하면 원인이 반으로 줄어든다는 것이다. 「안 돼」와 「소리는 나는데 표시가 안 돼」는 전혀 다른 곳을 가리킨다. 다른 하나는 눈에 띄는 이상한 증상 하나 뒤에 같은 뿌리의 조용한 고장이 여러 개 숨어 있을 수 있다는 것이다. 기능을 확인할 때는 그 순간만 보지 말고, 그다음 동작이 이어서 제대로 도는지까지 본다.
요약
- 사업 전제 한 줄이 바뀌면 그 위의 기술 결정들이 연달아 흔들린다. 전제를 고칠 때는 그 문장을 근거로 삼은 결정을 같이 찾는다.
- 앞선 결론이 기대던 기둥이 무너지면 결론도 다시 연다. 「앱 밖에서만 판다」에는 수수료·증빙 말고 「만나서 계약한다」도 함께 깔려 있었다.
- 같은 목록도 기준에 따라 개수가 다르다. 결제처로 세면 둘, 비용으로 세면 셋이다.
- 결제 경로를 늘리면 그 경로를 운영하는 회사의 규칙이 약관 안으로 들어온다. 환불·해지·개인정보 조항이 새 경로에서도 참인지 확인한다.
- 증상은 「무엇이 되고 무엇이 안 되는지」로 갈라 말하면 원인이 좁혀진다.
- 이상한 증상 하나 뒤에 같은 뿌리의 조용한 고장이 여럿 있을 수 있다. 확인은 그 순간이 아니라 다음 동작까지 본다.