drawyourmind
Back to Stories
Project
2026-08-13

선물상자 — 매일 오전 8시의 발견

검증된 가치상품을 주제별 상자로 묶어 매일 오전 8시에 채워주는 구독형 웹뷰. 홈쇼핑모아 앱 안에서 "싼 것만 모여 있는 곳"을 만들기 위해 5인이 2주 스프린트로 만든 첫 MVP 기록.

선물상자 — 매일 오전 8시의 발견

value-store에서 검증한 가치상품을 주제별 상자로 묶어, 매일 오전 8시에 채워주고 다음 날 8시에 비우는 구독형 웹뷰. 홈쇼핑모아 앱 안의 "싼 것만 모여 있는 곳"을 만드는 게 목표였다.

왜 선물상자였나

홈쇼핑모아에는 이미 좋은 상품이 있었다. TV보다 싼 가격, 재고떨이, 독점가처럼 "여기서 살 이유"가 데이터로 검증된 가치상품 풀(value-store)이 어드민에 쌓여 있었다. 문제는 유저가 그걸 발견하지 못한다는 거였다.

앞서 큐레이션(discovery) 지면으로 이 상품들을 모아 노출해봤지만, 결과는 뼈아팠다. 클릭은 폭증했는데 실매출은 0.4%. 반면 같은 기간 리타게팅 실매출의 57%는 홈에서 나왔다. 교훈은 두 가지였다.

  • 노출 위치는 홈이 정답이다. 유저가 가장 자주 오는 곳에 붙여야 한다.
  • 발견성(클릭) ≠ 실매출이다. 그냥 모아서 보여주는 것만으로는 가치가 전달되지 않는다.

유저 인터뷰도 같은 곳을 가리켰다. 28명 중 11명 이상이 "기획전·혜택이 있어도 안 보인다", "더 자주 보여달라"고 했고, 4명은 서로 다른 자리에서 독립적으로 "방송을 놓치면 그 혜택까지 같이 사라진다"는 아쉬움을 말했다.

그래서 나온 솔루션이 선물상자였다. 검증된 상품을 그냥 나열하는 게 아니라 "좋은 걸 골라 담아 드린다"는 프레이밍으로 묶고, 유저가 그 상자를 구독하게 만든다. 매일 오전 8시에 오늘 방송하는 가치상품으로 채워지고, 24시간 뒤 비워졌다가 다시 채워진다. 매일 볼 이유가 생기는 것 — 그게 재방문의 엔진이었다.

성패는 두 개의 허들에 달려 있었다.

  • 허들 A — 구독 전환: 유저가 구독을 하게 만드는 것
  • 허들 B — 가치의 즉시 전달: 상자 속 상품이 얼마나·어떤 점에서 가치 있는지를 고민할 틈 없이 배지·카피로 즉시 전달하는 것

5인, 2주 스프린트

크로스펑셔널 5인이 2주 얇은 MVP를 실배포하는 방식으로 굴렸다. "기능이 아니라 퍼널로 일한다"는 팀 원칙 아래, 공급(이미 있는 가치상품)과 발견(선물상자)을 분리해서 붙이는 게 이 에픽의 자리였다.

멤버역할맡은 일
찰리BE · 아이디어 오너백엔드 구축, 가치상품 필터링, MVP 기획안
시드니PD기획·정책 총괄, 화면 스펙, 지표 설계
무벅FE내 선물상자 화면 웹뷰 구현 (내 파트)
제이ACE네이티브(앱) 진입점·FAB·브릿지
디노QA품질 검수, 계정 전환·엣지 케이스

킥오프에서 진입 방식과 기획을 두고 고민이 많았다. 찰리 안(유저 분기 없이 간단)과 시드니 안(개인화·별도 탭까지 정교)이 있었는데, "일단 찰리 안으로 MVP식으로 가되, 가능성을 확인하면 시드니 안으로 확대한다"로 정리했다. 기획과 개발을 완전히 병렬로 돌리면 정책 변경에 롤백이 얽혀 골치 아파지니, 어느 정도 프리즈된 기획안이 나온 뒤 보완하는 방식으로 리스크를 줄였다.

웹뷰를 맡으며 부딪힌 것들

내 몫은 내 선물상자 화면이었다. 홈의 FAB(플로팅 버튼)에서 웹뷰로 hsmoa.com/gift-box로 진입하는, 앱 전용 화면. 단순한 리스트 화면처럼 보이지만 웹뷰라는 환경 때문에 까다로운 지점이 많았다.

RN 헤더 토큰이 진실의 원천

