74%가 하루 만에 떠나던 자리에 도장을 놓았습니다 — 화조당 출석 크레딧
가입자 69명 중 51명이 그날만 쓰고 떠났고, 25명은 가입 선물을 한 번도 쓰지 않았습니다. 하루 만에 배포 세 번 — 홈에 입력창을 놓고, 오늘의 기운을 로그인 밖으로 꺼내고, 매일 1크레딧·이레에 10크레딧을 주는 출석 도장을 붙였습니다. 트랜잭션 없는 DB에서 중복 지급을 막은 방법도 적었습니다.
가입한 사람 69명 중 51명이 가입한 그날만 쓰고 다시 오지 않았습니다. 그리고 25명은 가입 선물로 드린 20크레딧을 한 번도 쓰지 않고 떠났습니다.
돌아올 이유도 없고, 받은 것을 쓸 이유도 없었던 겁니다. 오늘 하루 그 두 가지를 메우는 작업만 했습니다. 배포 세 번이 나갔습니다.
30일치 숫자가 가리킨 곳
| 구간 | 숫자 |
|---|---|
| 방문자 | 254명 |
| 홈에 착지 | 82.7% |
| 첫날 한 페이지만 보고 이탈 | 42.9% |
| 로그인 페이지까지 와서 완주 | 118명 중 43명 (63.6% 이탈) |
| 가입 당일만 활동 | 69명 중 51명 (74%) |
| 가입 보너스 미사용 | 25명 |
앞의 셋은 들어오다 새는 것이고, 뒤의 둘은 들어와서 안 돌아오는 것입니다. 순서대로 막기로 했습니다.
1. 새는 곳부터 — 홈에 입력창을 놓았습니다
방문의 82.7%가 홈에 떨어지는데, 홈에는 버튼 두 개밖에 없었습니다. 볼 것을 주지 않고 고르라고만 한 셈입니다.
비로그인 방문자에게 버튼 대신 생년월일 입력창을 바로 줬습니다. 넣으면 결과 화면이 그 값을 집어 곧장 명식을 띄웁니다. 홈 → 사주 → 홈으로 오가던 왕복 클릭이 사라졌습니다.
무료 명식 결과 끝에는 오늘의 기운 카드와 잠긴 주제 7개를 붙이고, 가입 보너스를 "20크레딧"이 아니라 "전체 풀이 한 번을 무료로" 라고 바꿔 썼습니다. 25명이 안 쓴 이유가 값을 몰라서가 아니라 선물인 줄 몰라서였다고 보았기 때문입니다.
로그인에서 63.6%가 포기하는 이유는 아직 모릅니다. 오늘은 원인을 알아낼 계측만 심었습니다. 모르는 것을 안다고 말하고 고치는 것이 제일 위험합니다.
2. 매일 올 통로 — 로그인 밖으로 꺼낸 「오늘의 기운」
명식은 평생 한 번 보면 끝입니다. 그런데 일진은 매일 바뀝니다. 다시 올 이유가 되는 건 이쪽인데, 이 카드가 로그인 뒤에 숨어 있어서 처음 온 사람은 존재조차 몰랐습니다.
/today를 로그인 밖으로 꺼냈습니다. 비로그인 방문자의 생년월일은 30일간 기억해 다음에 올 때 다시 묻지 않습니다. 매일 오라고 만든 화면이 올 때마다 생년월일을 물으면 아무도 오지 않으니까요.
3. 다시 올 이유에 값을 붙였습니다 — 출석 도장
매일 1크레딧(유나와 한 마디), 이레를 채우면 10크레딧(상세운세 한 번)을 더 드립니다. 2주면 전체 풀이 하나가 모입니다.
돌아올 이유와 크레딧을 쓸 이유를 한자리에 붙인 것입니다. 계산은 전부 로컬에서 하므로 AI 호출이 없고, 따라서 원가가 0입니다.
여기서 제일 조심한 것 — 하루에 두 번 주지 않기
저희가 쓰는 데이터베이스 드라이버(neon-http)는 트랜잭션을 쓸 수 없습니다. 그래서 흔한 방식대로 짜면 사고가 납니다.
(흔한 방식) 오늘 찍었나? → 안 찍었네 → 넣고 크레딧 지급
같은 순간에 요청 두 개가 들어오면 둘 다 "안 찍었네"를 보고 둘 다 지급합니다. 버튼을 두 번 빠르게 누르는 것만으로 크레딧이 두 배가 됩니다.
그래서 순서를 뒤집었습니다.
(바꾼 방식) 일단 넣어본다 (UNIQUE user_id+day)
→ 행이 돌아왔으면 = 내가 넣은 것 → 그때만 지급
→ 빈 값이 돌아왔으면 = 남이 먼저 넣음 → 아무것도 안 함
지갑을 여는 열쇠를 데이터베이스의 유니크 제약 하나로 몰아넣은 것입니다. 은행 창구에서 번호표를 먼저 뽑게 하는 것과 같습니다. 번호표가 하나뿐이면 둘이 동시에 손을 뻗어도 한 명만 받습니다.
같은 날 두 번 누르는 경우와 동시 요청 두 개가 들어오는 경우를 테스트로 재현해서, 지급이 정확히 한 번인지 확인했습니다.
날짜는 전부 한국 기준입니다
서버는 세계 표준시(UTC)로 돕니다. 그대로 쓰면 한국 자정 직후에 찍은 출석이 어제 것으로 기록되어, 하루도 안 빼먹었는데 연속이 끊긴 것처럼 보입니다. 오늘 오전에 「오늘의 기운」에서 똑같은 함정을 하나 고쳤고(한국 00~09시에 어제 일진이 나가고 있었습니다), 도장에서는 처음부터 한국 날짜로 못박았습니다.
조회가 실패해도 화면은 열립니다
도장 칸을 읽다가 문제가 생기면 그 칸만 빠지고 오늘의 기운과 상품 선반은 그대로 열립니다. 부속 하나 때문에 본체가 멈추면 안 됩니다.
덤으로 만든 것 — 마이그레이션 적용기
데이터베이스 구조를 바꾸는 SQL을 지금까지는 사람이 콘솔에 붙여 넣었습니다. 그러다 보니 파일에 적힌 것과 실제로 적용된 것이 어긋나도 알 방법이 없었습니다. 파일을 그대로 실행하는 도구를 만들었고, 기본은 미리보기입니다. 붙는 곳이 운영 중인 데이터베이스라, 한 번 더 누르는 것보다 한 번 더 확인하는 쪽이 쌉니다.
오늘 있었던 사고 — 작업 세션이 38분 멈췄습니다
마이그레이션 파일을 쓴 직후 작업 창이 응답을 멈췄습니다. 에러도 없고 프로세스도 살아 있는 채로였습니다.
되짚는 순서는 이렇습니다.
- 작업 기록 파일의 마지막 기록 시각과 파일이 마지막으로 저장된 시각을 대조합니다. 둘이 같은 지점에서 멈춰 있으면 크래시가 아니라 대기입니다.
- 실행 중인 프로세스를 봅니다. 이때 부모 프로세스까지 봐야 합니다. 같은 시각에 멈춰 있던 셸 세 개는 알고 보니 다른 프로젝트(유튜브 영상 작업)의 것이었습니다. 무관한 것을 범인으로 지목할 뻔했습니다.
- 작업물의 글자 인코딩을 확인합니다. 이 PC에서는 한글이 셸을 거치며 깨지는 일이 있어서, 멀쩡한 파일을 깨진 줄 알고 "고치다가" 진짜로 깨뜨리는 사고가 더 무섭습니다.
결과적으로 잃은 것은 없었습니다. 파일 네 개 전부 온전했고 한글도 정상이었습니다. 이어서 나머지를 마저 만들었습니다.
오늘의 성적표
테스트 324개 통과 (+33)
타입체크 통과 · 린트 통과 · 프로덕션 빌드 성공
배포 3회 (홈 개편 · /today 공개 · 출석 도장)
다음은 「오늘의 선택」입니다. 매일 질문 하나와 선택지 셋, 고른 답을 유나가 기억하고 친밀도가 쌓입니다. 사주를 파는 서비스에서 캐릭터와 지내는 서비스로 한 칸 더 옮기는 일입니다.
그리고 메뉴를 넷으로 접었습니다
출석 도장을 붙이고 나니 곧바로 지적이 왔습니다. "이런식으로 하면 메뉴가 너무 많아지겠네."
세어 보니 진입점이 12개였습니다(상단 메뉴 7 + 유저 드롭다운 5). 다음 기능까지 붙이면 14개입니다.
문제는 개수가 아니라 기능 하나 = 화면 하나 = 메뉴 하나라는 공식이었습니다. 이 공식을 끊지 않으면 무엇을 만들든 메뉴가 같이 늘어납니다. 그래서 기준을 바꿨습니다 — 메뉴는 기능 수가 아니라 사람이 오는 이유의 수만큼만 둡니다. 화조당에 오는 이유는 넷입니다.
| 탭 | 로그인 전 | 로그인 후 |
|---|---|---|
| 오늘 | 오늘의 기운 | + 출석 도장 |
| 사주 | 무료 명식 | 상품 선반 |
| 유나 | 일상 피드 | 커피챗 |
| 마이페이지 | 로그인 | 크레딧·이력·고객센터 |
같은 탭이라도 로그인 여부에 따라 여는 곳이 다릅니다. 못 보는 화면으로 보내면 그 탭은 죽은 탭이 되니까요.
상단 햄버거와 유저 드롭다운은 없앴습니다. 이제 새 기능의 기본 자리는 탭 안의 카드이고, 새 화면을 따로 파는 기준은 하나입니다 — 그것 자체가 링크로 공유될 목적지인가. 출석 도장은 아니라서 「오늘」 안의 카드고, 다음에 만들 「오늘의 선택」도 카드 한 장을 더 얹는 것으로 끝납니다. 탭은 그대로 넷입니다.
개편이 드러낸 것들
메뉴를 정리하고 실제로 써 보니 네 가지가 더 나왔습니다.
- 사주 탭에 오늘의 기운과 출석이 또 있었습니다. 둘 다 「오늘」 몫인데 선반에도 남아 같은 카드가 두 군데 나왔습니다.
- 커피챗에 들어가면 가장 오래된 메시지가 보였습니다. 원인은 착각이었습니다 — 대화 영역이 스크롤되는 줄 알았는데 실제로 움직이는 건 창이었고, 그래서 "맨 아래로 내리는" 코드가 처음부터 아무 일도 하지 않고 있었습니다. 개편이 만든 버그가 아니라 원래 있던 버그입니다. 화면을 다시 보게 되니까 드러났습니다.
- 푸터의 사업자 정보를 접었습니다. 결제 심사 때는 전부 펼쳐 둬야 했지만 승인이 끝났습니다. 법이 요구하는 식별 정보는 그대로 두고 나머지만 접어 여섯 줄을 세 줄로 줄였습니다.
- 데스크톱에서 로그인 버튼이 로고 옆으로 붙었습니다. 가운데를 밀어 주던 메뉴를 걷어냈는데 정렬 규칙이 남아 있었습니다.
3번이 특히 그렇습니다. "승인 끝났으니 접어도 된다"는 판단은 누가 화면을 실제로 볼 때까지 아무도 하지 않았습니다. 만드는 것보다 만든 것을 열어 보는 일이 자주 밀립니다.