월간러닝 프로젝트, MVP완성

Justin (Yong) Lee / 검열
8 min readOct 7, 2024

--

안녕하세요, 월간러닝입니다!

지난 2주 동안 개발자 모드에 빠져 MVP 개발에 몰두했고, 이제 CBT를 위한 서비스가 준비되었습니다. 이 과정을 통해 AI 시대에 개발자로 일하고 있는 것에 새삼 감사함을 느끼게 되었습니다. 프론트엔드에서 랜딩 페이지와 UX를 구현했고, 백엔드에서는 데이터 최적화와 캐시 전략을 구현했습니다. 이제 지인 및 러너 분들을 대상으로 CBT를 진행하며 피드백을 받고, 버그를 수정할 수 있을 것 같습니다. 그러면 이번에 제작한 내용과 앞으로의 목표를 정리해보겠습니다.

랜딩 페이지

CBT를 위해선 직관적이면서도 심플한 랜딩 페이지가 필요했습니다. 디자인 역량을 최대한 발휘해, Strava 로그인 전 보여줄 페이지를 만들었습니다. 이미지는 사용하지 않고, 저의 2024년 3월 달리기 데이터를 시각화한 차트를 통해 사용자에게 첫인상을 전달하려 했습니다. 또한 Strava API의 Brand Guidelines를 참고해, Strava를 사용하는 서비스임을 자연스럽게 표현했습니다.

이미지 다운로드 UX

월간 차트를 스크린샷으로 저장하고 공유할 수 있지만, 깔끔하게 저장하기 어렵고, Flower 차트만 따로 저장할 수 없었습니다. 이를 해결하기 위해 html2canvas와 Canvas.toDataURL을 사용해 월간 차트와 Flower 차트를 이미지로 저장할 수 있는 기능을 추가했습니다. 이제 사용자가 예쁘게 차트를 저장할 수 있습니다.

Flower 차트 선택 UX

월간 차트에서는 월간 누적 마일리지와 최신 Flower 차트를 한눈에 확인할 수 있습니다. 하지만 저는 특정 날짜의 Flower 차트를 보고 싶은 경우도 많았습니다. 이를 반영해서 좌우 스와이프나 작은 꽃을 탭하면 해당 날짜의 Flower 차트로 이동할 수 있도록 구현했습니다. 처음에는 날짜를 이동할 때 마다 매번 API 호출을 하다 보니 반응 속도가 느렸는데, 이를 개선하기 위해 이번 달 모든 활동의 상세 데이터를 미리 가져오는 방식으로 반응 속도를 최적화했습니다. 또한, 선택된 날짜의 꽃은 달리는 느낌이 나도록 애니메이션을 추가해 더욱 생동감 있게 만들었습니다.

하지만 모든 상세 데이터를 가져오다보니 Strava API 호출 횟수도 많아지고, 데이터 전송량이 많아지는 부작용을 해결해야 했습니다. 이부분은 다음 파트에서 정리합니다.

데이터 경량화

회사의 자원을 사용할 때는 개발 효율성이 중요했지만, 클라우드(Vercel, Strava API)를 사용할 때는 사용량에 따른 제약이 있기 때문에 데이터 사용량이 신경 쓰이네요. 이때문에 Strava API의 응답을 재사용하기 위해서 Vercel의 KV 저장소에 모두 저장했더니 무료 용량을 금방 소진하는 문제가 발생했습니다.

이에 추후 비용도 줄이고 월간 차트의 로딩 속도도 개선하기 위해서 필요한 데이터만 추출해 저장하는 방향으로 경량화를 진행했습니다. 한 달 치 데이터를 700KB에서 84KB로 88% 줄이는 데 성공했습니다. 앞으로 클라우드 비용 절감에 큰 도움이 될 것 같습니다.

경량화된 데이터 구조

