Grass Gate: 야외에서만 피드가 해제되는 방식
요약
Grass Gate는 사용자가 웹사이트에서 과도한 스크롤링(doomscrolling)을 하는 것을 막기 위해 설계된 브라우저 확장 프로그램입니다. 이 시스템은 야외 활동과 연동되어, OpenStreetMap 기반의 실제 장소 퀘스트를 수행해야만 제한 시간 동안 콘텐츠에 접근할 수 있게 합니다.
핵심 포인트
- 야외에서만 피드가 해제되는 '둠스크롤' 방지 메커니즘 구현
- OpenStreetMap을 활용한 실제 위치 기반 퀘스트 제공
- 오픈-웨이트 비전 모델로 현장 사진의 야외 여부 및 내용 검증
- 사용자의 물리적 움직임과 연동된 디지털 디톡스 도구
이 글은 Hacktoberfest Open-Source AI Challenge Week 1: Touch Grass 제출물입니다.
제가 만든 것
Grass Gate는 야외에서만 열리는 '둠스크롤(doomscroll)' 잠금장치입니다.
이 브라우저 확장 프로그램은 제가 시간을 잃기 쉬운 웹사이트들을 차단합니다. 해당 사이트에 접속하면 죄책감을 주는 타이머가 뜨지 않습니다. 대신, 저는 임무를 받습니다. 휴대폰으로 Grass Gate를 열면, OpenStreetMap에서 근처의 실제 장소를 하나 골라줍니다:
💧 Mallard Lake는 남쪽으로 330m 거리에 있습니다. 그곳까지 걸어가서 물을 사진 찍으세요.
저는 휴대폰을 주머니에 넣고 걷습니다. 도착하면, 기기가 진동하고 소리가 납니다. 제가 사진을 찍으면, 휴대폰에서 구동되는 오픈-웨이트 비전 모델이 두 가지를 확인합니다: 실제로 야외인지, 그리고 그것이 정말 물인지? 둘 다 통과하면, 저는 일회용 6자리 코드를 받습니다. 이 코드를 차단된 페이지에 입력하면, 제 피드가 30분 동안 열립니다. 그러고 나면 다시 잠깁니다.
화면은 그저 잠금장치일 뿐입니다. 중요한 모든 것은 야외에서 일어납니다.
| 오늘 | 임무 완료 | 현장 기록 |
|---|---|---|
![]() | ![]() | ![]() |
이것은 뇌가 결정하기 전에 엄지손가락이 피드를 열어버리는 사람, 즉 저 같은 모든 사람을 위한 것입니다. 몇 가지 요소들이 이것을 단순한 장난감 이상의 것으로 만듭니다:
- "산책하기"가 아닌 실제 장소 기반 퀘스트. 주변에 실제로 지도화된 것(나무, 호수, 공원, 벤치, 전망대, 공공 예술 작품)에서 퀘스트를 받습니다. 각 퀘스트는 도보 시간과 방향을 보여줍니다.
- 휴대용 내비게이션 기능. 휴대폰과 함께 움직이는 나침반 화살표, 진행률 링(progress ring), 그리고 도착 시 진동 알림 기능을 제공하여 이동 중에 화면만 바라보고 있는 느낌이 들지 않도록 합니다.
- 현장 일지 (Field Journal). 완료된 모든 퀘스트는 사진, 장소, 시간과 함께 휴대폰에만 저장됩니다.
- 연속 기록(Streaks), 걸은 거리(km walked), 야외 활동 시간(minutes outside), 그리고 퀘스트 유형별로 수집 가능한 12개의 스탬프.
- 실제 하늘을 반영하는 홈 화면. 배경 풍경이 시간에 따른 낮과 계절에 따라 변화하며, 기기에서 계산된 "남은 일광 시간 2시간 10분 · 일몰 6:44 PM" 같은 표시가 나타납니다.
- 잠긴 페이지는 카운트를 유지합니다: "오늘 #7을 시도해 보세요." 이 숫자를 보는 것이 어떤 타이머보다 저에게 더 큰 의미를 주었습니다.

