"분명히 샀는데 또 샀네." "이거 언제 산 거지?" 냉장고 문을 열 때마다 반복되는 이 두 문장을 없애고 싶었다. 그렇게 만든 가정용 재고관리 앱 홈 인벤(Home Inven) 의 기능과 구현 이야기를 여기에 풀어볼까 한다..
홈 인벤은 가정 안의 식품·생활용품·의약품 재고를 한곳에서 관리하는 모바일 앱이다. 핵심은 두 가지 문제를 푸는 데 있다.
처음에는 인터넷 없이도 돌아가는 완전 오프라인 앱으로 2025년도에 배포되었지만, 지금은 가족과 재고를 공유하고, 4개 언어를 지원하며, 홈 화면 위젯과 바코드 스캔까지 갖춘 앱으로 발전했다.
크로스 플랫폼 모바일 앱이라 React Native + Expo 조합을 선택했다. 한 코드베이스로 iOS와 Android를 모두 커버하면서, 카메라·알림·백그라운드 작업 같은 네이티브 기능은 Expo 모듈로 안정적으로 끌어다 쓸 수 있다는 점이 좋았고, 무었보다 내가 nodejs 개발자이기 때문에 선택한것도 없지않아 있.... ㅋ
| 영역 | 사용 기술 |
|---|---|
| 프레임워크 | React Native 0.76 + Expo SDK 52 |
| 언어 | TypeScript (strict mode) |
| 상태 관리 | Zustand 5 |
| 로컬 저장소 | AsyncStorage |
| 클라우드 / 인증 | Supabase (Postgres + Auth) |
| 결제 / 구독 | RevenueCat (react-native-purchases) |
| 내비게이션 | React Navigation 7 (Stack + Bottom Tabs) |
| 알림 | expo-notifications + expo-task-manager |
| 카메라 / 이미지 | expo-camera, expo-image-picker, expo-image-manipulator |
| 다국어 | i18next + react-i18next |
| 공유 인텐트 | expo-share-intent |
| 날짜 처리 | date-fns 4 |
| 차트 | react-native-chart-kit |
React Native 개발을 시작하게되면 개발자들은 Expo 선택여부 앞에 많은 고민에 잠기게 된다. Expo 를 쓰면 확실히 쉽게 개발을 시작할 수 있지만 또 블로그 글을 보다보면 나중에는 어차피 갈아탄다 아니다 이런 얘기가 많기 때문이다.
둘다 써본 결론은 Expo 로 하는게 더 좋다? 인것같다.
요새 Expo 모듈이 잘 나오고 있어 개발이 더 편하기도 하고, 나중에 expo 에서 지원을 안하는 기능때문에 이젝트해야 한다고 하지만 실제 구현하려는 기능이 expo 에서 거의 구현이 가능한게 많기 때문이다.
정 신경쓰인다면 개발전에 GPT 등에게 생각하고 있는 기능에 필요한 모듈이 expo 에서 지원이 되는지 한번 확인해보는것도 좋을것같다.
물품마다 이름·카테고리·보관 위치·수량·단위·구매 링크·사진·메모·바코드를 담을 수 있다. 카테고리(식품, 생활용품, 의약품 등)와 보관 위치(냉장실, 냉동실, 창고 등)는 기본값을 제공하면서도 사용자가 자유롭게 추가·수정·삭제할 수 있게 했다. 즐겨찾기와 핀 고정으로 자주 쓰는 물품을 위로 끌어올릴 수도 있다.
목록 화면은 실시간 검색, 상태 필터(임박/만료/부족/소진), 카테고리·위치 필터, 정렬을 모두 지원한다. 여기서 신경 쓴 부분은 사용자의 편의성이다.
단순히 리스트만 보여주는것이 아니라 "pin" 기능을 통해 자주 사용하는 물건을 상위로 배치 할수 있고 리스트형식이 아니라 그리드 형식으로 노출선택이 가능하도록 기능을 추가했다.
날짜 숫자만 보여주면 사람은 위급함을 못 느낀다. 그래서 남은 기간을 4단계 상태로 환산하고 색을 입혔다.
| 상태 | 조건 | 색상 |
|---|---|---|
| 안전(safe) | 8일 이상 남음 | 초록 |
| 주의(warning) | 4~7일 남음 | 주황 |
| 위험(danger) | 0~3일 또는 당일 | 빨강 |
| 만료(expired) | 기한 경과 | 회색 |
처음엔 홈화면을 어떻게 보여주는게 좋을까 고민을 하다가 통계화면과 같은 차트를 넣었었는데, 눈에 잘 들어오지 않았다. 그러다가 문득 일정관리를 캘린더로 하는게 생각나서 유통기한도 캘린더로 보여주는게 한눈에 남은 일자를 파악할수 있어서 좋을것같다는 생각이 들어 홈화면은 캘린더 형식으로 유통기한을 보여주게 수정했고, 한화면에 재고가 부족한 아이템도 보일 수 있도록 재고 영역도 추가하게 되었다.
달력 위에 어느 날 무엇이 만료되는지 점으로 찍어주고, 날짜를 탭하면 그날 만료되는 물품이 캘린더 하단에 펼쳐진다. 이 캘린더모양은 그대로 위젯으로도 반영된다.
알림은 이 앱의 심장이다. 두 종류로 나뉜다.
가장 까다로웠던 건 앱이 꺼져 있어도 알림이 최신 데이터를 반영하게 만드는 일이었다. 로컬 알림은 예약 시점의 데이터로 고정되기 때문에, 그 사이 재고가 바뀌면 내용이 어긋난다. 이를 expo-task-manager 의 백그라운드 태스크로 풀었다. 약 12시간 주기로 시스템이 앱을 깨워 최신 재고로 일일 알림을 다시 스케줄링하고, 이 태스크는 앱 종료는 물론 기기 재부팅 후에도 살아남는다.
알림 제어는 계층 구조로 설계했다. 전역 설정으로 알림 전체를 끄거나, 물품마다 notificationsEnabled 를 꺼서 특정 물품만 알림에서 제외할 수 있다.
장 본 물건을 일일이 손으로 입력하는 건 고통이다. 그래서 expo-camera 로 실시간 바코드 스캔을 붙였다. 등록된 물품의 바코드를 비추면 해당 물품을 찾아주고, 여러 개를 연속으로 찍은 뒤 각각의 차감 수량을 조정해 한 번에 반영할 수 있다. 스캔 라인 애니메이션을 주어 정말 스캐너로 찍는듯한 느낌을 주도록 공을 들였다.
쇼핑 앱에서 산 물건을 등록할 때, 상품 페이지를 공유하면 앱이 알아서 채워주면 어떨까. expo-share-intent 로 외부 앱의 공유 시트에 홈 인벤을 띄우고, 넘어온 URL을 분석한다. 이미 같은 링크로 등록한 물품이 있으면 그 상세 화면으로 보내고, 없으면 상품명·대표 이미지·가격까지 미리 채운 추가 화면으로 연결한다. 단축 URL을 따라가며 실제 상품 URL을 복원하고 상품 정보를 뽑아내는 스크래핑 로직이 이 기능의 핵심이다. 주로 사용하는 앱이 쿠팡과 N+스토어 이기 때문에 해당 앱에서 "공유" 로 "홈인벤" 을 선택시 위의 기능이 작동하도록 적용했다.
한국어로 시작했지만 한국어·영어·중국어·일본어 4개 언어를 지원한다. i18next + react-i18next 로 구성하고, 모든 UI 문자열은 t('네임스페이스.키') 로만 출력한다. 하드코딩된 문자열은 두지 않는 것을 규칙으로 삼아, 새 화면을 만들 때 4개 언어 파일에 키를 함께 추가한다.
언어 선택은 앱이 알아서 시작한다. 최초 실행 시 expo-localization 으로 기기 언어를 감지해 지원 언어면 그대로, 아니면 영어로 떨어뜨린다. 한번 정해지면 설정값으로 저장되고, 이후엔 사용자가 직접 바꿀 수 있다. 날짜와 요일도 Intl.DateTimeFormat(i18n.language, ...) 로 언어에 맞춰 표시한다.
이 앱의 출발점은 "인터넷 없이도 완벽하게 돌아간다" 였다. 동시에 "가족과 냉장고를 공유하고 싶다" 는 요구도 있다. 로컬 저장과 클라우드 동기화는 보통 서로 충돌하는 요구지만, 저장소 추상화 로 깔끔하게 분리했다.
IStorageRepository 라는 인터페이스 하나를 정의하고, 이를 두 구현체가 따른다.
AsyncStorageRepository — 로그인하지 않은 사용자용, 모든 데이터를 기기 로컬에 저장SupabaseRepository — 로그인한 사용자용, 같은 인터페이스를 Postgres 쿼리로 구현let currentRepository: IStorageRepository = new AsyncStorageRepository();
export const getRepository = () => currentRepository;
export const setRepository = (repo: IStorageRepository) => {
currentRepository = repo;
};
화면과 서비스 코드는 언제나 getRepository() 만 부른다. 로그인 여부에 따라 실제 구현체가 통째로 교체될 뿐, 호출하는 쪽은 자신이 로컬을 쓰는지 클라우드를 쓰는지 신경 쓰지 않는다. 덕분에 "오프라인 전용 앱" 에 클라우드를 얹으면서도 화면 코드는 거의 손대지 않았다.
로그인하면 워크스페이스를 만들고 가족을 이메일로 초대할 수 있다. 초대를 수락한 구성원은 같은 재고를 함께 보고 편집한다. 한 사람이 우유를 차감하면 다른 가족의 화면에도 반영되는 구조다. 워크스페이스·멤버·초대 상태는 Supabase에 모델링했고, 권한은 owner/member로 구분한다.
우리집에서는 재고는 내가 차감하고 구매는 와이프가 하기 때문에 이런 구조가 필수이기도 했다.
앱을 열지 않고도 임박한 유통기한을 보고 싶다는 건 자연스러운 욕구다. 네이티브 위젯 모듈(WidgetDataModule)에 유통기한이 있는 물품 데이터를 동기화해, 홈 화면 위젯에서 바로 확인할 수 있게 했다. 앱 시작 시점과 데이터 변경 시점에 위젯 데이터를 갱신한다.
구글, 애플, 카카오 로그인을 지원하는데, 로그인을 하게되면 그때부터 클라우드에 데이터가 올라가기 때문에 비용이 발생된다. 물론 로그인을 하지않아도 모든기능을 동일하게 사용가능하지만 로그인을 하면 데이터 백업과 재고 공유가 지원된다.
클라우드 서버 비용이 들어가기 때문에 나중에 사용자가 늘어나면 유료화를 진행할까 생각했지만 사용자수가 많아야 앱의 가치가 높아지는 그런 앱이 아닌경우 초기부터 유료화 정책이 적용되어 있어야 추후 사용자의 반발을 줄일 수 있다는 이야기가 생각나 유료화 작업을 진행하기로 했다. ( 괜찮...겠지...? )
아직은 작업이 진행중이고 금액도 결정되지않았다.. 첫 한달은 무료 사용으로 제공할 예정이다.
홈 인벤은 "나도 모르게 유통기한이 지나 버려지는 식품은 없게하자" 라는 단순한 목표에서 출발했다.
그 목표를 지키기 위해 오프라인 우선 설계를 택했고, 거기에 클라우드 공유·바코드 스캔·다국어·위젯을 하나씩 얹으면서도 핵심 가치를 흐리지 않으려 노력했다.
음식을 덜 버리고 싶은 사람,
생활용품 떨어지는 타이밍을 놓치고 싶지 않은 사람,
산것같긴한데 물건을 어디에 두었는지 자주 잊어버리는 사람
이라면 한번 써보고 피드백을 주면 정말 고맙겠다. (굽신굽신...)
인터넷 없이도 바로 시작할 수 있고, 가족과 함께 쓰고 싶어지면 그때 로그인하면 된다.
홈 인벤 — 집에 뭐가 있는지 외우지 말자. 앱이 기억한다.
앱스토어
https://apps.apple.com/kr/app/id6753967518
구글플레이
https://play.google.com/store/apps/details?id=com.homeinven.app
한국어·English·中文·日本語 지원 / iOS · Android