Things I've built

Not just learning by reading — shipping things and logging them here.

Nogari

An anonymous community where boards are opened by user consensus, not admins

LiveJul 2026

An anonymous community for talking about people, products, brands, and events. Two experiments drive it. First, boards aren't created by an admin — anyone can propose a topic, and if 30 people agree within 72 hours, the room opens automatically; expiry, 24-hour self-destructing "bamboo forest" rooms, and inactivity archiving are all handled by pg_cron. Second, conversations had to work with zero signup: middleware grants every visitor an anonymous session, and a deterministic nickname derived from the room ID and a device hash keeps you consistently "Furious Lawmaker" within one room while making you untraceable across rooms. Duplicate room proposals are screened without embeddings — Claude receives the full list of existing topics and judges semantic duplicates directly — and comments pass through a two-stage filter: regex PII detection, then LLM screening. The trending ranking is a Hacker News-style time-decay score computed in a single Postgres view. The visual design — a line-art fish on off-white paper, all monochrome — was reimplemented in code from an HTML design handoff.

Next.jsTypeScriptReactSupabasePostgreSQLpg_cronTailwind CSSClaude APISerwist PWAVercel
Nogari first screen — real-time trending rooms
Browsing rooms by type — 300 seeded lawmaker cards
Browse by type + search
Room detail — auto-generated anonymous nicknames and comments
Anonymous comments — per-room nicknames

Blog Auto-Publisher SaaS

Register a topic — AI drafts it, the cloud publishes it

Preparing to launchJul 2026 ~

A service where you register topics by date, Claude writes the drafts, and posts are automatically published to Tistory and Naver Blog at the scheduled time. Both platforms shut down their writing APIs, so Playwright browser automation was the only way in — a headless Chrome runs inside a Vercel serverless function, types into the real editor, and clicks the publish button. For login, a Browserbase remote browser is embedded as an iframe so users sign in themselves; the service never touches passwords and stores only session cookies. Kakao sessions expire after just a day of inactivity, which I solved with a nightly cron that keeps sessions alive. Per-user Redis namespaces make it multi-tenant. It started as a local CLI and pivoted to a cloud SaaS — now preparing to launch as a paid subscription. Since the publishing automation is the product itself, this is the one project whose source stays private.

Next.jsTypeScriptPlaywright@sparticuz/chromiumUpstash RedisAuth.jsBrowserbaseVercel CronClaude API
Blog auto-publisher dashboard with topic registration and publish status
Dashboard — topics & publish status
Publish queue with topics scheduled by date
Publish queue — a month scheduled
AI draft generation in progress
AI drafting
Generated draft preview and edit modal
Draft preview & edit
Tistory and Naver blog connection settings
Blog connection settings

Document Verification Blockchain

Smart contract that verifies documents by matching file hashes on-chain

Deployed to Sepolia testnetJul 2026

A smart contract built to prevent tampering with sensitive documents like hospital-issued prescriptions. Privacy law prevents storing the actual document on a public blockchain, so the design only records a SHA-256 hash on-chain while the original file stays off-chain. Hospital (issuer) and pharmacy (verifier) permissions are separated by role, and prescriptions automatically expire 7 days after issuance. I wrote 29 Hardhat test cases covering forged-file detection, reuse prevention, and unauthorized access, then deployed it to the Sepolia testnet. I also built a web UI that computes the file hash in the browser via the Web Crypto API, so the original file never touches the server.

SolidityHardhatOpenZeppelinEthereumEthers.jsExpress
Document verification blockchain hospital issue screen
Hospital — Issue
Document verification blockchain pharmacy verify screen
Pharmacy — Verify
Document verification blockchain status lookup screen
Status Lookup

Matchda

Paste a job URL → AI matching → auto-generated cover letter

TestingApr 2026 ~

A personal tool built to automate the daily grind of job searching. Paste a job posting URL from Seek, Indeed, LinkedIn, or Glassdoor — Matchda scrapes the JD automatically and uses AI to score the match against your profile. Track each application through status stages (interested → applied → interview → offer), and generate English or Korean cover letters with one click. Export as TXT, DOCX, or PDF. It originally launched as "JobRadar," but was rebranded through a branding process to Matchda (match + da) — capturing the idea of matching every job in the world.

Next.jsTypeScriptSupabaseVercelCheerioClaude API
Matchda global career platform home
Matchda AI job matching
AI job matching
Matchda resume translation
Resume translation
Matchda application tracking
Application tracking

TILT — The Maze Puzzle

Tilt to escape — fully playable by blind users via voice alone

ReleasedMay 2026

A reaction-speed puzzle game where you tilt your phone to move a marker across a 3×3 grid to the target cell. The timer shortens each round, keeping pressure constant. Designed from the ground up for equal access: TTS announces your position and target each round, a metronome shifts from 60 to 180 BPM as time runs out, and haptic feedback marks every move and collision — so the game is fully playable without looking at the screen. Includes a real-time Supabase global leaderboard and a 3-step interactive onboarding tutorial.

React NativeTypeScriptExpoSupabaseexpo-sensorsexpo-speechexpo-audioexpo-haptics
TILT maze puzzle game screen
TILT main menu screen
Main menu
TILT gameplay screen
Gameplay
TILT game over and leaderboard screen
Game Over / Leaderboard