데모 (Demo)
휴대폰에서 사용해 보기: grassgate.vercel.app
브라우저를 열고 위치 및 카메라 접근을 허용한 후, **퀘스트 찾기(Find me a quest)**를 누르세요. 홈 화면에 추가하여 설치할 수 있습니다. 첫 사진 확인 과정을 거치면 비전 모델(vision model)이 캐시되어 신호가 없어도 작동합니다.
앱은 자체적으로 작동합니다: 퀘스트, 기기 내 사진 확인, 일지, 연속 기록 및 스탬프 기능입니다. 브라우저 확장 프로그램은 잠금 기능을 추가하며, 완료된 퀘스트에서 얻는 6자리 코드로 이를 해제할 수 있습니다. 책상에서 사용해 보고 싶다면, 설정(Settings)에서 **데모 모드(Demo mode)**를 켜서 도보 거리 확인 과정을 건너뛸 수 있습니다.
코드 (Code)
GitHub logo Rahul-Roy-Hub / grassgate
Hacktoberfest 오픈 소스 AI 챌린지: 1주차 (Week 1)
🌿 Grass Gate
외부 활동을 할 때까지 피드가 잠깁니다.
▶ 사용해 보기: grassgate.vercel.app (휴대폰에서 열기)
브라우저 확장 프로그램이 당신의 무한 스크롤 사이트를 차단합니다. 다시 접속하려면 휴대폰에서 Grass Gate를 켜야 합니다. 이 앱은 사용자 주변 실제 장소에서 미션을 하나 선택합니다("Elk Glen Lake가 남동쪽으로 430m 떨어져 있으니, 물을 사진 찍으러 가세요"). 그리고 당신을 그곳으로 안내합니다. 휴대폰의 오픈 웨이트 비전 모델이 사진을 확인하고, 당신은 1회용 6자리 코드를 받게 되며, 이 코드는 당신의 사이트를 30분 동안 잠금 해제합니다.
화면에는 오직 자물쇠만 보입니다. 전체 경험은 야외에서 일어납니다.
주요 기능
휴대폰 (PWA)
- 지도 기반 실제 미션: OpenStreetMap의 근처에 있는 나무, 호수, 공원, 벤치, 전망대 및 공공 예술 작품 등에서 도보 시간과 방향을 제공합니다.
- 포켓 내비게이션: 휴대폰에 나침반이 있을 경우 함께 돌아가는 나침반 화살표, 진행률 링, OSM 도보 경로 안내, 그리고 기타 기능들…
저장소: github.com/Rahul-Roy-Hub/grassgate
전체 앱은 순수한 HTML, CSS, JavaScript로 구성되어 있습니다. 설치 가능한 PWA이며, 번들러나 백엔드 없이 약 100KB의 앱 코드로 이루어져 있습니다. 확장 프로그램은 Chrome Manifest V3 확장 프로그램입니다.
제작 과정
노트북 (Chrome 확장 프로그램) 휴대폰 (PWA)
declarativeNetRequest로 피드 차단 GPS → OpenStreetMap (Overpass) → 근처 시설물
"풀밭에 가세요" 페이지 → 미션: 나무 / 물 / 공원 / 벤치 / 예술 / 전망
...
1. transformers.js를 통한 CLIP ViT-B/32: 심판 역할. 오픈 가중치(open-weight) 모델인 CLIP을 transformers.js를 통해 브라우저에서 실행합니다 (약 155 MB, 한 번 다운로드 후 캐시됨). 제로샷 분류(Zero-shot classification)란 제가 아무것도 학습시키지 않았다는 의미입니다. 분류기의 "지식"은 단지 영어 레이블 목록일 뿐이며, 이는 Is this even a valid card? Zero-shot image classification model in a lambda container에서 설명된 것과 같은 속임수입니다. 제 모델은 Lambda가 아닌 휴대폰에서 실행됩니다.
모든 사진에 대해 두 가지 검사를 수행합니다:
- 야외인가? (Outdoors?): "outdoors / nature / a street" 대 "indoors / a room / a screen". 저는 샘플 사진을 이용해 이 부분을 조정했습니다: 야외 사진은 0.56–0.86점을 기록했고 실내 사진은 0.20–0.29점을 기록했으므로, 기준점(cutoff)은 0.5입니다. 또한 모니터 위의 나무 사진도 거부합니다.
- 목표물인가? (Is it the target?): 이 부분에서 제가 처음으로 실수를 했습니다.
제 첫 번째 버전은 모든 동의어 점수를 합산했습니다: water + a river + a pond + a lake + a fountain. 시뮬레이션 카메라를 이용한 종단 간(end-to-end) 테스트 중, 모래 위의 해변 수건과 책 사진이 물 관련 질문에서 41%의 확률로 통과했습니다. 사진 속에는 물이 아무것도 없었습니다. 열세 개의 레이블 중 다섯 개가 물에 관한 것이었기 때문에, 모델이 무언가를 보기 전에 목표물 자체가 약 38%의 확률을 수집합니다. 제 기준점은 35%였습니다.
수정된 부분은 softmax 함수가 로짓(logit) 차이를 유지한다는 사실을 이용한 것입니다. 따라서 $\ln(p_a / p_b) = \text{logit}_a - \text{logit}_b$ 입니다:
// Best target label must rank top-3 AND beat the median decoy by ~2.7×.
const logit = Object.fromEntries(target.map((r) => [r.label, Math.log(r.score)]));
const best = Math.max(...quest.labels.map((l) => logit[l]));
...
18개의 오답(decoys)("sand", "a pavement", "an animal", "a book"…)를 사용하자, 내 샘플 사진 7장 모두가 올바르게 인식된다. 수건은 "물(water)을 찾을 수 없습니다. 책처럼 보입니다."라는 메시지와 함께 실패한다. 꽃 위의 나비와 잔디 위의 개는 성공적으로 인식된다. 7장의 사진은 적은 세트이므로, 내가 외부에서 테스트함에 따라 이러한 임계값(thresholds)들은 계속 변할 것이다. 이것들이 내 코드 속의 단순한 숫자이기 때문에, 나는 이 값들을 조정할 수 있다.
앱 역시 작동하는 모습을 보여준다. 모든 결과 아래에는 "온디바이스 모델이 본 내용(What the on-device model saw)" 목록에 실제 점수들이 나열된다. 여기서는 "an animal"가 CLIP의 최고 추측이지만, 잔디(grass)는 여전히 2위를 차지하며 마진을 쉽게 넘긴다:
2. Overpass API를 통한 OpenStreetMap: 퀘스트 제공자. 단일 Overpass 쿼리만으로 사용자가 선택한 반경 내의 나무, 물, 공원, 숲, 벤치, 놀이터, 전망대 및 공공 예술 작품을 가져올 수 있다. 퀘스트는 최소 120m 거리에 설정되어 있어 실제로 걸어 다녀야 하며, 마지막 지도는 신호가 없어도 작동하도록 캐시된다. 근처에 매핑된 것이 없거나 오프라인 상태라면, "어디서든" 퀘스트(하늘, 나뭇잎, 꽃, 잔디)를 받게 된다.
3. WebLLM을 통한 선택적 로컬 LLM: 이야기꾼. WebGPU를 사용하여 브라우저에서 실행되는 오픈 가중치 모델(Gemma 2 2B, Qwen2.5 0.5B 또는 Llama 3.2 1B이며, 설정에서 교체 가능)은 각 퀘스트를 친구가 외부로 부추기는 것처럼 다시 작성한다. LLM은 중요한 결정을 절대 내리지 않는다. 퀘스트 유형, CLIP 레이블 및 지오펜스(geofences)는 결정론적이다. 모델은 내가 이미 가지고 있는 사실들(장소 이름, 거리, 방향, 계절)만을 받아서 단어들을 작성할 뿐이다. 작거나 교체된 모델이 텍스트를 더 나쁘게 만들 수는 있지만, 존재하지 않는 장소로 사용자를 안내할 수는 없다.
4. 서버가 전혀 없음. 휴대폰과 확장 프로그램은 링크를 통해 또는 16자리 코드를 입력하여 비밀 정보를 한 번 공유합니다. 잠금 해제 코드는 해당 비밀 정보와 5분 시간 창의 HMAC-SHA256이며, TOTP와 동일한 아이디어로 양쪽에서 오프라인으로 확인됩니다. 각 퀘스트는 정확히 하나의 코드를 제공하며, 확장 프로그램은 재사용을 거부하고 잘못된 추측에 대해 속도 제한을 적용합니다.
Open Innovation이 중요한 이유?
이 앱의 핵심이 제가 사는 곳과 걸어 다니는 장소의 일일 지오태그 사진이기 때문입니다. Grass Gate는 폐쇄적인 비전 API로는 출시하지 않을 것입니다.
| Grass Gate (개방형, 온디바이스) | 폐쇄적 API에서의 동일한 아이디어 | |
|---|---|---|
| 사진 | 휴대폰을 벗어나지 않음 | 다른 사람의 서버에서 이루어지는 이웃 지역의 일일 사진 흔적 |
| ... | ||
| 마지막 줄은 개방형이 가장 어려운 부분에서 승리한 곳입니다. 비치 타월 버그는 제가 전체 확률 분포를 볼 수 있고 로짓(logits)으로 거슬러 올라갈 수 있었기 때문에만 수정할 수 있었습니다. 폐쇄적인 |
AI 자동 생성 콘텐츠
본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기


