모든 '바깥 활동' 앱이 걷는 것을 전제로 할 때, 저는 오늘 얼마나 갈 수 있는지 묻습니다
요약
Doorstep은 기존의 '걸음 수'나 '연속 기록' 같은 목표 기반 앱들과 달리, 사용자가 오늘 실제로 걸을 수 있는 최대 거리(상한선)를 측정하는 새로운 접근 방식을 제시합니다. 이 거리를 바탕으로 로컬 오픈 웨이트 모델에게 도달 가능한 최적의 장소를 추천받는 것이 핵심입니다.
핵심 포인트
- 기존 목표 기반 앱 대신 '오늘 얼마나 갈 수 있는지'에 초점을 맞춤.
- 사용자의 상한선(ceiling)을 측정하여 현실적인 활동 범위를 제시함.
- 측정된 거리를 바탕으로 로컬 오픈 웨이트 모델이 최적의 장소를 추천받음.
- 걸음 수, 속도 등 기존 지표를 배제하고 순수한 도달 가능성에 집중함.
이것은 Hacktoberfest Open-Source AI Challenge Week 1: Touch Grass에 제출하는 작품입니다.
제가 만든 것
저는 이 챌린지에 다른 참가자들의 글들을 읽고 나서야 한 줄을 작성했습니다. 약 아흔 개가 넘었습니다. 퀘스트 생성기, 트레일 가이드, 새소리 식별기, 식물군계 경로 계획기, 밖에 나가도록 휴대폰 잠금 기능을 하는 앱들까지 다양합니다.
이 모든 것들은 당신을 막는 것이 동기 부여(motivation)라고 가정합니다.
하지만 그렇지 않은 사람들을 위한 것은 없습니다. 수술 후 3주가 지난 사람. 매일 아침 무릎이 얼마나 걸을 의향이 있는지 결정하는 사람. 길 끝까지 갔다가 돌아오는 정도만 할 수 있는 여든 살 노인, 그리고 어떤 날은 그것조차 못 하는 사람 말입니다.
Doorstep은 다른 앱들이 묻지 않는 단 하나의 질문을 던집니다:
오늘 얼마나 가셨나요?
목표(target)가 아닙니다. 상한선(ceiling)입니다. 어떤 날은 그 수치가 더 작아도 괜찮습니다.
오늘의 수치를 받아, 그 안에 있는 모든 것을 진정으로 파악하고, 로컬 오픈 웨이트 모델(local open-weight model)에게 걸을 만한 하나의 장소를 선택해 달라고 요청합니다.
How far today? [=====o----------------] 200 metres
Balbhim Mandir
...
그리고 나중에, 또 하나의 질문: 가셨나요? 이것이 전체 루프입니다. 걸음 수(steps)도 없고, 속도(pace)도 없고, 칼로리(calories)도 없고, 연속 기록(streak)도 없습니다.
데모
Doorstep은 당신에 대해 두 가지 정보를 가지고 있습니다: 오늘 몸이 얼마나 버틸 수 있는지, 그리고 그 미터법이 측정되는 정확한 지점입니다. 이 둘을 합치면 주소와 부착된 의료 기록(medical record)과 같습니다.
이는 데모를 만드는 데 문제가 됩니다. 앱은 좌표가 아닌 장소 이름을 표시합니다.

제가 가장 자랑스러운 부분은 두 번째 화면입니다. 가장 가까운 지도상의 장소 아래의 슬라이더를 움직여도 Doorstep이 늘어나거나, 임시로 처리하거나, 혹은 낮춰서 계산하지 않습니다. 정확하게 그 값을 보여줍니다.

