My Service Links

푸쉬온 (Push On)App

얼굴 인식으로 푸쉬업을 자동으로 세고, 기록·통계·리더보드로 꾸준한 루틴을 돕는 모바일 앱

푸쉬온 (Push On)App

  • React Native
  • TypeScript
  • ARCore
  • Supabase

어떤 앱인가요

폰을 앞에 두고 푸쉬업을 하면 전면 카메라가 얼굴을 인식해 횟수를 자동으로 세어 줍니다. 손으로 세거나 별도 기기를 찰 필요 없이, 운동에만 집중하면 됩니다. 세트마다 횟수와 시간이 기록되고, 달력·통계·리더보드로 꾸준함을 이어갈 동기를 만듭니다.

주요 기능

  • 얼굴 인식 자동 카운트 — 카메라 앞에서 내려갔다 올라오는 동작을 인식해 실시간으로 횟수를 셉니다. 카메라와 너무 멀면 “가까이 오세요!“처럼 자세를 안내합니다.
  • 세트 기록 — 세트별 횟수·소요 시간을 저장하고, 일시정지·종료 흐름으로 운동을 관리합니다.
  • 히스토리 달력 — 운동한 날이 달력에 표시되고, 날짜를 누르면 그날의 푸쉬업 수·세트 수·총 시간과 세트별 상세 기록을 볼 수 있습니다.
  • 통계 — 주간/월간 푸쉬업 수·세트 수·운동 시간을 차트로 보고 총합·평균·최고 기록을 확인합니다.
  • 리더보드 — 일일·월간·연간 순위로 다른 사용자와 경쟁할 수 있습니다.
  • 소셜 로그인 — Google, Kakao 계정으로 간편하게 시작합니다.

어떻게 만들었나

  • 앱: React Native 0.79 (bare) + TypeScript. 화면 상태는 Jotai, 서버 상태는 TanStack Query, 내비게이션은 React Navigation, 차트는 react-native-chart-kit.
  • 얼굴 인식: Android 네이티브 모듈(Java)을 직접 작성했습니다. ARCore Augmented Faces로 얼굴을 추적하고, 푸쉬업 동작을 판정한 결과를 NativeEventEmitter로 JS에 전달해 UI가 실시간으로 반응합니다.
  • 백엔드: Supabase — 인증(Google/Kakao), 운동 기록 저장, 리더보드는 RPC 함수로 서버 측에서 집계해 네트워크 트래픽을 줄였습니다.
  • 운영: 온보딩 2단계 간소화, 앱 내 업데이트 확인, Android 16KB 페이지 크기 대응 등 출시 후 1.5.x까지 꾸준히 업데이트했습니다.

출시

  • Google Play 출시 (v1.5.3)
  • App Store 출시 예정

스크린샷

홈 화면 — Start 버튼과 오늘의 순위 얼굴 인식 카운트 화면 히스토리 달력과 세트별 기록 주간 푸쉬업 통계 차트 오늘의 순위 리더보드

sclean (Session Clean)CLI

여러 프로젝트에 흩어진 코딩 에이전트 세션을 한 화면에서 확인하고, 규칙 기반 추천으로 한 번에 안전하게 정리하는 터미널 UI

sclean (Session Clean)CLI

  • Rust
  • ratatui
  • TUI
  • npm
sclean 메인 화면 — 왼쪽 프로젝트 목록, 오른쪽 세션 목록

어떤 도구인가요

Claude Code, Codex, OpenCode 같은 코딩 에이전트를 여러 프로젝트에서 쓰다 보면 홈 디렉터리에 세션과 연결 데이터가 계속 쌓입니다. 각 도구의 세션 선택기는 과거 세션을 다시 여는 데는 좋지만, 여러 프로젝트의 불필요한 세션을 골라 한꺼번에 정리하는 흐름은 없습니다. sclean은 이 빈틈을 채우는 에이전트 세션 정리 전용 로컬 TUI입니다 — 왼쪽에 프로젝트, 오른쪽에 세션을 놓고 왜 추천하는지를 한 줄로 보여주며, 휴지통을 거쳐 안전하게 정리합니다.

v0.1은 Claude Code(~/.claude)를 지원하며, Codex·OpenCode 등 다른 에이전트 지원을 이어서 추가할 계획입니다. 어떤 에이전트를 지원하든 제품명과 실행 명령은 sclean 하나로 유지합니다.

npx session-clean   # 한 번 써보기
npm i -g session-clean && sclean

