← 주요 프로젝트← Key projects

2025 SDHS 해커톤 · TEAM LBM

2025 SDHS HACKATHON · TEAM LBM

Benefit:ON

Benefit:ON

청소년 할인 매장을 검색하는 React 웹 앱입니다. 거리 필터, 즐겨찾기, Gemini 추천과 영수증 분석을 구현했습니다.

A React app for finding youth discounts, with distance filters, favorites, Gemini recommendations and receipt analysis.

REACT 19TYPESCRIPTVITEGEMINI APIGEOLOCATIONVERCEL
담당 작업Work involved

웹 UI · 검색/필터 · 로컬 상태 · Gemini 연동 · 배포

Web UI, search and filtering, local state, Gemini integration and deployment

현재 상태Current status

Vercel에 프로토타입 공개 · 사용자 계정과 서버 DB는 미구현

Prototype on Vercel; user accounts and server database not implemented

01

프로젝트 개요

청소년 할인 탐색 웹 앱의 사용자 흐름과 실제 구현 범위

Benefit:ON V2는 영화관, 음식점, 쇼핑, 문화와 스터디 매장의 청소년 할인을 찾는 React 웹 앱이다. 검색어, 카테고리와 거리로 매장을 걸러 볼 수 있다. 즐겨찾기와 최근 본 매장, Gemini 추천, 영수증 이미지 분석도 제공한다.

사용자 목표 구현된 흐름 저장 위치
할인 매장 찾기 검색어·카테고리·현재 위치 거리 필터 정적 카탈로그 + React 파생 상태
다시 볼 매장 관리 즐겨찾기·최근 본 목록 브라우저 localStorage
취향에 맞는 추천 받기 선호 문장 → Gemini → 매장 ID 매핑 요청 중 메모리
영수증 혜택 확인 이미지 업로드 → Gemini 분석 → 기록 미리보기 메모리 + 로컬 기록

구현 범위

저장소에는 React 화면, 검색과 거리 계산, 모달과 하단 메뉴, 로컬 즐겨찾기, 이미지 미리보기, Gemini 호출, Vite 빌드와 Vercel 배포 설정이 포함된다.

현재 상태

공개 Vercel 주소에서 실행되는 클라이언트 중심 프로토타입이다. 회원 계정, 서버 데이터베이스, 관리자 카탈로그 편집, 서버 측 비밀 관리와 분석 이력 동기화는 없다. 실제 사용자 전환율, 추천 품질과 접근성 성과는 측정되지 않음이다.

01

Project overview

User journey and implemented scope of the youth-benefit discovery web app

Benefit:ON V2 is a React web app for finding youth discounts at cinemas, restaurants, shops, cultural venues and study stores. Users filter stores by query, category and distance, save favorites, view recent stores, and use Gemini recommendations and receipt analysis.

User goal Implemented flow Storage
Find a benefit Query, category and nearby-distance filters Static catalog + derived React state
Return to a store Favorites and recent views Browser localStorage
Receive guidance Preference text → Gemini → store-ID mapping In-memory request state
Inspect a receipt Image upload → Gemini analysis → history Preview memory + local history

Implementation

The repository includes React screens, search and distance calculations, modals, bottom navigation, local favorites, image previews, Gemini calls, Vite builds and Vercel deployment settings.

Current status

This is a deployed, client-centric prototype. It has no user account, server database, catalog administration, server-side secret boundary or synchronized analysis history. User conversion, recommendation quality and accessibility outcomes are not measured.

02

아키텍처

React 상태, 로컬 데이터와 외부 AI 요청이 만나는 상세 웹 실행 구조

1. 빌드와 전달 경계

index.tsx가 App을 마운트하고 Vite가 React 19·TypeScript 소스를 브라우저 번들로 만든다. 매장 이미지와 정적 HTML은 같은 배포 결과에 포함되어 Vercel CDN에서 전달된다. 별도 서버 렌더링이나 API route가 없으므로 첫 HTML 뒤의 검색·모달·AI 동작은 모두 브라우저에서 수행된다.

