아무도 요청하지 않은 150가지 개발 도구를 만들었습니다. Google은 저에게 클릭 6회를 보냈습니다.
요약
작성자는 백엔드 서버 없이 브라우저 내에서 작동하는 150여 개의 작은 개발 도구 모음(dauntexlabs)을 만들었습니다. 이 프로젝트는 Next.js의 정적 내보내기 기능을 활용하여 개인 정보 보호를 최우선으로 하며, 사용자가 필요로 하지만 기존 서비스가 불편하게 제공하던 니치한 문제들을 해결하는 데 초점을 맞추고 있습니다.
핵심 포인트
- 백엔드 서버 없이 브라우저에서 모든 도구 실행 (Privacy-first)
- Next.js 정적 내보내기(static export)를 활용하여 성능 최적화
- 사용자가 인지하지 못했으나 실제로 필요한 니치한 문제 해결에 집중
- Claude Code 사용 경험을 바탕으로 개발되었으며, 테스트 스위트와 명세서가 중요함
요약 (스크롤하는 사람들을 위해)
- 저는 dauntexlabs를 만들었습니다: 약 150개의 작은 도구들(일부는 아직 진행 중이며, 네, 언젠가 500개 계획입니다 ㅋㅋ). JSON 처리, 해싱(hashing), PDF 병합(PDF merge), 이미지 변환(image convert), 항공 계산기(aviation calculators), 인도 레거시 글꼴 변환기 등이 있습니다. 이 모든 것은 전적으로 브라우저 내에서 실행됩니다. 백엔드 서버가 없습니다. 사용자 파일은 노트북에 남아 있어야 하며, 이를 확인하는 테스트도 존재합니다.
- 이것은 완전히 정적(static) Next.js export입니다. 도구당 하나의 HTML 페이지를 사용하며, 공유되는 JS는 약 105 KB로 유지됩니다. 무거운 라이브러리는 필요한 버튼을 클릭할 때만 로드됩니다.
- "개인 정보 보호 우선(Privacy-first)"은 모두가 주장하는 문구입니다. 그래서 저는 이것을 테스트로 만들었습니다: 모든 도구에 캐나리아 문자열(canary string)이 들어가며, 이 문자열이 네트워크 요청, URL, 페이지 제목, 저장소 또는 콘솔에서 나타나면 테스트가 실패하도록 했습니다.
- Search Console 3개월간의 기록: 20.8K 노출 수, 6 클릭, 평균 순위 76위. 구글은 "km를 마일로" 변환하는 기능을 가지고 있습니다. 저는 위젯과 경쟁하는 것이 아닙니다.
- 그래서 일부러 틈새 시장(niche)을 공략했습니다: 조종사를 위한 측풍 계산기, 인도어 타이피스트를 위한 Krutidev에서 Unicode 변환기 등, 각각 사용자 수가 겨우 12명 정도인 것들입니다. 아무도 필요로 하지 않습니다. 하지만 정말로 필요한 사람들이 있습니다.
- 고백하자면: 이것은 주로 Claude Code를 사용해 **느낌에 의존하여 코딩(vibe coded)**한 결과물입니다. 이게 단순한 잡동사니가 되지 않게 하는 것은 규칙 파일, 코드 작성 전의 명세서(specs), 그리고 검토자 역할을 하는 테스트 스위트 덕분입니다. 코드는 공개되어 있습니다: github.com/jha-adrs/dauntexlabs.
이것이 전체 게시글 내용입니다. 어떻게 만들었는지 알고 싶다면 계속 읽어보세요.
애초에 왜 이걸 만들었나
솔직히 말해서, 여기에는 사업성이 없습니다. 사이트의 모든 도구는 이미 다른 곳에 존재합니다.
저를 짜증 나게 했던 것은 그것들이 존재하는 방식이었습니다. PDF 두 개를 병합하고 싶으면, 상위 결과물은 은행 명세서를 어떤 서버에 업로드하라고 요구하고, 기다렸다가 다시 다운로드해야 합니다. JWT(JSON Web Token)를 디코딩하고 싶으면, 트래커 14개가 붙어 있는 무작위 사이트에 실제 운영 토큰을 붙여넣어야 합니다.
이 모든 것에 서버가 필요하지 않습니다. 브라우저만으로도 해싱(hash), 암호화(encrypt), PDF 읽기 및 이미지 재인코딩이 가능합니다. 그래서 이 프로젝트의 규칙은 간단했습니다:
만약 도구가 브라우저에서 실행될 수 있다면, 브라우저에서 실행된다. 그렇지 못하면 출시되지 않는다.
이것이 핵심이다. 대부분의 사람이 가지고 있지 않은 문제를 해결하며, 실제로 가진 사람들도 자신이 그 문제가 있다는 것을 모르는 경우가 많다. 전형적인 '있으면 좋은(good to have)' 프로젝트인 셈이다. 나는 그것에 만족한다.
설정: 모든 것을 정적으로 (static everything)
스택은 의도적으로 지루하다:
- Next.js 15와
output: 'export'사용. 모든 라우트가 일반 HTML 파일이 된다. 나는 순수 SPA(Single Page Application) 대신 Next.js를 선택했는데, 오직 SEO 때문만이다. 각 도구는 자체 제목과 설명이 포함된 고유한 실제 페이지를 갖게 된다. - 하나의 레지스트리 파일로 전체 사이트를 구동한다.
lib/tools.ts는 도구 목록(slug, name, category, blurb, keywords)이다. 홈페이지, 검색, 카테고리 필터, 모든 도구 페이지,sitemap.xml, 그리고robots.txt가 모두 이 하나의 목록에서 나온다. 항목을 추가하기만 하면 페이지, 카드, 사이트맵 행이 저절로 나타난다. - 각 도구가 자체적인 지연 로딩(lazy chunk)이다.
slug → dynamic(import(...))맵 덕분에 PGP 도구의 코드가 워드 카운터 페이지에서 절대 로드되지 않는다.
// components/ToolMount.tsx (생략된 형태)
const TOOLS = {
'merge-pdf': dynamic(() => import('@/components/tools/MergePdf'), { ssr: false }),
...
서버도, 데이터베이스도, 인증(auth) 기능도 없다. 배포는 'out/ 폴더를 어딘가에 복사'하는 것뿐이다. 2009년 같은 느낌이라 마음에 든다.
번들 예산 (The bundle budget)
150개의 도구(그리고 500개를 목표로 할 만큼 망상적인 사람이라면)를 다룰 때, 쉽게 실패하는 지점은 모든 페이지가 서서히 모든 라이브러리를 전송한다는 것이다. 그래서 예산이 있다: 공유되는 첫 로드 JS는 약 105 KB를 유지해야 하며, 새로운 레지스트리 항목 12개당 약 1 KB만 증가해야 한다.
이를 지키는 규칙들은 다음과 같다:
- 라이브러리보다 Web API를 사용하세요. SHA, HMAC, AES는
crypto.subtle을 통해 처리됩니다. 이미지 도구들은 외부 라이브러리 없이 네이티브 Canvas API를 사용합니다. - 플랫폼에 공백이 있다면 직접 작성하세요. Web Crypto에는 MD5가 없습니다 (공정하게도, MD5는 구식입니다). 사람들은 여전히 체크섬(checksums)을 위해 이것이 필요하므로,
lib/md5.ts는 수동으로 포팅한 MD5입니다. 작고 의존성이 없습니다. - 허용되는 라이브러리는 세 개이며, 클릭 핸들러 내부에서 로드됩니다.
openpgp,pdf-lib, 그리고qrcode-generator는 번들링되어 (CDN 사용 안 함) 필요한 함수 _내부_에 임포트됩니다. 파일 상단이나 페이지 로드 시가 아닙니다.
async function merge(files: File[]) {
const { PDFDocument } = await import('pdf-lib') // 오직 지금, 오직 여기서만
const out = await PDFDocument.create()
...
빌드할 때마다 저는 이 라이브러리들이 공유 번들(shared bundle)에 포함되지 않고 각 도구별 청크(per-tool chunks)로 나타나는지 확인합니다. 단 하나의 잘못된 최상위 레벨 임포트만 있어도 예산이 바닥납니다.
'개인 정보 보호'를 테스트가 아닌, 실제 구현으로 만들기
모든 도구 사이트는
4. 마지막 방어선으로서의 CSP(Content Security Policy). public/_headers는 사이트 자체와 분석 도구에 대한 연결만을 허용하는 Content Security Policy를 설정합니다. 따라서 코드가 새어나가더라도 브라우저가 해당 요청을 차단합니다. 크로스 오리진(cross-origin) 요청이 발생하여 차단되는지 확인하는 E2E 테스트가 있습니다.
제가 이상하게 자랑스러워하는 작은 세부 사항: 내부 페이지에서 검색할 때 주소가 /?q=term이 아니라 /#q=term으로 이동합니다. # 뒤의 부분은 서버로 절대 전송되지 않기 때문에, 사용자의 검색 기록이 누구의 접근 로그에도 남지 않습니다.
솔직하게 말하자면
개인 정보 보호 페이지에는 예전에는 "어떤 데이터도 기기를 떠나지 않는다"고 적혀 있었습니다. 저는 이것을 "사용자 장치에서 실행되도록 설계되었다(designed to run on your device)"로 변경하고, 데이터가 어떤 상황에서 떠날 수 있는지에 대한 섹션을 추가했습니다.
왜 약하게 만들었냐고요? 절대적으로 완벽한 버전은 제가 보장할 수 있는 것이 아니기 때문입니다. 저는 제 코드는 통제하지만, 사용자의 9개 Chrome 확장 프로그램까지는 통제하지 못합니다. "설계되었다"고 말하고 이를 뒷받침하는 테스트를 제시하는 것이, 지킬 수 없는 보증보다 더 정직하다고 판단했습니다.
픽셀을 그리는 테스트 도구들
1,780개의 Vitest 테스트가 있으며, 각 도구마다 하나의 스위트(suite)가 존재합니다. 실제 컴포넌트를 렌더링하고 실제 출력을 확인합니다. 암호화 관련 테스트는 적절한 라운드 트립을 수행합니다: AES 또는 PGP로 암호화하고, 복호화하여 평문(plaintext)을 되찾습니다.
이미지 도구가 문제였습니다. jsdom은 실제 캔버스(canvas)가 없기 때문에, 유닛 테스트에서는 흐름(파일 로드가 되는지, 버튼이 작동하는지, 다운로드가 발생하는지)만 확인할 수 있습니다. JPEG가 실제로 JPEG인지 여부는 알려줄 수 없습니다.
그래서 Playwright가 이 부분을 처리합니다. 실제 Chromium 환경에서 동작하며 다운로드된 바이트를 확인합니다:
- JPEG와 WebP 출력물은 올바른 매직 넘버(magic numbers)로 시작하는지
- 크기 조정된 PNG는 IHDR 청크에서 읽어온 올바른 너비와 높이를 갖는지
.ico파일이 다중 크기 헤더를 가지고 있는지- QR 코드는 jsQR을 사용하여 **복호화(decoded back)**되며, 실제로 스캔되는 눈 모양 스타일만 UI에 제공되는지
마지막 부분이 가장 중요합니다. 멋진 QR 눈 모양 디자인이라도 실제로는 디코딩되지 않는 경우가 있습니다. 따라서 테스트가 제 취향이 아닌, 허용 가능한 스타일을 결정합니다.
고백: 안전벨트와 함께 분위기(vibe)로 코딩했습니다
이 코드의 대부분은 제가 직접 타이핑한 것이 아니라 Claude Code가 작성했습니다. 그렇지 않은 척하지 않겠습니다.
속도를 보면 명확합니다. 레포지토리는 public에 공개되어 있으니 확인해 보세요:
- 6월 9일: 첫 커밋.
- 6월 20일: 네 번의 커밋으로 68개의 새로운 도구 추가. 하루 만에.
- 10월 7~8일: 두 일 동안 59개 커밋, 전체적인 시각적 리브랜딩과 아래 설명된 니치 확장 작업이 포함되었습니다.
제가 하루 만에 68개의 도구를 손으로 작성할 수는 없습니다. 하지만 "vibe code 150 tools" 역시 약간 고장 난 도구 150개를 얻는 정확한 방법이기도 합니다. 따라서 분위기(vibes)도 일종의 레일 위에서 돌아갑니다.
1. 에이전트가 매 세션마다 읽는 규칙 파일. 레포지토리 루트에 CLAUDE.md가 있습니다. 이것은 "제발 좋은 코드를 작성해줘"라는 느낌의 요청이 아닙니다. 이는 에이전트가 스스로 검사할 수 있는 구체적인 내용들입니다:
- 도구 로직에
fetch, XHR 또는 WebSocket을 사용하지 않기 - 무거운 라이브러리는 최상단(top)이 아닌 핸들러 내부에서 가져오기 (import)
- 모든 도구를 공유 UI 키트(shared UI kit)로 구축하고, 새로운 CSS 클래스를 만들지 않기
- 5단계 "도구 추가" 체크리스트 (레지스트리 항목, 컴포넌트, 마운트 라인, 소개/FAQ 콘텐츠, 개인정보 보호 카나리아 케이스)
- 공유 JS는 약 105 KB를 유지합니다.
제가 에이전트에게 어떤 것에 대해 두 번이나 수정한 적이 있다면, 그 내용은 이 파일에 들어갑니다. 이 파일은 기본적으로 프로젝트의 기억입니다.
2. 도구 하나보다 큰 것이라면 코딩 전에 명세서(spec) 작성. 레포지토리에는 docs/superpowers/ 아래에 6개의 디자인 명세서와 3개의 구현 계획이 있습니다. 흐름은 이렇습니다: 에이전트가 명세서를 작성하고, 제가 이를 되돌려주고, 합의를 본 다음, 계획을 세우고, 마지막으로 코드를 작성합니다. 니치 확장 명세서는 말 그대로 제 Search Console 테이블로 시작했습니다. 데이터가 먼저 요청했고, 그 후에 에이전트가 타이핑을 했습니다.
3. 테스트가 진짜 코드 리뷰어입니다. 제가 하루 만에 68개의 도구를 한 줄씩 검토할 수는 없고, 솔직히 당신도 할 수 없습니다. 그래서 1,780개의 테스트, 개인정보 보호 카나리아(privacy canary), 그리고 CSP(Content Security Policy) 검사가 그 역할을 수행합니다. npm run verify (타입 체크(typecheck), 테스트, 빌드)는 프리-푸시 후크(pre-push hook)로 실행되므로, 빨간 코드(red code)가 제 노트북을 벗어나지 못하게 합니다.
제 일은 타이핑이 아니라 결정하는 것입니다. 무엇을 만들지, 그리고 무엇을 만들지 않을지를 결정하는 것이죠. HEIC에서 JPG로 변환하는 기능은 디코더 자체의 크기가 1.3 MB가 넘기 때문에 제외되었고, OCR(광학 문자 인식)은 아예 불가능합니다. 또한 "폰트 맵은 두 개의 독립적인 출처가 동의할 때만 배포된다"와 같은 규칙도 있습니다. AI는 자신감 있고 그럴듯해 보이는 문자 맵을 기꺼이 제공할 것입니다. 하지만 이 규칙이 존재하는 이유는 '보기 좋은 것'이 결코 '올바른 것'을 의미하지 않기 때문입니다.
그래서 네, 감(vibe)에 의존합니다. 하지만 그 감에는 테스트 스위트가 있습니다.
수치로 본 결과 (바이럴은 아닙니다)
Google Search Console에서 3개월간의 데이터:
| 섹션 | 페이지 수 | 노출수 | 클릭 수 | 평균 순위 |
|---|---|---|---|---|
| 단위 변환기 (km를 mi로 등) | 220 | 16,791 | 1 | 78.5 |
| ... |
총 노출수 20.8K. 클릭 수 6회. 평균 순위는 76으로, 즉 8페이지에 해당합니다. 아무도 페이지 8로 가지 않습니다. 페이지 8은 콘텐츠가 방치되는 곳입니다.
이 교훈을 받아들이는 데 시간이 걸렸습니다. "km to miles"를 검색하면 구글이 어떤 결과보다 먼저 자체 위젯에 답을 보여줍니다. 제 페이지는 결과 목록에서 16,791번이나 노출되었지만 클릭은 단 1건이었습니다. 저는 구글이 검색 페이지에 내장한 계산기보다 순위를 높일 수 없다는 것을 깨달았고, 애초에 시도해서도 안 된다는 것을 알았습니다.
하지만 제가 얻은 몇 안 되는 클릭들은 모두 니치(niche)했습니다: 매듭과 해리 단위, "query params to json", JSON 피벗 도구 같은 것들이었습니다. 페이지 1이 약한 아주 긴 검색어들 말입니다.
그래서 저는 의도적으로 더 니치하게 갔습니다
새로운 규칙은 이렇습니다. 니치하고, 보통 유료 결제가 필요하거나 업로드만 가능한 곳이며, 사람들이 적게 필요한 도구를 공략하는 것입니다. 그리고 그것들을 무료로, 기기 내에서 사용할 수 있게 만듭니다.
그것이 어떤 모습이었는지:
- 220개의
noindex처리된 변환기 페이지. 이 페이지들은 여전히 작동하고 링크도 되어 있지만, 사이트맵에서는 제외했습니다. 단 1회의 클릭을 위해 크롤링 예산(crawl budget)을 소모하고 있었기 때문입니다. - 항공 계산기: 측풍 성분(crosswind component), 풍향 보정각(wind correction angle), 밀도 고도(density altitude), 비행 시간 및 연료량. 조종사 및 학생 조종사들은 이 도구들이 필요하며, 현재 존재하는 많은 도구들은 유료이거나 사용하기 불편합니다.
- 인도 특화 도구: GSTIN 검증기, 인도 숫자 형식(lakh와 crore), 그리고 Krutidev 같은 레거시 힌디어 글꼴을 Unicode로 변환하는 도구. 인도의 정부 문서나 오래된 사무용 문서는 여전히 이 구형 글꼴로 타이핑되는 경우가 많고, 이를 변환하는 과정이 매우 어렵습니다.
- 각 페이지에 실제 콘텐츠: FAQ 스키마가 적용된 서버 렌더링 방식의 소개(about), 사용 방법(how-to), 그리고 FAQ 블록을 추가했습니다. 단순히 텍스트 상자만 있는 도구 페이지는 검색 엔진에서 순위가 높지 않습니다.
제가 어기지 않은 규칙은 하나 있습니다: 레거시 글꼴 변환기는 최소한 두 개의 독립적인 출처와 실제 텍스트를 기반으로 문자 맵(character map)을 확인했을 때만 배포됩니다. Shivaji 글꼴의 맵은 이 검증에 실패했습니다. 따라서 그 페이지는 누군가의 문서를 조용히 망가뜨리는 것보다 '점검 중' 상태로 유지됩니다. 잘못된 변환기가 없는 변환기보다 더 나쁩니다.
과거의 저에게 전하고 싶은 말
AI 자동 생성 콘텐츠
본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기