주요 기능

  • 한눈에 보기 — 프로젝트별 세션 수·추천 수·선택 수를 왼쪽 패널에, 선택한 프로젝트의 세션을 오른쪽 패널에 표시합니다. 터미널이 좁으면 현재 패널만 전체 폭으로 보여줍니다.
  • 설명 가능한 추천 — 오래된 세션(기본 30일), 사라진 프로젝트 경로, 메시지 1개짜리 짧은 세션, 종료된 하위 에이전트, 고아 데이터 등 5가지 규칙으로 정리 후보를 골라 이유를 함께 보여줍니다. 규칙은 F 화면에서 켜고 끌 수 있습니다.
  • 안전한 정리 — 실행 중인 세션, 분석할 수 없는 형식, 스캔 이후 바뀐 파일은 정리를 차단합니다. 기본은 휴지통 이동이며 완전 삭제는 DELETE를 직접 입력해야 실행됩니다.
  • 휴지통과 복원 — 옮긴 파일은 manifest.json과 함께 보관하고 R로 복원합니다. 원래 경로에 파일이 있으면 덮어쓰지 않고 충돌로 표시합니다.
  • 색에 의존하지 않는 UI — [x] 선택 · ★ 추천 · ! 분석 불가 · ▶ 실행 중처럼 기호만으로 상태를 구분할 수 있습니다.

어떻게 만들었나

  • 언어/프레임워크: Rust (edition 2024) + ratatui + crossterm. 세션 스캔은 rayon으로 병렬화해 2,000개 세션 기준 성능 테스트를 둡니다.
  • 데이터 안전성: 모든 대상 경로가 에이전트 데이터 디렉터리(현재 ~/.claude) 안에 있는지 확인하고 심볼릭 링크는 따라가지 않습니다. 파일을 옮기기 전에 매니페스트를 먼저 기록해 도중에 강제 종료돼도 다음 실행에서 복구를 제안하고, history.jsonl 같은 공유 파일은 백업 → 임시 파일 → 원자적 교체로 수정합니다.
  • 테스트: 실제 ~/.claude를 건드리지 않고 임시 디렉터리 fixture만 쓰는 테스트 182개. 렌더 결과를 스냅샷으로 검증합니다.
  • 배포: npm에 플랫폼별 네이티브 바이너리(session-clean-darwin-arm64 등 4종)를 optionalDependencies로 올리고, 얇은 JS 런처가 os/cpu에 맞는 것을 찾아 실행합니다. 태그를 밀면 GitHub Actions가 네 플랫폼을 빌드·테스트해 npm과 GitHub Release에 올립니다.

출시

  • npm session-clean v0.1.0 (2026-08-26) — 실행 명령은 sclean
  • 지원 에이전트: Claude Code (v0.1) · Codex, OpenCode 지원 예정
  • 지원 플랫폼: macOS arm64/x64, Linux x64/arm64 (Windows는 WSL)

핑계콜 (Fake Call)App

곤란한 자리에서 자연스럽게 빠져나오도록, 정해 둔 시간에 진짜 같은 가짜 전화를 걸어 주는 모바일 앱

핑계콜 (Fake Call)App

  • Flutter
  • Dart
  • Riverpod
핑계콜 — 수신 화면, 홈 화면, 통화 중 화면

어떤 앱인가요

곤란한 자리에선, 전화 한 통이면 돼요. 누가(발신자 이름) 언제(지금~3분 후) 두 가지만 정하면, 그 시각에 시스템 전화와 똑같이 생긴 수신 화면이 울립니다. 받으면 통화 시간이 흐르는 통화 중 화면까지 그대로 이어져서, “잠깐 전화 좀”이라며 자연스럽게 자리를 뜰 수 있습니다. 이 앱이 파는 것은 전화가 아니라 자리를 뜰 명분입니다.

주요 기능

  • 누가·언제만 고르는 홈 — 발신자 이름을 입력하면 태그로 저장돼 다음엔 탭 한 번으로 끝. 지연은 지금·10초·30초·1분·3분 중에서 고릅니다.
  • 진짜 같은 전화 UI — 수신·통화 중 화면을 Android 시스템 전화 UI에 맞춰 재현했습니다. 통화 타이머, 키패드, 음소거·스피커 버튼까지 동작합니다.
  • 정확한 카운트다운 — 벽시계 기준이라 앱을 백그라운드에 두거나 화면을 꺼도 정한 시각에 울립니다.
  • 벨소리·진동 모드 — 상황에 맞게 벨소리 또는 진동만으로 받을 수 있습니다.
  • 마스코트 — 전화를 받는 모습의 보라색 마스코트가 홈과 앱 아이콘을 지킵니다. 가끔 윙크합니다.