[
{
"id": (number),
"name": (string),
"type": "Run",
"start_date_local": "2024-03-31T17:57:57Z",
"start_date": "2024-03-31T08:57:57Z",
"timezone": "(GMT+09:00) Asia/Seoul",
"distance": 12040.2,
"moving_time": 4610,
"average_heartrate": 131.3,
"average_speed": 2.69,
"laps": [
{
"distance": 1000,
"average_speed": 2.65,
"average_heartrate": 124.1,
"moving_time": 377
},
{

},
],
"map": {
"polyline": "ogzcFuus…"
}
},
{

}
]

더욱 경량화할 수 있는 여지는 많지만, 가독성과 유지보수를 위해서 우선 이정도로 만족하고 넘어갑니다.

Strava API 호출 횟수 최적화

Strava API의 호출 횟수 제한을 고려한 최적화도 필요했습니다. Strava API는 제한된 호출 횟수를 제공하기 때문에, 무분별하게 사용하다간 서비스가 일시적으로 중단될 위험이 있습니다. 이를 해결하기 위해, Vercel KV 저장소에 데이터를 캐싱하고, 신규 활동이 없으면 추가적인 API 호출 없이 기존 데이터를 사용하도록 설계했습니다. 이 방식으로 API 호출을 월말 기준으로 약 30회에서 3회로 줄였습니다.

AS-WAS: 월간 차트 Strava API 호출

  1. (1회) 이번 달의 모든 활동 목록 조회
  2. (~30회) 모든 활동의 상세 데이터 조회

AS-IS: 월간 차트 Strava API 호출

  1. (1회) 이번 달의 모든 활동 목록 조회
  2. (KV 저장소에 저장된 데이터와 비교)
  3. (~3회) 신규 활동의 상세 데이터 조회
  4. (KV 저장소에 최신 데이터 저장)

이렇게 최적화하면서 API 호출 횟수를 크게 줄였지만, 여전히 신규 고객이 월말에 접속하는 경우 최대 30회 이상의 API 호출이 발생할 수 있습니다. 만약 한 달에 100번 정도 달리는 헤비 러너가 들어오면 약 100회의 API 호출이 생기죠. 이런 경우, 15분에 3명 이상의 신규 고객이 들어오면 서비스가 일시적으로 중단될 위험이 있습니다😱

Next.js의 fetch 캐시 적용

많지는 않지만 Next.js(+Vercel)의 fetch 함수에 내장된 캐시 기능을 활용해 Strava API 호출을 더 줄였습니다.

모든 활동 목록

 const response = await fetch(`https://www.strava.com/api/v3/athlete/activities?before=${before}&after=${after}&per_page=${perPage}&page=${page}`, {
headers: {
'Authorization': `Bearer ${accessToken}`
},
next: {
revalidate: 60, // 1 mins cache
},
})

1분만 캐시 해서 연속된 호출의 경우만 적용되고, 가능한 최신 활동 목록을 가져오도록 했습니다.

활동 상세 데이터

   const response = await fetch(`https://www.strava.com/api/v3/activities/${activityId}?include_all_efforts=true`, {
headers: {
'Authorization': `Bearer ${accessToken}`
},
cache: 'force-cache',
})

영구(permanent) 캐시를 적용했습니다. 하지만 기본적으로 KV 저장소에 저장된 데이터를 사용하기 때문에 혹시 모를 버그를 방지하기 위한 안전장치에 가깝습니다. Vercel에 배포를 하면 캐시는 없어집니다. 이후 고도화를 위해선 Strava의 Webhook을 이용해서 캐시 관리를 해야합니다.

CBT 계획 및 10월 목표

MVP는 완성되었지만 여전히 개선할 부분이 많습니다. 이제부터는 러닝 커뮤니티에서 Strava를 사용하는 러너들을 중심으로 서비스를 소개하고, 그들의 피드백을 받아가며 추가 개발을 진행할 계획입니다. 10월의 목표는 다음과 같습니다:

  • 200명의 CBT 고객 확보 및 100명의 유료 고객 전환
  • 100개의 피드백 수집
  • Flower 테마 예술 작품 완성 및 결제 연동
  • Squiggle, Willow Tree 무료 테마 완성

앞으로의 도전과 성장 과정을 기대해주세요!

(혹시 CBT에 참여하고 싶은 분은 DM 주세요.)

--

--

No responses yet