2026.06 — 2026.09회사 프로젝트
VGOLF 앱
골프 라운드 기록 모바일 앱 (iOS · Android)
- 맡은 일
- Frontend Developer · React Native 앱 단독 개발
- 기간
- 2026.06 — 2026.09
- 소속
- 플럭시티클라우드팀
골프 라운드 기록을 관리하는 React Native 앱입니다. 화면 구현부터 네이티브 설정, 릴리스 서명, Play Console 제출까지 앱 전체를 맡아 개발했습니다.
5k LOC · 문서 55편 규모로, FSD 5레이어 아키텍처와 3겹 에러 안전망을 세우고, zod 2층 스키마 기반 실서버 계약 테스트로 백엔드 응답 계약을 프론트에서 먼저 정리해 백엔드에 제안했습니다. Expo 없이 ios/ · android/ 프로젝트를 직접 관리하며 소셜 로그인 4종, 2겹 스플래시, 릴리스 서명·환경 분리까지 네이티브 영역을 다뤘습니다.
i.Problem
React Native 는 처음이었고 앱을 맡을 사람은 저 하나였습니다. 서버 응답도 아직 정리되지 않아, 화면에는 아무 오류가 없는데 값만 틀린 일이 생겼습니다.
ii.Action
zod 스키마를 런타임용과 엄격용 2층으로 두고 실제 서버 응답을 계약처럼 검사했습니다. 어긋난 12건은 영향과 우선순위를 표로 정리해 백엔드에 전달하되, 하위 호환을 지키는 형태로 제안했습니다. 화면 밖 일은 Expo 없이 ios · android 프로젝트를 직접 관리하며 서명·데이터 보안 항목·공개 약관 페이지까지 하나씩 채웠습니다.
iii.Result
서버가 par 를 null 로 주어 모든 홀이 더블보기로 보이던 무증상 오류를 화면에 나가기 전에 잡았습니다. 555커밋 · 화면 21개로 스토어 제출까지 마쳤고 지금 Play 스토어에 올라가 있습니다.
핵심
- 01
React Native 앱
555커밋, 화면 21개, 16.5k LOC, iOS·Android 동시 대응
- 02
FSD 5레이어 + 세그먼트 구조
슬라이스 공개 API 규칙으로 '기능 삭제 = 폴더 삭제'가 성립하는 코드베이스
- 03
zod 2층 스키마(런타임·엄격) 실서버 계약 테스트
서버 par: null로 모든 홀이 더블보기로 표시되던 무증상 오류를 사전 검출
- 04
API 응답 불일치 12건을 영향·우선순위(P0/P1) 표로 문서화해 백엔드에 전달
하위 호환을 지키는 형태로 제안
나머지 4개 더 보기접기
- 05
3겹 안전망(ErrorBoundary · 전역 핸들러 · QueryState) + axios 에러 6종 정규화
로딩·에러·빈 상태를 컴포넌트 3개로 수렴
- 06
Expo 없이 네이티브 직접 소유
소셜 로그인 4종, 2겹 스플래시, 적응형 아이콘, 릴리스 서명·cleartext 분리
- 07
MMKV 암호화 키를 Keychain/Keystore에 보관 + 토큰 재발급 single-flight
동시 401 중복 재발급·재시도 루프 제거
- 08
Play Console 제출 실무
데이터 보안 매핑, 공개 약관·개인정보처리방침·회원 탈퇴 페이지 제공
화면
Pl. 2of5
데모 계정으로 찍은 화면입니다. 이용객 이름과 골프장 상호는 알아볼 수 없게 처리했습니다.
자세히
궁금한 항목을 펼치면 그때 무엇이 문제였고 어떻게 판단했는지 나옵니다.
FSD 5레이어(shared → entities → features → pages → app) 단방향 의존 + api/model/ui/lib 세그먼트 분리
슬라이스 밖에서는 index.ts로만 import
상태를 역할로 3분할
서버 데이터는 react-query, 사용자 선택값은 zustand + MMKV 영속, 폼은 react-hook-form + zod로 고정하고 겹치지 않게 규칙 문서화
버전을 '최신'이 아니라 '호환'으로 선택
MMKV v4(Nitro 네이티브 의존)·zod v4(ESM ↔ RN Babel 충돌)를 v3에 고정하고 올리지 않은 이유를 아키텍처 문서에 기록
3겹 안전망
ErrorBoundary(렌더) + ErrorUtils.setGlobalHandler(렌더 밖, 기본 핸들러 체이닝) + QueryState(로딩·에러·빈 상태 render-prop 단일 분기)
모든 실패를 ApiError 하나로 정규화
axios 인터셉터에서 timeout/network/client/server/invalid/unknown 6종 분류 + 상태코드별 한국어 문구 테이블
조건부 재시도 정책
network·timeout·5xx만 1회 재시도, 4xx 제외(408·429는 예외), staleTime 60s, 스키마 불일치(invalid)는 재시도 대상에서 제외
Sentry 크래시 대응
짝 없는 서로게이트가 네이티브 UTF-8 직렬화에서 앱을 죽이는 문제를 전송 전 정제 단계로 차단
배운 것
React Native도 처음이고 앱을 맡을 사람도 저 하나였습니다. 모르는 걸 빨리 배우는 것보다 중요했던 건, 왜 그렇게 정했는지를 남기는 일이었습니다. 버전을 올리지 않은 이유, 붙였다가 뺀 기능까지 문서 55편으로 남겼고, 나중에 다시 같은 고민이 생겼을 때 문서부터 펼쳐 볼 수 있었습니다.
화면에 오류가 없다고 데이터가 맞는 건 아니라는 걸 배웠습니다. 서버가 par 를 null로 주어 모든 홀이 더블보기로 보이던 문제는 앱이 아무 에러도 내지 않았기 때문에 더 위험했습니다. 그 뒤로는 응답이 '깨지지 않는가'와 '약속대로 오는가'를 따로 검사합니다.
백엔드에 고쳐 달라고 할 때도 상대의 비용을 먼저 생각하게 됐습니다. 어긋난 12건을 영향과 우선순위로 정리하고 하위 호환을 지키는 방법까지 함께 제안했더니, 요청을 받는 쪽도 무엇부터 고칠지 바로 정할 수 있었습니다.
스토어 제출까지 직접 해 보니, 코드가 끝나도 앱은 끝나지 않는다는 걸 알았습니다. 서명·환경 분리·데이터 보안 항목·공개 약관 페이지처럼 화면 밖의 일까지 마쳐야 사용자 손에 닿았고, 그 마지막 구간까지 해 본 경험이 오래 남았습니다.
쓴 기술
이어서 볼 것
플럭시티에서 한 다른 프로젝트