2. 화면과 상태 오케스트레이션

App.tsx가 매장 원본, 검색어, 선택 카테고리, 위치 모드, 활성 화면, 모달, AI 응답, 영수증, 즐겨찾기와 최근 본 항목을 소유한다. Header, MenuBar, StoreCard, Modal은 props와 callback으로 이 상태를 표시하거나 변경한다. 한 파일에서 제품 흐름을 추적하기는 쉽지만 상태와 비동기 처리가 커질수록 회귀 범위도 함께 넓어진다.

3. 탐색 데이터 흐름

constants.tsx의 STATIC_STORE_DATA가 원본 카탈로그다. effect가 카테고리와 검색어를 적용하고, 근처 모드에서는 Haversine 거리 계산 결과로 필터링·정렬해 filteredStores를 만든다. 서버 조회가 없어 응답은 즉시 일관되지만 혜택 변경은 새 정적 빌드가 배포되기 전까지 반영되지 않는다.

4. 개인화와 AI 흐름

즐겨찾기, 최근 본 매장과 영수증 이력은 세 개의 localStorage 키로 저장된다. 추천은 사용자의 선호 문장과 매장 목록을 Gemini에 보내고 응답의 ID를 기존 카탈로그에 다시 매핑한다. 영수증은 FileReader로 미리보기를 만들고 이미지 bytes와 MIME type을 AI 요청에 포함해 구조화된 분석 결과를 받는다.

5. 신뢰·실패·보안 경계

위치 권한 거절은 오류 상태 또는 강남·용산 데모 좌표로 대체할 수 있다. Gemini 응답은 JSON 형태를 기대하므로 비정상 형식과 존재하지 않는 매장 ID를 걸러야 한다. VITE_API_KEY가 클라이언트 번들에 주입되는 구조는 운영 비밀을 보호하지 못하므로 실제 서비스에서는 서버 프록시, 요청 제한, 입력 크기 검증과 관측 가능한 오류 계약이 필요하다.

6. 데이터·상태 소유권

매장 원본은 constants.tsx, 화면 전환과 검색 결과는 App.tsx, 즐겨찾기·영수증·최근 본 항목은 브라우저 localStorage가 소유한다. 서버 동기화가 없으므로 장치 간 일관성은 미구현이며 삭제·만료 정책도 명시적으로 추가해야 한다.

7. 성능 병목과 복구

단일 App 컴포넌트의 넓은 상태 범위와 전체 카탈로그 필터링은 데이터 증가 시 재렌더 비용을 키운다. 이미지 lazy loading, 파생 목록 memoization, Gemini 요청 취소와 timeout이 다음 검증 지점이며, 외부 API 실패 때 정적 카탈로그 탐색은 계속 남는다.

8. 관측·검증·기술 부채

현재 사용자 오류는 toast로 드러나지만 요청 단계별 latency·failure telemetry는 미구현이다. 클라이언트 비밀 제거, 영수증 개인정보 보존 정책, App 상태를 도메인 hook으로 분리하는 작업이 우선 기술 부채이며 운영 성과는 측정되지 않았다.

02

Architecture

Detailed web runtime across React state, local data and external AI requests

1. Build and delivery boundary

index.tsx mounts App, while Vite turns React 19 and TypeScript into a browser bundle. Store images and static HTML ship in the same deployment and are delivered from Vercel. There is no server rendering or API route, so search, modals and AI actions execute after the browser receives the shell.

2. Presentation and state orchestration

App.tsx owns the source catalog, query, category, nearby mode, active view, modals, AI response, receipt state, favorites and recent stores. Header, MenuBar, StoreCard and Modal receive this state through props and callbacks. The flow is visible in one place, but async behavior and regression scope grow with that central component.

3. Discovery data path