ChainPlay

Autoplay YouTube videos in the order you choose

ReleasedMay 2026

An Android app for watching only the YouTube videos you picked, in your own sequence. Paste youtube.com/watch, youtu.be, Shorts, or embed URLs to add videos to a local playlist, then ChainPlay automatically moves to the next item when one ends. It uses the official YouTube player without login and supports previous/next controls, ▲▼ reordering, deleting videos, local persistence, and Korean/English auto-detection.

React NativeTypeScriptExpo SDK 54react-native-youtube-iframeAsyncStorageIntl API
ChainPlay 메인 화면
ChainPlay main screen in English
Main screen
ChainPlay add URL screen in English
Add URL

Cassette Music Player

A retro cassette music player with no skip button

ReleasedMay 2025

An Android music player that brings the 1980s cassette tape experience to a modern phone. Instead of instant skipping, you have to press and hold FF to move to the next track. Each cassette has Side A and Side B with a 30-minute limit per side, and tape noise plays between tracks. It uses local music files only, with no internet and no algorithm, so the experience stays focused on the songs you chose.

React NativeTypeScriptExpo SDK 54expo-routerexpo-avreact-native-reanimatedreact-native-svgAsyncStorage
Cassette Music Player 화면
Cassette Music Player Side A player screen
Player Side A
Cassette Music Player library screen
Library
Cassette Music Player Side B playing screen
Player Side B

back to dev

A developer blog — a 40-something PM returning to code

LiveApr 2026 ~

A personal developer blog documenting the journey back into development. The publishing workflow is fully automated: push a Markdown file to GitHub, and GitHub Actions publishes it on the scheduled date. Built with Next.js App Router to serve Korean and English routes separately, with SEO metadata, sitemap, and robots configured. This portfolio page is part of the same blog.

Next.jsTypeScriptVercelGitHub APIGitHub Actionsnext-intlTailwind CSS
back to dev blog home
Posts list page
Posts

Village — Pixel Village Guestbook

Find the easter egg to plant a tree, and leave a comment

LiveJul 2026

I wanted to keep this blog running on GitHub alone, with no database — but I still wanted a comment feature. So I built a 90s-style RPG pixel village you walk around as a character. You can only plant a guestbook tree by finding a hidden easter egg (a stone), and to read a comment, you have to track down a tree planted somewhere in the village. Guestbook entries are stored with no database at all — they're committed directly as a JSON file in the GitHub repo. Visitors can't edit or delete their own entries; if they want a change, they have to send me a GitHub PR, which I review and merge myself. I added a honeypot field and an IP-based throttle (one tree per minute) to keep bots out.

Next.jsTypeScriptReactGitHub REST API
Pixel village overview map
Village overview — home, library, workshop
Pixel village square screen
Village square
Pixel village library interior
Library — post shelves
Pixel village library recent posts modal
Recent posts list

WiFi QR Code Generator

No more reading out WiFi passwords to guests

LiveMay 2026

Built to eliminate the awkward WiFi password exchange at cafés, restaurants, and small businesses. Enter your SSID and password, get a QR code instantly. Print it as a card, stick it on the wall — guests scan with their camera and connect. Supports Korean, English, Chinese, and German.

ReactTypeScriptViteqrcode.reactZodi18n
WiFi QR 코드 생성기 화면

Mahjong Joy

Mahjong is intimidating — so I stripped it down to just matching

In developmentJul 2026

What actually scares people away from mahjong isn't the rules — it's the scoring system (yaku, han, fu). So I stripped the rules down to one shape to remember: 4 sets + 1 pair. Winning scores are rebuilt as a simple receipt instead: a base 100 points, plus bonuses, then multiplier bonuses, revealed line by line like a real receipt. The default mode is solo play against AI — you're always seat 0, AI fills the rest — but I also built LAN multiplayer: create a room on the same Wi-Fi and up to 4 people play together, with the host acting as the sole referee while guests just send actions, so it needs no server at all. Sound effects are synthesized in Python with no external assets, and the app supports Korean, Chinese, and English, plus a beginner mode that drops scoring entirely and keeps just the matching.

FlutterDartdart:io SocketsProvideraudioplayers
Mahjong Joy home screen
Home — Play with AI / Friends
Mahjong Joy gameplay screen with claim timer
Gameplay — 15s response timer
Mahjong Joy how to play screen
How to play — one shape, no hands

Football Dice

An American football board game resolved by dice and cards

In developmentJul 2026

A board game app that recreates American football with dice and cards. Pick a play from your offense or defense playbook, and the outcome is resolved against a dice matchup chart — keeping the tension of a real tabletop board game. Play against AI across three difficulty levels, or challenge a friend directly over the same Wi-Fi network. Beyond the three suggested plays, you can browse the full playbook, and the app supports Korean and English with independent toggles for dice-and-card animations and sound effects.

FlutterDartdart:io Socketsshared_preferencesaudioplayers
Football Dice home screen
Home — difficulty select
Football Dice offense playbook screen
Offense playbook selection
Football Dice dice resolution result screen
Dice resolution result

// more coming soon.