어떻게 만들었나

  • 앱: Flutter + Dart. 상태는 Riverpod, 라우팅은 go_router, feature-first 구조. 설정과 자주 쓰는 발신자 이름은 기기에 저장합니다.
  • AI 음성 통화도 구현했습니다 — 온디바이스 TTS(flutter_tts)가 시나리오 대본을 실제 목소리로 말하고, 마이크 진폭 기반 발화 감지로 사용자가 말을 시작하면 AI가 즉시 멈추는 barge-in까지 동작합니다(스피커 소리가 마이크로 새어 들어오는 에코는 AI 발화 중 임계값을 높여 보정). 다만 마이크 권한 없이 설치 즉시 쓸 수 있는 앱이 먼저라, v1.0에서는 인터페이스 뒤로 분리해 꺼 두고 설정에 “AI 음성 — 출시 예정”으로 남겼습니다.
  • 스토어 자료도 코드로 — Play 등록용 스크린샷·피처 그래픽·전 해상도 런처 아이콘을 headless Chrome 렌더러 + Python 스크립트로 생성합니다. 카피 문구만 고쳐 다시 돌릴 수 있습니다.

출시

  • Google Play 출시 준비 중 (v1.0.0) — 스토어 등록 자료 준비 완료
  • AI 음성 통화는 다음 버전에서 공개 예정

스크린샷

홈 — 발신자 이름과 지연 시간 선택 카운트다운 — 27초 후 전화가 옵니다 시스템 전화와 똑같은 수신 화면 통화 중 화면 — 타이머와 통화 버튼

Coverly (AI Cover)Web

노래를 올리고 Voice를 고르면 30초 AI 커버를 무료로 만들어 주는 웹서비스 — 마음에 들면 전체 곡을 유료로 생성

Coverly (AI Cover)Web

  • Next.js
  • Supabase
  • Modal
  • Seed-VC
Coverly — 노래 올리기, 구간 고르기, Voice 고르기 3단계 생성 화면

어떤 서비스인가요

좋아하는 노래를 새로운 목소리로. 음원을 올리고(또는 YouTube 링크를 붙여넣고) 30초 구간과 Voice를 고르면, AI가 원곡의 보컬을 그 목소리로 바꾼 30초 커버를 무료로 만들어 줍니다. 설치나 복잡한 설정 없이 웹에서 바로 — 결과가 마음에 들면 전체 곡을 유료로 생성하는 모델입니다.

주요 기능

  • 3단계 생성 플로우 — 노래 가져오기(파일 업로드 · YouTube) → 30초 구간 고르기 → Voice 고르기. 한 화면에서 끝납니다.
  • Voice 카탈로그 — 성별·음색·장르 태그와 음역대(A3–E5 식)를 붙인 6가지 목소리. 데모는 Coverly가 권리를 가진 반주 위에 각 Voice가 부른 것만 씁니다.
  • 무료 미리보기 → 유료 전체 곡 — “무료 사용자가 결과에 만족하면 전체 곡에 돈을 낼까?“라는 가설을 검증하는 MVP 구조. 크레딧은 Supabase 함수로 관리합니다.
  • 만든 커버는 본인만 — PRD에 있던 공개 Explore 피드는 뺐습니다. 상업 음원에서 분리한 반주를 서비스가 공개 전송하면 목소리와 무관하게 음반사의 저작인접권을 침해하기 때문입니다. 사용자는 자기 커버를 내려받아 공유 여부를 스스로 정합니다.

어떻게 만들었나

  • 웹: Next.js 16 + React 19, Tailwind CSS 4, shadcn/ui·Radix. 랜딩·Voice 카탈로그·생성 플로우·결과 페이지.
  • 백엔드: Supabase — Google·이메일 로그인, RLS, 크레딧 함수, 스토리지 버킷, 잡 큐까지 한 벌.
  • AI 파이프라인: ffmpeg 트리밍 → Demucs 보컬 분리 → Seed-VC(f0-conditioned, 44.1 kHz) 목소리 변환 → ffmpeg 믹스. 각 모델은 독립 venv의 서브프로세스로 격리해, RVC나 호스티드 API로 갈아 끼워도 파이프라인은 그대로입니다.
  • GPU 워커: Modal에 배포. 큐에 들어온 YouTube 잡이 L4 GPU에서 97초 만에 30초 커버로 완성되는 것까지 엔드 투 엔드 검증 — VRAM 3.0 GB, 회당 약 $0.022(≈23원). 벤치마크 CLI로 단계별 시간·비용을 계측합니다.

상태

  • MVP 개발 중 — AI 파이프라인 PoC·프런트·Supabase·GPU 워커까지 동작 확인
  • 카탈로그 Voice들의 파인튜닝 모델 제작과 결제 연동이 다음 단계

스크린샷

Voice 카탈로그 — 태그·음역대·샘플 듣기가 있는 6가지 목소리 카드

15만년 로또Web

"매주 로또를 산다면 1등까지 몇 년 걸릴까?" — 희박한 확률을 시간이 흐르는 추첨으로 체감하게 하는 시뮬레이터

15만년 로또Web

  • Next.js
  • TypeScript
  • Web Worker
  • Canvas
추첨 화면 — 흘러간 시간 769년, 1만x 속도, 진행 회차와 총 구매 금액

어떤 서비스인가요