앱 웹뷰에서는 RN이 최초 로드 때 주입한 헤더 토큰(access-token 등)이 인증의 기준이다. 그런데 GET /boxes는 무토큰이면 401이 아니라 **200 익명(전부 미구독)**을 돌려준다. 웹뷰가 재사용되거나 헤더가 안 실리면 SSR 결과가 익명이 되어버려서, 마운트 시 클라이언트 토큰으로 반드시 재조회해 실제 구독 상태로 보정해야 했다.

여기서 함정이 하나 더 있었다. 웹의 useUser()는 게스트 로그인 토큰으로 유저 토큰을 덮어써서, 앱에서 실행되면 RN이 준 진짜 토큰이 게스트로 바뀐다. 그래서 useUser를 쓰는 컴포넌트는 웹에서만 마운트되도록 격리했다.

iOS WKWebView의 focus 경합

가장 오래 붙잡은 버그. iOS WKWebView는 첫 터치에 focus 이벤트가 발생하는데, 이게 구독 토글 요청과 재조회를 경합시켜서 응답이 역전되는 문제가 있었다. 유저가 방금 켠 구독이 이전 응답으로 덮여 다시 꺼진 것처럼 보이는 식이다. 시퀀스 번호(revalidateSeq)로 최신 응답만 반영하도록 막았다.

낙관적 업데이트를 "안" 하는 선택

구독 토글은 보통 UI를 먼저 뒤집는 낙관적 업데이트를 쓰지만, 여기선 일부러 안 했다. 첫 구독(안내 화면)이냐 재구독(오늘 상품 복원)이냐는 BE만 알 수 있어서, 미리 뒤집으면 깜빡임이 생긴다. BE 확정 후 300ms 페이드로 단일 전환하고, 상자별 중복 요청 가드를 뒀다.

그 외

  • 24시간 소멸 타이머: 자정을 넘기면 "오늘의 선물상자 → 어제 받은 선물상자"로 라벨이 자동 전환되며 긴박감을 준다. FOMO를 전달하는 장치.
  • 스크롤스파이 탭: 탭 활성 상태를 유저 클릭이 아니라 스크롤 위치로 정하고, 클릭 시 앵커 스크롤. 탭 바는 sticky로 고정.
  • 웹/앱 통계 분기: 같은 이벤트를 앱은 RN 브릿지(/stat/)로, 웹은 analytics(/wstat/)로 한쪽만 보내도록 갈랐다.
  • RN 당겨서 새로고침: 스크롤 위치를 RN에 보고해, 최상단에서만 RefreshControl이 동작하도록 했다.

무엇으로 성패를 재는가

이 프로젝트에서 가장 인상 깊었던 건, 코드보다 "어떻게 성공을 판정할 것인가"에 쏟은 고민이었다. 큐레이션의 0.4% 실패를 반복하지 않으려면, 조회만 늘고 구매는 안 느는 패턴을 미리 경계해야 했다.

  • 선물상자를 켠 실험군 vs 안 켠 대조군을 12개 버킷으로 나눠 A/B (대조군 50% · 실험군 50%)
  • 핵심 지표는 재방문(KR4). 조회 UV(KR2)와 실구매(KR5)가 뒤따르는지 함께 봤다.
  • KR5는 절대 목표가 아니라 "조회가 는 만큼 구매도 따라왔는가"로 판정 — 큐레이션의 교훈을 지표 설계에 그대로 박았다.

가드레일도 촘촘히 뒀다. 기존 홈 생방송 매출을 잠식하지 않는지, 매일 8시 알림으로 알림 해제가 늘지 않는지, 특정 제휴사에 상품이 쏠리지 않는지까지.

배포, 그리고 남은 것

  • 디자인 공유(8/3) → BE API 1차(8/4) → 웹뷰 dev 전달(8/7) → 스테이지 배포(8/11) → 실서버 배포(8/18 예정)
  • 공휴일 대응 이슈로 실배포일을 목요일에서 화요일로 미루는 등, 2주 안에 실배포까지 밀어붙이며 일정 조율의 감각도 같이 배웠다.
  • 구독 반복발송 자동화·자동 큐레이션·뒤로가기 넛지 팝업은 이번 범위에서 의도적으로 잘라내고 2차로 넘겼다. "중요한 문제는 작게 검증하고, 충분히 통과한 일만 크게 만든다"는 원칙대로.

작은 화면 하나였지만, 웹뷰라는 환경의 제약 안에서 인증·경합·상태 전환을 어떻게 다룰지, 그리고 만든 것의 성패를 무엇으로 증명할지를 팀과 함께 정면으로 다룬 스프린트였다.


이 스토리의 현장 사진은 준비 중입니다. 곧 구글 포토 앨범으로 공유할 예정이에요.