STATIC_STORE_DATA in constants.tsx is the source catalog. An effect applies category and text filters; nearby mode also calculates Haversine distance and sorts the derived filteredStores. Discovery stays deterministic offline, while benefit changes require a new static build.

4. Personalization and AI path

Favorites, recent stores and receipt history use three localStorage keys. Recommendations send preference text and catalog context to Gemini, then map returned IDs back to known stores. Receipt input uses FileReader for preview and sends image bytes with a MIME type to obtain a structured analysis result.

5. Trust, failure and security boundaries

Denied geolocation becomes an error state or a Gangnam/Yongsan demo coordinate. Gemini output is expected as JSON, so malformed shapes and unknown IDs require filtering. Because VITE_API_KEY is injected into the client bundle, a production system needs a server proxy, secret management, request limits, input-size checks and an observable error contract.

6. Data and state ownership

constants.tsx owns the source catalog, App.tsx owns navigation and result state, and browser localStorage owns favorites, receipts and recent items. Cross-device consistency is missing because no server synchronization exists; retention and expiration also need explicit policy.

7. Performance bottlenecks and recovery

The broad state surface in one App component and full-catalog filtering increase render cost as data grows. Image lazy loading, memoized derived lists, cancellation and timeouts for Gemini are the next validation points; static catalog discovery remains available when the external API fails.

8. Observability, validation and debt

Toasts expose user-facing errors, but stage-level latency and failure telemetry are missing. Removing client secrets, defining receipt-data retention and splitting App state into domain hooks are the primary debts. Production outcomes have not been measured.

구조와 데이터 흐름

Structure and data flow

도식을 누르면 크게 볼 수 있습니다. 구현 여부와 참고한 코드도 표시했습니다.

Select a diagram to enlarge it. Labels show implementation status and source files.

01
시스템 컨텍스트 · 신뢰 경계System context · trust boundaries청소년 사용자, 브라우저 앱, 위치·Gemini 사이의 경계입니다.Boundaries between youth users, the browser app, location and Gemini.
선택하면 전체 화면에서 세부 구조와 근거 번호를 볼 수 있습니다.Select to inspect the structure and evidence references full screen.
02
런타임 · 모듈 · 상태 소유권Runtime · modules · state ownershipApp.tsx가 화면 상태를 소유하고 서비스·로컬 저장소를 조정합니다.App.tsx owns UI state and coordinates services and local storage.
선택하면 전체 화면에서 세부 구조와 근거 번호를 볼 수 있습니다.Select to inspect the structure and evidence references full screen.
03
핵심 사용자 흐름 · 요청 시퀀스Core user flow · request sequence근처 혜택 탐색과 AI 추천이 결과 카드로 합쳐지는 시퀀스입니다.Sequence merging nearby discovery and AI recommendations into result cards.
선택하면 전체 화면에서 세부 구조와 근거 번호를 볼 수 있습니다.Select to inspect the structure and evidence references full screen.
04
데이터 · 배포 · 보안 · 복구Data · delivery · security · recoveryVite 정적 배포, 브라우저 저장과 클라이언트 AI 호출의 위험을 표시합니다.Shows Vite delivery, browser persistence and client-side AI-call risks.
선택하면 전체 화면에서 세부 구조와 근거 번호를 볼 수 있습니다.Select to inspect the structure and evidence references full screen.
03

기술 결정

빠른 웹 프로토타입을 위한 선택과 그에 따른 비용

React + Vite 단일 페이지 앱

매장 탐색과 여러 모달의 상태 전환을 빠르게 구성하기 위해 React와 Vite를 선택했다. 정적 호스팅과 빠른 개발에는 유리하지만 URL별 화면 상태, 서버 렌더링과 서버 비밀 영역은 제공하지 않는다.

정적 카탈로그를 원본으로 사용