로또 1등 확률 814만 분의 1은 숫자로 보면 아무 느낌이 없습니다. 이 서비스는 그 확률을 시간이 흐르는 경험으로 바꿉니다. 매주 살 게임 수와 번호를 정하면 실제 추첨기처럼 공이 섞이며 한 주씩 추첨이 반복되고, 1등이 나올 때까지 몇 년 — 몇백 년, 몇만 년 — 이 흐르는지 직접 지켜보게 됩니다. 매주 1게임이면 평균 약 15만 년. 이름이 여기서 나왔습니다.

확률 계산기가 아니라 확률 체험형 콘텐츠로 설계했고, 인스타그램 채널 개발세발의 릴스 콘텐츠(“저 로또 당첨됐어요!! …대략 15만 년 뒤에요”)와 이어지는 기획입니다.

주요 기능

  • 진짜 같은 마킹 용지 — 실제 로또 용지처럼 A~E 게임에 번호를 마킹합니다. 귀찮으면 게임별·전부 자동 선택.
  • 추첨기와 시간 카운터 — Canvas 추첨기에서 공이 섞여 나오고, 화면 위 “흘러간 시간”이 년 단위로 쌓입니다. 진행 회차·총 구매 금액도 함께.
  • 속도 컨트롤 — 1x·100x·1만x. 최고 속도를 초당 5,000주로 일부러 제한해 “시간을 견디는” 체감을 유지합니다(매주 5게임 기준 1등까지 평균 약 5분).
  • 면책 고지 — 확률 체험용 시뮬레이션이며 실제 복권 구매를 권유하지 않는다는 문구를 모든 화면에 둡니다.

어떻게 만들었나

  • 웹: Next.js 16 + React 19, Tailwind CSS 4, zustand(sessionStorage 유지), motion·canvas-confetti. PostHog 분석, AdSense 슬롯은 환경 변수로 on/off.
  • 확률 엔진: 6/45 + 보너스, 등수 판정은 비트마스크 popcount, RNG는 xoshiro128**(crypto.getRandomValues 시드). vitest로 검증합니다.
  • 성능: 배치 시뮬레이션은 Web Worker에서 초당 약 200만 주를 처리합니다. UI는 그 속도를 다 쓰지 않고 1만x로 제한하는 것이 제품 결정 — 빠르게 끝나면 체감이 사라지기 때문입니다.

출시

스크린샷

설정 화면 — 게임 수 선택과 로또 6/45 마킹 용지 랜딩 — 당신은 몇 년 뒤 로또 1등이 될까요?

Keyboard Piano (Piano Assist)Web

컴퓨터 키보드로 치는 피아노 — Easy Play를 켜면 아무 키나 막 눌러도 음악처럼 들리는 연주가 만들어지는 웹서비스

Keyboard Piano (Piano Assist)Web

  • Next.js
  • Tone.js
  • Web Audio
  • MIDI
연주 화면 — Easy Play 토글, 엘리제를 위하여 진행 바, 눌린 건반이 빛나는 88건반

어떤 서비스인가요

컴퓨터 키보드를 피아노로 바꿉니다. 핵심은 Easy Play(Assist) 토글 — 켜면 ASDF JKL; QWER처럼 아무렇게나 눌러도 시스템이 음을 골라 자연스러운 피아노 연주로 만들어 줍니다. 목표는 “나는 아무렇게나 눌렀는데 왜 이렇게 잘 치는 것 같지?“라는 감정입니다. 끄면 보정이 전부 사라지고 키 하나가 건반 하나에 대응하는 진짜 가상 피아노가 됩니다.

주요 기능

  • 클래식 곡 연주 — 엘리제를 위하여, 달빛, 캐논, 즉흥 환상곡, 혁명 등 19곡. 키를 누를 때마다 곡이 한 음씩 진행되어, 음은 작곡가의 것이고 타이밍은 내 것이 됩니다. 윗줄을 치면 밝고 세게, 아랫줄을 치면 여리게 — 막 두드려도 기계적으로 들리지 않습니다.
  • 자유 연주 — 감성·밝게·슬프게·영화처럼 4가지 분위기. 코드 진행이 흐르는 동안 키보드의 어느 줄을 쳤는지가 음역(아랫줄 낮게·가운데 줄 중간·윗줄 높게)을 정하고, 실제 음은 엔진이 고릅니다. 자동 반주와 박자 보정이 함께 붙습니다.
  • Real Piano 모드 — A S D F G H J K = 도레미파솔라시도, 검은 건반은 W E T Y U O P. Q R I는 실제 피아노에서 검은 건반이 없는 자리(미–파, 시–도)라 일부러 비워, 컴퓨터 키보드가 피아노의 모양을 그대로 따릅니다.
  • 녹음과 결과 화면 — “당신이 누른 키 52개, 틀린 음 0개.” 다시 듣기, MIDI 저장·MP3 저장, 링크 공유까지.
  • 88건반 — 곡의 음역에 맞춰 건반이 넓어지고, 키보드로 닿지 않는 음역은 마우스·터치로 칩니다.

