2026-07-23 | 앱인토스 7월 바이브코딩 챌린지 "미니앱과 함께하는 여름" 출품 준비기
깃허브 링크 : https://github.com/thal070909/umbrella-app
토스미니앱 "우산챙겨!"의 소개와 사용법 : 우산챙겨!

현재 위치를 기반으로 실시간 날씨와 체감온도를 확인하고, 오늘의 옷차림을 추천받을수있는 앱입니다.
비 예보가 있으면 우산 챙기라는 알림을 보내주고,
갑자기 비가 쏟아지면 근처 카페와 음식점을 찾아 비를 피할 수 있는 어플!
무더위가 심한 날엔 근처 무더위쉼터(도서관, 주민센터)도 안내하고,
기온이 30도를 넘으면 차 안에 물건을 두고 내리지 않았는지 알림 기능추가!

프로젝트 타임라인
- 7/1 — 챌린지 시작! 아이디어 기획 & 프로젝트 세팅
- 7월 초~중순 — 화면 구현, 기능 개발, 여름 테마 강화
- 7/23 (오늘) — .ait 빌드, 콘솔 버전 등록, 검수 요청 (1차 반려 😇 → 수정 후 재요청), 깃허브 업로드
0. 기획: "비 오는 날, 우산 챙기라고 말해주는 앱"
7월 1일에 챌린지 공지를 보고 시작했다.
주제가 "미니앱과 함께하는 여름"이라서, 여름 하면 떠오르는 것 = 장마 + 무더위였다.
특히나 우리 엄마는 우산을 자주 깜빡한다....(본가에 편의점 우산이 늘 쌓여있는 이유)
또한 여름에 뉴스를 보면 가끔 나이가 있으신 어르신들은 무더위에 힘들어하시는 기사를 종종 볼수있다
가장 기본적이고 단순하면서도 그러기에 잊어버리는 경우가 많은거같다는 생각이 들었다.
여기서 출발한 아이디어가 우산챙겨! 였다.
처음 기획서에 적었던 핵심 기능은 이랬다.
- 현재 위치 기반으로 지금 비가 오는지, 얼마나 오는지 보여주기
- 온도 + 체감온도 ("실제보다 6도 더 덥게 느껴져요" 같은 문구)
- 체감온도에 맞는 옷차림 추천
- 비가 오면 "우산 챙기세요!" 알림
여기에 살을 붙이면서 추가된 것들:
- 비 피하기 스팟:
갑자기 비가 쏟아지면 근처 카페·음식점을 찾아주는 기능.
홈에 다 넣지 않고 별도 메뉴로 분리해서, 필요할 때만 들어가게 설계했다. - 수익화 고려:
공모전이지만 "실제 비즈니스 모델까지 생각했다"는 인상을 주고 싶어서,
스팟 리스트에 제휴 매장 우선 노출 슬롯 + 광고 배너 자리를 넣어뒀다. - 여름 테마 강화:
주제 연관성을 확실히 하려고 무더위쉼터(도서관·주민센터) 찾기와,
기온 30도 이상일 때 혹시모를 여름 사고에 대비해 "차에 보조배터리·캔음료 두고 내리지 않았는지 확인하세요"
배너를 추가했다.
원래는 "차량 블루투스 연결이 끊기면 알림"을 하고 싶었는데,
미니앱은 토스 앱 안의 웹뷰라 백그라운드 블루투스 감지가 불가능했다.....
플랫폼 제약을 확인하고 → 같은 문제의식을 구현 가능한 방식(온도 기반 배너)으로 바꾸는 과정이 꽤 재밌었다.
디자인 방향
- 토스 느낌의 귀엽고 심플한 UI: 흰 배경 + 토스 블루, 둥근 카드, 큰 숫자
- 날씨에 따라 표정이 바뀌는 구름 마스코트 (비 오면 울상 ㅠㅠ)
- 말투는 친근한 존댓말 ("~예요", "~해요")
기술 설계
역할 선택 이유
| 프레임워크 | Vite + Vanilla JS | React 없이도 충분한 규모, 앱인토스 WebView SDK로 감싸기 쉬움 |
| 날씨 | Open-Meteo API | 무료 + API 키 불필요 |
| 위치명 | BigDataCloud 역지오코딩 | 좌표 → "명동 서울특별시" 변환 |
| 장소 검색 | Overpass API (OSM) | 무료로 위치 기반 검색 가능 (대신 안정성 이슈… 아래에서 후술 😅) |
| 폴백 | 위치 권한 거부 시 서울 기준 | 안내 배너와 함께 |
1. 앱인토스 배포 구조 이해하기
일반 웹 배포랑 다르게, 앱인토스는 빌드 결과물을 .ait 파일로 패키징해서 콘솔에 올리는 방식이다.
npm run build → dist/ 생성 → .ait 패키징 → 콘솔 업로드 → 검수 → 출시
- granite.config.ts의 web.commands.build가 실행되고, 그 결과물(outdir, 기본 dist) 기준으로 .ait가 만들어진다
- 검수 요청은 한 번에 1개 번들만 가능. 오류 발견하면 요청 취소하고 새 버전으로 다시!
1차 검수 반려... 그리고 배운 것 ⭐
두근두근 검수 요청을 넣었는데 반려 메일이 왔다. 사유는 두 가지였다.
- 아이콘 불일치 — granite.config.ts의 icon에는 로컬 파일이 아니라 콘솔에 업로드된 이미지의 URL을 넣어야 한다. 비워두면 토스 앱에서 "잠시 문제가 생겼어요"가 뜬다
- 이름 불일치 — 콘솔엔 "우산챙겨", config엔 "우산챙겨!"... 느낌표 하나 차이로 반려됐다 😂
교훈: 콘솔 앱 정보와 granite.config.ts는 글자 단위로 똑같아야 한다.
검수 전에 appName / displayName / icon 3종 세트 대조는 필수!
2. 무료 API의 배신: 됐다 안 됐다 하는 스팟 검색 고치기
스팟 검색에 쓰는 Overpass API가 무료 공개 서버라 과부하로 간헐적으로 실패했다.
브라우저에선 되는데 토스 앱 테스트에선 안 되고... 전형적인 "어쩔 땐 되고 어쩔 땐 안 되는" 버그.
1차 시도: 미러 서버 순차 폴백 → 실패
서버 3곳을 순서대로 시도하게 했더니, 앞 서버가 느리면 그만큼 기다렸다가 다음으로 넘어가서 최악의 경우 36초까지 스피너가 돌았다. 유저 입장에선 그냥 멈춘 앱이다.
2차 시도: 동시 요청 + 최초 성공 채택 → 성공! 🎉
const controllers = endpoints.map(() => new AbortController());
const attempts = endpoints.map((ep, i) =>
fetch(ep, { method: 'POST', body, signal: controllers[i].signal })
.then(res => { if (!res.ok) throw new Error(); return res.json(); })
);
const timeout = new Promise((_, rej) => setTimeout(() => rej(new Error('timeout')), 8000));
try {
data = await Promise.race([Promise.any(attempts), timeout]);
} finally {
controllers.forEach(c => c.abort()); // 승자 정해지면 나머지는 취소
}
- Promise.any: 여러 요청 중 가장 먼저 "성공"한 것만 채택 (race는 실패가 먼저 와도 잡아버리는데, any는 성공만 기다린다는 게 포인트!)
- race + 타임아웃 프로미스: 전체 대기 8초 상한 → 스피너가 10초 넘게 도는 일 없음
- AbortController: 남은 요청은 즉시 취소해서 트래픽 낭비 방지
- 위치+카테고리별 캐시: 한 번 성공한 결과는 재방문 시 요청 없이 즉시 표시
교훈: 못 믿을 외부 API는 "순차 재시도"보다 "동시 요청 후 최초 성공 채택"이 체감 속도도 성공률도 좋다.
대신 요청량이 3배가 되니까 취소(abort)랑 캐시를 꼭 세트로 설계하기.
3. Windows 개발 환경에서 만난 함정들
PowerShell이 npx를 막는다?!
이 시스템에서 스크립트를 실행할 수 없으므로 ... npx.ps1 파일을 로드할 수 없습니다
PowerShell 실행 정책 때문이다. 관리자 권한으로 딱 한 번만 풀어주면 된다.
Set-ExecutionPolicy -Scope CurrentUser -ExecutionPolicy RemoteSigned
<app-name>을 진짜로 입력하면 안 된다
npx create-ait-app <app-name>을 그대로 치면 PowerShell이 <, >를 리다이렉션 연산자로 해석해서 에러 난다.
문서의 꺾쇠괄호는 "여기에 값 넣으세요" 표기일 뿐!
{깃허브아이디} 같은 중괄호도 마찬가지로 git에서 unknown switch 에러를 냈다...
플레이스홀더는 기호까지 빼고 입력하자.
폰에서 로컬 서버 접속이 안 될 때
localhost는 "그 기기 자신"이라서 폰에 입력하면 폰 자신을 찾는다(...)
vite.config.js에 아래를 추가하면 Network: 주소가 뜨고, 같은 와이파이의 폰에서 그 주소로 접속 가능하다.
server: { host: true }
스캐폴딩 도구가 만든 코드도 의심하기
create-ait-app에서 예제 코드를 선택 안 했더니
{{SAMPLE_IMPORTS}} 같은 템플릿 플레이스홀더가 치환 안 된 채 코드에 남아서
SAMPLE_IMPORTS is not defined 에러가 났다.
CLI가 만들어준 코드라고 무조건 믿으면 안 되더라......
4. Git 대참사: .gitignore는 git add 전에!!
.gitignore 없이 git add . → 커밋 → push를 해버려서 node_modules 포함 63,444개 파일, 170MB가 통째로 올라갔다 ㅋㅋㅋ큐ㅠ
- 한 번 커밋된 파일은 나중에 지워도 히스토리에 남아서 저장소 용량이 안 줄어든다
- 첫 커밋 직후였어서: 저장소 삭제 → .git 폴더 삭제 → .gitignore 확인 → 재커밋으로 수습
- 앞으로는 git add . 하고 커밋 전에 git status로 node_modules 없는지 확인하는 습관 들이기
- 보너스 삽질: README .md(공백 포함)로 저장하면 깃허브가 README로 안 보여준다. 파일명은 정확히 README.md!
내일 할 일
- [✅] 재검수 결과 확인 (평균 2~3일 걸린대서 기다리는 중)
- [✅] 챌린지 신청폼 제출 (7/29 마감 — 검수 승인 전에도 제출 가능하니 미리미리)
- [✅] 승인되면 토스 앱에서 실기기 최종 확인 (위치 권한, 알림 동작)
기획부터 검수까지 혼자 다 해보니까, 코드 짜는 시간보다 플랫폼 규칙 이해하고 환경 문제 푸는 시간이 훨씬 길었다.
근데 그게 다 경험치... 승인 나면 토스에서 만나고싶다..!!
다들 우산챙겨!!!!! ☔
썸네일출처 : https://mobile.newsis.com/photo/NISI20190809_0000376647#_PA
'끄적끄적 개인 개발' 카테고리의 다른 글
| 🥭 망고의 라이어 게임 제작기: 기획부터 배포, 그리고 마주친 에러들 (1) | 2026.07.23 |
|---|