외부 데이터베이스 없이 재현 가능한 데모를 만들기 위해 매장과 혜택을 TypeScript 상수로 관리한다. 네트워크 장애 없이 탐색할 수 있는 대신 운영자가 즉시 데이터를 갱신하거나 변경 이력을 관리할 수 없다.

로컬 저장으로 개인화

계정 시스템을 만들지 않고 즐겨찾기·최근 본 매장·영수증 기록을 유지하기 위해 localStorage를 사용했다. 구현 비용은 낮지만 기기 간 동기화, 사용자 삭제 정책, 다중 사용자 분리는 제공하지 않는다.

브라우저 Geolocation과 데모 좌표

실제 위치 기반 정렬을 제공하면서 권한 거절 상황도 시연하기 위해 브라우저 위치 API와 강남·용산 데모 위치를 함께 둔다. 데모는 안정적이지만 실제 영업 상태나 이동 시간 대신 직선거리만 계산한다.

Gemini 직접 연동

추천과 영수증 vision 흐름을 짧은 경로로 검증하기 위해 브라우저에서 Gemini SDK를 호출한다. 제품 가설 확인에는 빠르지만 키 노출, 악성 요청, 비용 통제와 rate limit을 애플리케이션 서버에서 관리할 수 없다.

03

Decisions

Choices made for a fast web prototype and the costs they introduce

React and Vite as a single-page app

React and Vite make the catalog and modal-heavy interaction quick to assemble and deploy statically. The tradeoff is no route-level state, server rendering or trusted server execution boundary.

Static catalog as the source of truth

TypeScript constants make the demo reproducible without a database. Discovery remains available without an upstream service, but operators cannot update benefits immediately or retain change history.

Browser-local personalization

localStorage preserves favorites, recent views and receipts without building identity. It does not provide cross-device sync, user separation or a complete deletion policy.

Geolocation with demo coordinates

The browser API supports real nearby sorting, while Gangnam and Yongsan coordinates keep the feature demonstrable after permission denial. Results are straight-line estimates rather than travel time or live business availability.

Direct Gemini integration

Calling Gemini from the client validates recommendation and vision flows quickly. It leaves credential exposure, malicious requests, cost control and rate limiting outside an application server.

04

검증과 한계

웹 기능별 재현 시나리오와 아직 증명되지 않은 영역

핵심 탐색 시나리오

  • 검색어와 카테고리를 함께 적용했을 때 교집합만 남는지 확인한다.
  • 위치 권한 허용·거절·미지원과 두 데모 위치에서 정렬과 안내 문구를 확인한다.
  • 매장 상세를 연 뒤 최근 본 목록과 즐겨찾기가 새로고침 후에도 유지되는지 확인한다.
  • 결과가 없는 검색에서 빈 상태가 표시되고 기존 카드가 남지 않는지 확인한다.

AI·파일 시나리오

  • 선호 입력이 비어 있을 때 요청을 막고, 성공 응답의 매장 ID만 카탈로그에 매핑하는지 확인한다.
  • Gemini 오류, 빈 추천, 비정상 JSON과 존재하지 않는 ID를 각각 확인한다.
  • 카메라·갤러리 입력, 이미지 미리보기, 분석 중 상태와 분석 실패 후 재시도를 확인한다.
  • 영수증 결과가 기록에 추가되고 브라우저 재접속 뒤 복원되는지 확인한다.

자동 검증 상태

저장소에는 dev, build, preview 스크립트가 있지만 단위·컴포넌트·E2E 테스트 스크립트는 확인되지 않는다. 브라우저별 호환성, Core Web Vitals, 추천 정확도와 접근성 점수는 측정되지 않음이다.

알려진 한계

  • 매장 데이터와 이미지가 정적 번들에 결합되어 있다.
  • 개인화 데이터는 현재 브라우저에만 남는다.
  • AI 키와 요청이 클라이언트에 노출되는 구조다.
  • 영수증 이미지는 민감정보 마스킹·보존 정책이 없는 프로토타입 입력이다.
  • AI가 반환한 혜택은 운영 데이터와 별도로 검증해야 한다.