어떻게 만들었나

  • 웹: Next.js 16 + React 19, Tailwind CSS 4, motion.
  • 오디오 엔진: Tone.js Sampler에 실제 그랜드 피아노 샘플(Salamander)을 올렸습니다. 실제로 쓰는 C2–C7 음역만 받아 로딩을 줄였고, SSR 중에는 Web Audio를 건드리지 않도록 전부 지연 로드합니다. 백그라운드 코드 클럭이 화음과 왼손 반주를 이끕니다.
  • Assist 음 선택: 코드톤 65% · 음계음 25% · 경과음 10% 가중치에 직전 음을 반영해, 무작위 입력이 난수 생성기가 아니라 선율로 들리게 합니다. 분위기마다 템포·음계·코드 진행·반주 패턴(pad/pulse/arpeggio/swell)·박자 보정 강도가 다릅니다.
  • 악보는 기억이 아니라 파일에서: 유명한 선율을 기억으로 옮기면 틀리기 때문에, Mutopia Project의 퍼블릭 도메인 MIDI를 직접 만든 변환 스크립트로 음표 그대로 옮겼습니다(곡마다 출처와 라이선스 기록). 수천 스텝짜리 곡은 고를 때 불러옵니다. 퍼블릭 도메인 피아노판이 없는 캐논·월광 도입부·환희의 송가만 직접 편곡했습니다.
  • MP3 내보내기: 녹음을 오프라인으로 다시 렌더링해 브라우저에서 192 kbps로 인코딩합니다. 인코더는 크기가 커서 저장 버튼을 누를 때만 불러옵니다.
  • 작은 디테일: “엘리제를 위하여가”, “혁명이“처럼 곡 제목 뒤 조사를 받침에 맞춰 자동으로 고릅니다. 숫자로 끝나는 제목(짐노페디 1번 등)은 읽는 소리 기준으로 판단합니다.

출시

스크린샷

곡 선택 — 클래식 19곡과 자유 연주 분위기 결과 화면 — 누른 키, 생성된 음, 다시 듣기, MIDI·MP3 저장, 링크 공유

Eye Rub GuardWeb

웹캠으로 얼굴과 손을 실시간 추적해, 손이 눈가에 머물면 바로 알려주는 눈 비비기 습관 감지 웹서비스

Eye Rub GuardWeb

  • Next.js
  • MediaPipe
  • Web Worker
  • Vitest
눈 비비기 감지 장면 — 눈 주변 타원 영역에 손가락이 닿아 주황색으로 강조되고 '눈 비비기 감지!' 상태가 표시됨
감지 화면의 오버레이를 재현한 그림입니다. 실제 화면에서는 카메라 영상 위에 그려집니다.

어떤 서비스인가요

모니터 앞에서 오래 일하다 보면 나도 모르게 눈을 비빕니다. 본인이 얼마나 자주 그러는지는 스스로 알기 어렵고, 직접 기록하기도 번거롭습니다. Eye Rub Guard는 웹캠으로 얼굴과 손을 실시간 추적하다가 손이 눈가에 일정 시간 이상 머무는 순간 바로 알려주고 횟수를 셉니다. 설치 없이 브라우저에서 동작하고, 영상은 기기 밖으로 나가지 않습니다.

의료기기가 아니라, 반복 행동을 스스로 알아차리도록 돕는 습관 관리 도구입니다.

주요 기능

  • 실시간 감지와 알림 — 손이 눈가에 감도 시간(낮음 1.2초 · 보통 0.8초 · 높음 0.5초) 이상 머물면 1회로 세고, “눈을 만지고 있어요 👀” 알림과 알림음, 탭 제목으로 알려줍니다.
  • 다른 창에서 일할 때도 — 탭이 가려지면 Web Worker 타이머로 초당 10회 감지를 이어가 백그라운드 탭의 타이머 제한을 피합니다. Chrome·Edge에서는 Document Picture-in-Picture로 항상 위에 떠 있는 미니 창을 띄울 수 있습니다.
  • 세션 통계와 결과 — 감지 횟수, 집중 시간(얼굴이 안 보이면 “자리 비움”으로 시간을 멈춤), 시간당 평균, “언제 만졌을까” 타임라인. 결과는 인스타그램 세로(4:5) 이미지 카드로 공유할 수 있습니다.
  • 개인정보 우선 — 모든 추론은 브라우저(WebGL/Wasm)에서 실행되고 영상은 저장·전송하지 않습니다. 분석은 선택 사항이며, 켜도 이벤트 이름과 숫자만 보냅니다.

