FootPrint · 발자취 일기장
26.07 ~ 진행 중
친구의 요청에서 출발해 혼자 기획하고 App Store에 출시한 여행 일기 앱입니다. 관심 있는 아이디어를 직접 찾아보고 만들어 실제 사용자에게 전달하는 과정을 경험하고 싶었습니다. 사진·위치·일기를 지도 위에 기록하고 여행 경로를 함께 돌아볼 수 있습니다.
출시와 운영
- 누적 다운로드 270건 이상 · 2026.09.14 기준 (운영자 집계, 활성 사용자 수와는 다릅니다.)
- 기획·디자인·개발·스토어 등록·배포·운영을 1인으로 진행했습니다.
- 인스타그램 계정을 운영하며 서비스를 알리고 있습니다.
- 별도 자체 서버를 운영하지 않고 Firebase와 Cloudinary의 무료 티어를 활용했습니다.
예상보다 많은 사람이 앱을 내려받는 경험이 특히 즐거웠습니다. 기능을 만드는 일도 좋아하지만, 지금은 제가 넣고 싶은 기능뿐 아니라 사람들이 실제로 필요로 하는 기능이 무엇인지 살피며 다음 개발 방향을 고민하고 있습니다.
서비스 기획과 운영 판단
사진 선택·장소 검색·일기 작성·지도 조회·공유로 이어지는 사용자 흐름을 구체화했습니다. 운영 비용에 맞춰 사진 저장 방식을 선택하고, 지도 공유 결과와 플랫폼별 실행 오류를 개선하며 출시 후에도 사용할 수 있는 상태를 유지하는 데 집중했습니다.
📱 앱 화면