04

Validation and limits

Reproducible web scenarios and behavior that remains unproven

Core discovery scenarios

  • Combine a query and category and verify only their intersection remains.
  • Exercise allowed, denied and unsupported geolocation plus both demo locations.
  • Open a store and verify recent views and favorites survive a reload.
  • Confirm an empty query result removes stale cards and exposes a clear empty state.

AI and file scenarios

  • Block empty preferences and map only known store IDs from a successful response.
  • Exercise Gemini errors, empty recommendations, malformed JSON and unknown IDs.
  • Test camera and gallery input, preview, loading, failure and retry states.
  • Confirm successful receipt output is appended and restored after reopening the site.

Automated evidence

The repository defines dev, build and preview, but no unit, component or end-to-end test script is present. Cross-browser behavior, Core Web Vitals, recommendation accuracy and accessibility scores are not measured.

Known limits

  • Store data and images are coupled to the static bundle.
  • Personal state remains on the current browser.
  • AI credentials and requests cross the client boundary.
  • Receipt images have no production privacy, redaction or retention contract.
  • AI-generated benefit claims require independent verification.
05

로드맵

완료된 웹 흐름, 우선 개선과 명시적으로 제외한 범위

완료

  • 반응형 매장 카드, 검색, 카테고리와 거리 기반 탐색
  • 즐겨찾기, 최근 본 매장과 영수증 이력 로컬 저장
  • Gemini 선호 추천과 이미지 영수증 분석
  • 위치 권한 실패를 위한 데모 위치
  • Vite 프로덕션 빌드와 Vercel 공개 배포

우선 개선

  • Gemini 요청을 서버 프록시 뒤로 이동하고 키·rate limit·입력 크기 보호
  • 카탈로그 repository와 UI state를 App.tsx에서 분리
  • 검색·거리·AI 응답 파서 단위 테스트와 주요 모달 E2E 추가
  • 영수증 업로드 전에 개인정보 안내와 이미지 폐기 계약 제공
  • focus trap, 오류 announcement와 키보드 탐색 접근성 검증

이후 계획

  • 관리 가능한 혜택 데이터 API와 갱신일·출처 표시
  • 계정 기반 즐겨찾기 동기화와 데이터 내보내기·삭제
  • 추천 결과의 근거, 불확실성, 사용자 피드백 기록
  • 성능·오류율·검색 성공률을 측정하는 운영 관측

범위에서 제외

할인 자격 보증, 결제·쿠폰 발급, 영수증 회계 처리와 자동 구매 판단은 현재 프로토타입의 범위가 아니다. 실제 제공 여부는 각 매장의 최신 정책을 사용자가 다시 확인해야 한다.

05

Roadmap

Completed web flows, priority improvements and explicit exclusions

Completed

  • Responsive store cards with query, category and nearby discovery
  • Browser persistence for favorites, recent views and receipt history
  • Gemini preference recommendations and image receipt analysis
  • Demo coordinates for denied location access
  • Production Vite build and public Vercel deployment

Priority improvements

  • Move Gemini behind a proxy with secret, rate and payload protection
  • Separate catalog repositories and UI state from App.tsx
  • Add unit tests for search, distance and AI parsing plus modal E2E coverage
  • Explain receipt privacy and disposal before upload
  • Verify focus trapping, error announcements and keyboard interaction

Later work

  • Managed benefit API with source and update timestamps
  • Account-backed sync, export and deletion
  • Recommendation reasoning, uncertainty and user feedback
  • Production measurement for performance, failures and search success

Out of scope

Eligibility guarantees, payments, coupon issuance, receipt accounting and automated purchase decisions are outside this prototype. Users must confirm the latest policy with each provider.

확대 보기Expanded view

도식을 좌우로 이동하거나 확대해 세부 흐름을 확인할 수 있습니다.

Pan or zoom the diagram to inspect the detailed flow.