어떻게 만들었나

  • 웹: Next.js 16 + React 19, Tailwind CSS 4.
  • 비전: MediaPipe Face Landmarker + Hand Landmarker. 빌드 때 스크립트가 Wasm 런타임과 모델(약 11 MB)을 받아 두고 같은 출처에서 서빙해, 외부 CDN 없이 카메라 프레임이 기기 안에만 머뭅니다.
  • 감지 로직 (순수 함수로 분리하고 Vitest로 검증)
    1. 양쪽 눈 윤곽 중심과 얼굴 크기를 구합니다. 고개를 돌려 얼굴 폭이 줄면 얼굴 높이 기반 추정치로 대체합니다.
    2. 눈마다 얼굴 크기에 비례한 타원 영역(가로 0.17 × 세로 0.13)을 만들고 머리 기울기만큼 회전합니다.
    3. 손 관절 21개 중 하나라도 영역 안에 들어오면 “접촉”. 얼굴보다 1.6배 이상 크게 보이는 손은 카메라 앞을 지나가는 손으로 보고 무시합니다.
    4. 접촉·지속시간·쿨다운 상태 머신 — 한 번 이어진 접촉은 한 번만 세고, 350 ms 이하의 추적 끊김은 같은 접촉으로 보며, 카운트 사이에 2.5초 쿨다운을 둡니다.
    5. 손이 눈을 가려 얼굴 추적이 끊겨도 1.5초 동안은 마지막 눈 위치를 기억합니다.
  • 알려진 한계도 기록: 고개를 돌린 채 손으로 얼굴을 괴는 자세는 2D 화면에서 손가락이 눈꼬리와 겹쳐 보여 비비기와 구분하기 어렵습니다. 손의 이동 방향과 반복 움직임을 보는 것이 다음 과제입니다.

출시

스크린샷

시작 화면 — 카메라 연결, 감지 모델 준비, 얼굴 찾기 체크리스트 감지 중 패널 — 눈 비비기 횟수, 집중 시간, 알림 소리·카메라·랜드마크 표시, 감도, 미니 창

송편 스튜디오 (Songpyeon Studio)Web

반죽 색·속·모양·문양을 골라 3D 송편을 빚고, 솔잎 위에서 쪄서 이미지 카드나 링크로 보내는 추석 웹 토이

송편 스튜디오 (Songpyeon Studio)Web

  • Three.js
  • React Three Fiber
  • Next.js
  • WebGL
랜딩 화면 — 보름달 아래 상 위에 놓인 쌀빛·쑥빛·치자빛·오미자빛 송편

어떤 서비스인가요

“이번 추석, 송편 하나 빚고 가세요.” 브라우저에서 반죽 색 → 속 → 모양 → 문양 네 단계를 고르면 보름달 아래 상 위에서 3D 송편이 빚어집니다. 솔잎을 깔고 찐 다음, 메시지를 적어 이미지 카드나 링크로 보내면 받는 사람은 그 송편을 그대로 열어 보고 자기 송편을 빚으러 갑니다.

로그인도 데이터베이스도 없습니다. 송편과 메시지는 전부 URL 쿼리에 담깁니다 (?dough=omija&filling=chestnut&shape=chubby&stamp=rabbit&msg=…&from=…).

주요 기능

  • 네 단계로 빚기 — 반죽 5색(쌀빛·쑥빛·치자빛·오미자빛·자색빛), 속 5종(깨·밤·콩·팥, 그리고 “열어봐야 아는” 비밀 소), 모양 4종(반달·통통·손빚음 등), 문양 4종(꽃·잎·별·토끼).
  • 빚는 과정이 그대로 보입니다 — 속을 고르면 반죽 위로 소가 떨어져 자리를 잡고, 모양을 고르면 반죽이 접히며 변형되고, 찌는 동안 김이 오릅니다.
  • 공유 카드 — 완성한 송편을 인스타그램 세로(1080×1350)와 스토리(1080×1920) 크기 이미지로 저장합니다. 달·한지 배경과 “오미자빛 · 밤 · 통통” 같은 조합, 손글씨 메시지가 함께 들어갑니다.
  • 링크로 보내기 — 받은 사람은 같은 송편을 3D로 보고 메시지를 읽은 뒤, 바로 자기 송편을 빚기 시작할 수 있습니다.
  • 모바일 우선 — 세로 화면 기준으로 만들고 데스크톱까지 함께 지원합니다.