🛠️ 기술 스택
- App: React Native 0.85 (Expo SDK 56), TypeScript 6 (strict: true)
- 지도: @rnmapbox/maps 10 (globe projection + Mapbox Geocoding)
- 애니메이션/제스처: react-native-reanimated 4, react-native-gesture-handler, expo-blur
- 상태관리: Zustand 5 — 서버 캐시 라이브러리 없이 스토어 + lib/ 레이어에서 직접 동기화
- BackEnd: Firebase (Auth / Firestore) + Cloudinary (이미지 호스팅)
- 인증: 이메일·비밀번호 + Apple 로그인 (expo-apple-authentication + expo-crypto nonce)
- 알림: expo-notifications (매일 정해진 시각 로컬 알림)
- 기타: react-native-svg, react-native-view-shot(스토리 내보내기), expo-image-picker, expo-location, expo-media-library, AsyncStorage
- 빌드/자동화: expo prebuild 기반 Dev Build(Mapbox 네이티브 모듈 때문에 Expo Go 미지원), scripts/firebase-setup.mjs(Firestore 룰·인덱스 세팅), scripts/generate-pins.mjs(sharp로 핀 에셋 일괄 생성)
구조를 선택한 이유
화면과 외부 서비스 연동을 분리하고, Firebase·Cloudinary·Mapbox 연동을 별도 모듈로 관리했습니다. 화면 전환과 핀에서 일기가 펼쳐지는 애니메이션을 함께 제어하기 위해 Zustand 상태를 기준으로 레이어를 전환하는 구조를 사용했습니다.
무료 운영이라는 제약 때문에 저장소 선택과 이미지 업로드 방식을 조정했습니다. 기능을 추가하는 것뿐 아니라, 실제로 계속 운영할 수 있는 구성을 찾는 경험이었습니다.
💡 주요 기능
- 지도(홈): Mapbox globe projection 위 핀, 발자국 핀 / 사진 핀 선택, 월·연·기간 토글, 색상 필터, 여행 경로 선, 내 위치, 지도 테마 4종(블루마블·미드나잇·위성·라이트)
- 핀 클러스터링: 핀이 겹치면 클러스터 버블로 합치고, 탭하면 getClusterExpansionZoom으로 펼침
- 일기 작성 & 핀 드롭: 사진 다중 선택(드래그로 순서 변경) → 장소 검색 → 본문 작성 → 미니맵에서 핀 위치 지정, 제출 시 spring drop
- 편지형 상세 보기: 탭한 핀의 화면 좌표(rect)를 넘겨 누른 자리에서 카드가 자라나고, 닫으면 그 자리로 되돌아가는 확대 애니메이션
- 목록 / 리캡: 카드형 + 1열·2열 그리드 토글(설정 유지), 월별 커버, 연간 리캡 시트
- 미끄러지는 글래스 하단 네비게이션: 하이라이트 하나가 탭 사이를 스프링 이동, 드래그로도 전환, 아이콘 색 크로스페이드
- 스토리로 내보내기: 지도 카드를 이미지로 캡처해 인스타 스토리 공유
- 친구 / 공유 일기: 친구 요청(보낸·받은), 친구의 발자국 지도 보기(본문·사진은 비공개), 일기 공유·초대
- 매일 저녁 일기 알림: 로컬 푸시(마이페이지에서 on/off)
- 이메일·비밀번호 + Apple 로그인, 클라우드 동기화(Firestore users/{uid}/entries), Cloudinary 사진 호스팅, 최초 1회 온보딩
- 랜딩 페이지: Vercel 정적 배포 + GEO/SEO 대응, 히어로에 앱과 같은 Mapbox 지구본을 실제로 띄워 핀을 누르면 앱과 동일한 편지 상세가 열리도록 구현
🧠 기술적 도전
- Firebase Storage → Cloudinary 전환: 신규 Firebase 프로젝트에서 Storage가 Blaze(유료) 요금제를 요구 → 무료 Cloudinary(unsigned, base64 data URI 업로드)로 전환해 재설치·타 기기에서도 사진이 유지되도록 개선
- 스토리 내보내기의 마커 누락: Mapbox 스냅샷이 오버레이 마커(MarkerView)를 포함하지 않는 문제를, 사진 핀을 getPointInView로 화면 좌표에 투영해 캡처 직전 오버레이로 합성하는 방식으로 해결
- 핀 렌더링 성능: 핀마다 React 뷰를 띄우는 MarkerView → 네이티브 SymbolLayer + GeoJSON 소스로 전환. 흰 박스 아티팩트를 해결하고 렌더링 부담을 줄이는 방향으로 변경 (사진 핀만 MarkerView 유지, 정량 성능 측정은 미실시)
- iOS Modal 중첩 크래시: RN Modal 2개가 겹칠 때 나는 ‘already presenting’ 크래시를 absolute-fill 오버레이 + zIndex 레이어링 재설계로 회피 — react-navigation을 쓰지 않기로 한 결정과도 이어짐
- Android 런치 크래시: Mapbox Standard 스타일에 클라이언트 localizeLabels를 넘기면 Android에서 RuntimeException으로 앱이 죽는 문제를 발견. 기본 테마가 Standard였기 때문에 실행 즉시 크래시 → 클래식 스타일에만 해당 prop을 넘기도록 분기
- 키 없이도 굴러가는 폴백: Mapbox 토큰이 없으면 그라데이션 프리뷰 지구본, Firebase 미설정 시 로컬 시드 데이터로 폴백 — 키 없이 클론해도 크래시 없이 데모 가능
- 랜딩 지구본의 반응형 프레이밍: 줌을 고정값으로 박아두니 좁은 화면에서 구가 컨테이너보다 커져 ’확대된 평면 지도’처럼 잘려 보였음 → 컨테이너 크기에서 기준 줌을 역산(ResizeObserver)하고, 데스크톱은 ⌘+스크롤일 때만 확대되게 해 페이지 스크롤을 뺏지 않도록 조정
🌱 이 프로젝트로 발전한 방향
- 네이티브 모듈(Mapbox)·서드파티 SDK(Firebase, Cloudinary)가 얽힌 환경에서 ’내 코드 문제인가 / 네이티브 레이어 문제인가’를 분리해 좁혀 들어가는 디버깅 습관이 붙음
- 무료 티어·플랫폼 정책(Firebase Storage 유료화, Apple 심사, Mapbox 할당량) 같은 기술 외적 제약이 아키텍처를 바꾼다는 걸 체감하고, 대안을 찾아 갈아끼우는 판단을 여러 번 함
- 앱 → 랜딩 → 카드뉴스까지 하나의 디자인 토큰으로 브랜드를 일관되게 유지하는 작업을 해봄
- 남은 과제: 테스트 코드가 아직 없고 tsc –noEmit 타입 체크를 사용 중이며, 실행 시 동작의 회귀를 보장하지는 못함. Mapbox·Firebase처럼 네이티브/외부 의존이 큰 영역이라 어디부터 붙일지가 다음 과제
📦 프로젝트 정보
- 유형: 모바일 앱 (React Native + Expo, iOS 우선) + 랜딩 페이지
- 기간: 2026.07 ~ 진행 중 (App Store 출시 · 운영 중)
- 구성: 개인 프로젝트 (기획·디자인·구현·배포·스토어 등록 전담)
- Landing: https://footprint-amber.vercel.app
- GitHub: https://github.com/HanseKim/FootPrint