작성자의 정보를 공개하지 않고도 스스로를 증명할 수 있는 도구여야 합니다.
코드
GitHub logo imarpanpatra / doorstep
Doorstep
외부 활동을 하는 사람들을 위해, 외부가 200미터인 경우.
당신을 외출하게 만들고 싶어 하는 모든 앱은 당신을 막는 유일한 것이 동기 부여(motivation)라고 가정합니다. 하지만 Doorstep은 정반대를 가정합니다. 즉, 이미 가고 싶다는 마음이 있고, 한계는 오직 신체라는 것입니다. 이 앱은 다른 어떤 앱도 묻지 않는 단 하나의 질문만 합니다.
오늘 얼마나 갈 수 있나요?
목표가 아닙니다. 상한선(ceiling)입니다. 어떤 날은 그 상한선이 더 작을 수도 있고, 그것 또한 허용됩니다.
[Hacktoberfest Open-Source AI Challenge]의 Week 1: Touch Grass를 위해 제작되었습니다.
아이디어
피트니스 앱들은 거리를 극복해야 할 숫자로 취급합니다. 연속 기록(Streaks), 개인 최고 기록(personal bests),
Gemma 3 4B를 Ollama를 통해 로컬에서 실행합니다. 주변 지역에 대한 OpenStreetMap 데이터를 단 한 번 다운로드했습니다. Node와 하나의 HTML 페이지로 구성되어 있으며, 데몬은 루프백(loopback)에 바인딩됩니다.
중요한 규칙
모델이 무엇을 선택할지 결정한다. 얼마나 멀리까지는 절대 스스로 정하지 않는다.
어떤 장소든 모델에게 질문이 들어가기 전에 코드 상에서 오늘 도달 가능한 범위 내의 지점들과 필터링됩니다:
const eligible = places.filter((p) => p.metres <= reach);
Gemma가 이 목록을 볼 때, 그 안에 있는 모든 선택지는 이미 안전합니다. 최악의 경우에도 단지 평범한 장소를 고를 뿐입니다.
이것은 구조적인 깔끔함(architectural tidiness) 때문이 아닙니다. "조금 더 멀리"라는 말을 기쁘게 제안하는 언어 모델은, 이 사용자에게는 집에서 두 블록 떨어진 곳에 앉을 곳도 없이 방치되는 앱과 같습니다. 거리는 이 제품에서 판단의 영역이 되어서는 안 되는 유일한 것이므로, 산술적인 계산(arithmetic)이어야 합니다.
목록에 없던 장소를 지칭하는 선택은 제안이라기보다는 환각(hallucination)으로 간주되어 가장 가까운 좌석 위치로 돌아갑니다.
아무것도 없을 때의 응답
오늘 당신 주변 100미터 이내에는 매핑된 곳이 없습니다.
여기가 지도가 빈약한 것이지, 당신이 부족한 것은 아닙니다.
그것은 조용히 반경을 넓혀 무언가를 찾으려 하지 않습니다. 벤치를 만들어내지도 않습니다. 이 사용자에게는 꾸며낸 목적지가 아무 답변이 없는 것보다 더 나쁜데, 왜냐하면 그들은 그곳으로 걸어갈 것이기 때문입니다.
같은 앱도 다음 도시에서는 다른 앱이 된다
저는 약 300km 떨어진 두 개의 인도 도시에 대해 지도를 두 번 가져왔습니다.
| 규모가 작은 도시 | 규모가 큰 도시 | |
|---|---|---|
| 1200m 이내 장소 수 | 28개 | 701개 |
| ... |
같은 코드, 같은 반경, 같은 질의(query)를 사용했습니다. 더 큰 도시에서는 Doorstep이 만디르(mandir), 카페, 그리고 선택할 수 있는 500개의 매핑된 나무들을 가지고 있습니다. 작은 도시에서는 거리가 비어있기 때문이 아니라 OpenStreetMap 자체가 그곳에 대해 알지 못하기 때문에 '문 근처에는 아무것도 없다'고 말해야 합니다.
첫 번째로 데이터를 가져온 후 저의 첫 본능은 '인디아에는 벤치 지도가 없다'라고 쓰는 것이었습니다. 두 번째 데이터 요청을 통해 그것이 틀렸다는 것을 알았고, 실제 답변은 더 흥미롭습니다. OSM(OpenStreetMap)은 자원봉사자들이 지도화한 것이기 때문에, 가구 시설물 레이어는 벤치가 어디에 있는지가 아니라 지도 제작자들이 어디에 사는지를 추적합니다. '200미터 거리에 앉을 곳이 있다'는 것이 앱의 전체 약속인 앱에게, 이는 데이터가 다른 사람이 자신들을 위해 앱을 만들 가능성이 가장 낮은 바로 그 장소에서 가장 희박하다는 것을 의미합니다.
따라서 목적지로 간주되는 목록은 유럽 지도를 보고 작성할 때 생각하는 목록과 다릅니다:
| 여기저기 지도화된 곳 | 거의 아무데도 지도화되지 않은 곳 |
|---|---|
| 사원(temples) 및 만디르(mandirs): 그늘지고 조용하며, 앉을 수 있음 | 벤치 |
| ... | |
| 두 목록 모두 쿼리에 포함되어 있습니다. 벤치가 존재하는 곳에서는 그것이 최선의 답변입니다. 벤치가 없는 곳에서는 아침 7시의 사원 안뜰이 실제로 앉을 수 있는 장소이며, 지도에도 표시되어 있습니다. |
거의 출시할 뻔했던 버그
이 지도는 자원봉사자들이 운영하는 무료 서비스인 Overpass에서 가져왔기 때문에, 저는 여러 미러를 시도하는 루프를 작성했습니다. 첫 번째 버전은 응답을 다음과 같이 구조 분해(destructure)했습니다:
({ elements = [] } = await r.json());
이 기본값 설정은 함정입니다. HTTP 200으로 데이터를 보내지 않은 미러는 빈 배열을 생성했을 것이고, 이는 참(truthy)으로 간주되어 성공으로 계산되었으며, Doorstep은 빈 지도를 조용히 저장했을 것입니다.
그 버그가 사용자에게 보여준 결과: 자신의 현관문 근처에는 아무것도 없다고 알려주는 앱. 단 하나의 = [] 때문에 발생할 수 있는 프로젝트가 내놓을 수 있는 가장 낙담스러운 메시지입니다.
이제는 요소(elements)가 없는 200 응답을 실패한 미러로 처리하고 넘어갑니다. 제대로 진단할 가치가 있는 다른 것들도 있습니다: kumi.systems는 500을 반환하고, overpass.osm.ch는 200 상태와 함께 HTML을 반환하며, 오직 overpass-api.de만이 실제로 응답합니다.
의도적으로 스트릭(streaks) 없음
스트릭(Streaks)은 나쁜 날을 벌합니다. 여기서 '나쁜 날'이란 의지의 실패가 아니라, 그저 살아가는 조건 자체를 의미합니다. 스트릭 카운터는 일주일 동안 몸이 쉬었던 사람에게 무언가를 망가뜨렸다고 알려줍니다.
따라서 이 카운터는 한 달 동안 밖에 나간 날짜들을 계산하며, 어떤 힘든 한 주도 지울 수 없습니다. 그리고 '나가지 않은 날'의 평균 거리가 아니라, '나간 날'에 기록된 평균 도달 거리만 보여줍니다. 모델에게는 또한 거리(distance), 속도(pace), 진전(progress), 더 노력하기(trying harder) 또는 사람의 건강 상태에 대해 언급하지 않도록 지시합니다. 코치처럼가 아니라 무언가를 알아차린 친구처럼 쓰도록 합니다.
Open Innovation은 왜 중요할까요?
세 가지 이유가 있는데, 그중 첫 번째 이유는 스스로 결정합니다.
1. 이것은 건강 데이터이며, 당신의 주소에 붙어 있습니다. 오늘 150미터를 걸었고 어제는 400미터를 걸었다는 것을 아는 앱은 당신의 몸이 나아지고 있는지 아니면 악화되고 있는지를 기록하고 있습니다. Doorstep 역시 그 모든 미터가 측정된 정확한 지점을 알고 있어야 합니다. 슬라이더가 달린 의료 기록과 집 주소가 같은 파일에 존재한다는 의미입니다.
저는 이것을 구축하면서 얼마나 강하게 느꼈는지 모릅니다. 이 게시물의 모든 스크린샷은 제 현관문이 아니라 공원에 초점이 맞춰져 있고, data/ 폴더는 .gitignore에 포함되어 있어 실제 지도가 리포지토리(repo)에 절대 도달하지 않습니다. 저는 그저 본능적으로 그렇게 했습니다. 데모용으로 가상의 이력을 넣어 말이죠. 그러다가 그 본능이 바로 논거라는 것을 깨달았습니다. 만약 제가 해커톤 게시물에 이 두 가지 숫자를 함께 공개할 생각이 없다면, 아무리 멀리 걷지 못하는 사람을 대신해 매일 아침 누군가의 API에 이를 올릴 리가 없습니다.
여기에는 감사(audit)를 위한 네트워크 호출이 없기 때문에 문제가 되지 않습니다. 설정 후에는 네트워크 호출 자체가 없습니다.
2. 전파(radio)가 꺼져 있어도 작동해야 합니다. 지도는 한 번 다운로드하면 다시는 필요하지 않습니다. 그 이후 Doorstep은 지하 아파트, 선불 SIM 카드가 끊긴 곳, 신호가 없는 골목에서도 작동합니다. 폐쇄된 API는 필요한 바로 그 순간, 문 앞에 서서 위험을 감수할지 결정하는 사람에게 연결(connection)에 의존하게 만듭니다.
3. 답변당 비용이 들지 않습니다. 요청 건별로 요금을 부과하는 서비스는 은퇴자가 끄게 만드는 서비스입니다.
그리고 제가 예상치 못한 네 번째 아이디어. 이 기능을 작은 로컬 모델에 구축하면서 거리 계산 논리를 모델에서 완전히 분리할 수밖에 없었습니다. 왜냐하면 누군가에게 잘못된 결과가 피해를 줄 수 있는 숫자 문제에는 4B 모델을 신뢰할 수 없었기 때문입니다. 만약 최신 프론티어 API를 사용했다면, 저는 아마도 그것이 결정하도록 내버려두고 더 위험한 것을 출시했을 것입니다. 탈출구가 없다는 점이 더 안전한 설계를 만들었습니다.
수상 부문 (Prize Categories)
- Gemma 활용 최우수 (Best Use of Gemma)
Gemma 3 4B는 여기서 자신이 진정으로 잘하는 한 가지 일, 즉 실제 장소의 짧은 목록을 읽고 오늘 가치 있는 곳이 무엇인지 말해주는 일을 수행합니다. 동시에 누군가에게 피해를 줄 수 있는 모든 결정은 코드 내에 남아 있습니다. 이 모델은 노트북 CPU에서 실행되는데, 이것이 누군가의 이동성 기록을 담는 앱이 사용자 자신의 기기에 머무를 수 있게 하는 유일한 이유입니다.
AI 자동 생성 콘텐츠
본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기