어떻게 만들었나

  • 3D: three.js + React Three Fiber + drei. 송편은 모델링 파일 없이 코드로 만듭니다 — UV 구를 납작한 반죽 원반으로 눌러 기본형을 만들고, 반달·통통·손빚음을 모프 타깃으로 두어 모양 변경을 블렌딩으로 처리합니다.
  • 질감도 코드로: 반죽의 거칠기·노멀 맵과 나무 상판은 캔버스에서 생성한 타일 노이즈이고, 문양은 SVG 경로로 그린 높이 맵입니다. 문양은 셰이더를 가로채 오브젝트 공간에서 앞뒤 면에 투영하기 때문에, 모양이 바뀌어도 문양이 같이 눌려 살아남습니다.
  • 연출: 소가 떨어지는 건 인스턴싱한 유사 물리, 김은 포인트 셰이더, 조명은 3점 + RoomEnvironment. 단계마다 카메라 프리셋이 있고 감쇠(damping)로 부드럽게 옮겨 갑니다.
  • 공유 카드 렌더링: 캔버스의 드로잉 버퍼 크기를 잠시 카드 비율로 바꿔 3D 프레임을 뽑고, 배경·달·텍스트를 2D로 합성합니다.
  • 버전 고정 이유까지 기록: React Three Fiber 9.x가 react < 19.3만 지원해서 Next 16 대신 15.5에 묶어 두었습니다.
  • 시각 QA 스크립트: Playwright로 데스크톱·아이폰에서 전 단계를 걸어가며 스크린샷을 찍고, 공유 카드 저장과 링크 왕복까지 확인합니다. 콘솔 에러가 하나라도 있으면 실패합니다. (이 페이지의 이미지도 그 스크립트로 찍었습니다.)

출시

화면

속 고르기 — 반죽 위로 밤 소가 떨어지는 장면 문양 찍기 — 토끼 문양이 눌린 오미자빛 송편
모바일 완성 화면 — 메시지 카드와 보내는 사람 입력 공유 이미지 카드 — 2026 추석 송편, 조합과 메시지

와이파이 렌즈 (Wi-Fi Lens)App

카페·숙소의 Wi-Fi 안내문을 카메라로 비추면 이름과 비밀번호를 읽어 바로 연결해 주는 앱

와이파이 렌즈 (Wi-Fi Lens)App

  • Flutter
  • ML Kit OCR
  • Android/iOS 네이티브
와이파이 렌즈 — 안내문을 비추는 카메라 화면과 인식된 Wi-Fi 정보 화면

어떤 앱인가요

카페·식당·숙소 벽에 붙은 Wi-Fi 안내문을 카메라로 비추면, 네트워크 이름과 비밀번호를 읽어 바로 연결해 줍니다. coffee2024! 같은 비밀번호를 한 글자씩 옮겨 치다 틀리는 일을 없애는 것이 목적입니다. 서버도 로그인도 없고, 글자 인식은 전부 기기 안에서 끝납니다.

카메라로 비춤 → 온디바이스 OCR(여러 프레임 다수결) → 확인·수정 → Wi-Fi 연결 → OS 승인 → 연결됨

주요 기능

  • 셔터 없이 인식 — 프리뷰가 켜진 동안 0.35초마다 프레임을 읽어, 3개 이상 프레임이 같은 값을 가리키면 “Wi-Fi 정보를 찾았어요” 카드가 떠서 바로 넘어갑니다.
  • 흔들려도 정확하게 — 여러 프레임의 판독을 글자 위치별 다수결로 합칩니다. 어떤 프레임이 @를 0으로 읽어도 다수가 @로 읽으면 @가 됩니다. 확신이 낮은 자리는 “후보” 칩으로 대안을 함께 보여 줍니다.
  • 가게마다 다른 표기 지원 — Wi-Fi : 카페모모, ID / PW : momo / 1234, 표 형태, 비밀번호는 abc12345 입니다 같은 문장까지 규칙 기반 파서가 알아봅니다. FREE WIFI 같은 제목은 값으로 쓰지 않습니다.
  • 대소문자·특수문자 보존 — OCR 잡음(전각 문자, 특수 대시, ID를 lD로 읽는 경우)은 바로잡되, 비밀번호의 대소문자와 !@#$% 같은 문자는 절대 바꾸지 않습니다.
  • OS 공식 API로 연결 — Android는 WifiNetworkSuggestion, iOS는 NEHotspotConfiguration을 호출해 시스템 확인 화면을 띄웁니다. 갤러리의 기존 사진으로도 연결할 수 있고, 인식 기록은 Keychain·Keystore에만 남습니다.

어떻게 만들었나

  • 앱: Flutter 3.47 (Dart 3.13). Android minSdk 24 / iOS 15.5+.
  • 인식: Google ML Kit 텍스트 인식(한국어 + 라틴 인식기 동시 실행 — 영문·숫자만 있는 안내문은 라틴 전용 모델이 더 정확합니다). 결과의 줄 좌표로 같은 높이의 줄을 한 행으로 재구성해, 라벨 열과 값 열이 떨어져 있는 안내문도 올바르게 짝지읍니다.
  • 가이드 영역만 크롭 — 촬영한 사진에서 화면 가운데 정사각형 영역만 잘라 인식해 메뉴판 글자가 섞이지 않게 합니다. 값을 못 찾으면 전체 사진으로 한 번 더 인식해 빠진 쪽만 채웁니다. 사진과 크롭 파일은 인식 직후 삭제합니다.
  • Wi-Fi 연결은 직접 구현 — 유지보수가 불확실한 패키지 대신 MethodChannel로 Android(Kotlin)·iOS(Swift) 네이티브 API를 직접 호출합니다.
  • 테스트: 파서·검증기 단위 테스트와, 안내문 이미지를 그려 실제 ML Kit → 파서까지 통과시키는 통합 테스트(영문/한글/2열/특수문자).
  • 개인정보: 비밀번호는 로그에 남기지 않고, 분석 도구나 서버 전송이 없습니다.

출시

스크린샷

안내문을 비추면 Wi-Fi 정보를 찾는 카메라 화면 인식한 네트워크 이름과 비밀번호 확인 화면 Wi-Fi에 연결되었습니다 안내 인식해서 연결한 Wi-Fi 기록

Laptop GuardWeb

카페에서 잠깐 자리를 비울 때, 노트북이 움직이면 노트북 카메라가 알아채고 휴대폰으로 알려주는 웹앱

Laptop GuardWeb

  • Next.js
  • Computer Vision
  • Upstash Redis
  • Canvas
Laptop Guard — 노트북의 감시 중 화면과 휴대폰의 상태 화면

어떤 서비스인가요

카페에서 화장실에 다녀오는 몇 분, 노트북을 두고 가기가 불안합니다. Laptop Guard는 노트북 카메라가 주변을 지켜보다가 노트북 자체가 움직이면 휴대폰으로 알려줍니다. 앱 설치 없이 노트북에서 페이지를 열고 QR을 휴대폰으로 찍으면 끝입니다.

노트북에서 '자리 비움 시작' → QR을 휴대폰으로 스캔 → 휴대폰 연결됨 → '감시 시작' → 움직이면 휴대폰 알림

카메라 영상은 저장하지도, 서버로 보내지도 않습니다. 분석은 전부 브라우저 안에서 이뤄집니다.

주요 기능

  • 사람이 지나가는 것과 노트북이 움직이는 것을 구분 — 사람이 앞을 지나가면 일부 지점만 가려지고 배경은 그대로라 알리지 않습니다. 화면 전체가 함께 움직여야 “노트북이 움직였다”로 봅니다.
  • 천천히 미는 것도 감지 — 매 프레임을 비교하는 대신 감시 시작 시점의 기준 프레임부터 누적 변위를 추적하기 때문에, 조금씩 미는 움직임도 쌓여서 잡힙니다. 들어 올리거나 카메라를 가리는 것도 “시야 전체 변화”로 감지합니다.
  • 민감도 3단계 — 위험 신호가 400 / 700 / 1200 ms 이상 이어져야 알림을 보내, 잠깐의 흔들림은 무시합니다.
  • 연결이 끊겨도 알림 — 노트북 탭을 닫거나 덮으면 휴대폰이 “연결 끊김”으로 울립니다. 조용히 꺼지는 대신, 정상 종료는 감시 종료 버튼을 눌렀을 때만입니다.
  • 디버그 뷰 — ?debug로 열면 추적 중인 특징점(고정·이동·추적 실패)과 내부 신호값이 보입니다. 민감도 튜닝용입니다.

어떻게 만들었나

  • 웹: Next.js 16 + React 19, Tailwind CSS 4. 노트북과 휴대폰 모두 브라우저 페이지입니다.
  • 이동 감지는 OpenCV 없이 직접 구현 — 카메라 프레임을 160px 흑백으로 줄여 약 8fps로 분석합니다.
    • 화면을 8×6 격자로 나눠 칸마다 가장 강한 코너(Shi–Tomasi)를 하나씩 골라 특징점을 고르게 분포시키고,
    • 피라미드 Lucas–Kanade로 기준 프레임부터 추적하며,
    • RANSAC으로 전역 움직임(이동·회전·스케일)을 추정해 판단합니다.
    • 국소 high-pass 영상과 패치 NCC 검사를 더해 자동 노출 변화나 어두운 옷을 입은 사람이 지나가도 배경 추적을 유지합니다.
  • 두 기기 동기화: 각 기기가 자신의 최신 상태를 Upstash Redis에 저장하고(TTL 10분 → 자동 삭제) 상대 상태를 0.7초마다 조회합니다. 메시지 나이는 서버 시계로 계산해 기기 간 시계 차이에 영향받지 않습니다. Redis 환경변수가 없으면 메모리 저장으로 동작합니다.
  • 합성 시나리오 테스트: 사람 통과·테이블 흔들림·노출 변화는 알림 없음, 밀기·천천히 밀기·회전·들기·가림은 알림이 뜨는지 스크립트로 검증합니다.

출시

화면

휴대폰 연결 — QR 코드와 연결 대기 상태 노트북 감시 중 — 움직임 없음, 민감도 선택 휴대폰 화면 — 노트북 감시 중, 움직임 없음