Lazy Developer10 min

3일에 메이저 기능 3개 동시에 만들었다

Apsity에 Keyword Search·MCP 서버·월간 매거진 셋을 3일 만에 동시에 출시했다. 41 커밋 +15,380줄을 어떻게 동시에 굴렸는지 — Phase 분할, Claude 활용 전략, Day별 회고를 정리했다.

On this page (10)

2026년 4월 · 귀찮은개발자 EP.21

보통 메이저 기능 하나 만드는 데 1주일 잡는다. 설계, 구현, UI, i18n, 마케팅 페이지, 문서. 한 번에 하나씩 한다. 그래야 안 깨진다고 배웠다. 근데 4월 28일부터 30일까지 사흘 동안 메이저 기능 셋을 동시에 끝냈다. 키워드 검색, MCP 서버, 월간 매거진. 41 커밋, +15,380줄.

결론부터 말한다. 셋을 같은 손으로 동시에 만든 게 아니다. 매일 하나씩 끝냈다. 같은 사흘에 끝났을 뿐이다. 가능했던 이유는 두 가지였다. 기능별로 Phase를 명확히 잘랐고, Claude한테 던지는 단위를 작게 잡았다. 이 글은 그 사흘의 기록이다.

EP.02에서 Apsity 대시보드를 만들었고, EP.03에서 AI 인사이트를 붙였다. 거기까지가 베이스다. 이번에 붙인 셋은 그 위에 올라가는 다음 단계 — 키워드 발굴, Claude에서 직접 호출, 매달 1일 자동 매거진이다.

빠르게 보기

– 4/28~4/30 사흘. 41 커밋 +15,380줄. Apsity 메이저 기능 3개 동시 출시
– Day 1: 키워드 검색 (14 tasks 한 번에). 18개국 Top 50 + AI 요약 + 워치리스트
– Day 2: MCP 서버. Claude에서 Apsity 데이터 직접 호출. FREE는 키 발급 자체 차단
– Day 3: 월간 매거진. Phase 1~7 하루에 완성. 매달 1일 자동 발송
– 가능했던 이유: 기능별 Phase 분할 + Claude한테 던지는 단위 작게

왜 셋이 동시에 필요했나

각자 따로 필요했던 게 아니다. 하나의 흐름이었다. Apsity는 이미 등록된 앱의 데이터는 잘 보여준다. 매출, 다운로드, 키워드 순위, 경쟁앱 변화. 근데 빠진 게 있었다. “새 키워드를 어디서 찾지?”와 “이 데이터를 다른 도구에서 어떻게 쓰지?”와 “매달 무슨 일이 있었는지 한눈에 보고 싶다”.

키워드 검색은 발굴 도구다. ASO를 한다는 건 결국 어떤 키워드에 노출시킬지 정하는 일이다. 등록된 앱 키워드만 보면 우물 안이다. 다른 앱들이 어떤 키워드로 뜨는지를 봐야 한다. iTunes Top 50을 18개국에서 긁어와서 보여주고, AI가 요약하고, 마음에 드는 키워드는 워치리스트에 넣는다.

MCP 서버는 출구다. Apsity 안에서만 보던 데이터를 Claude에서 자연어로 묻고 싶다. “내 앱 어제 매출 어땠어?”를 Claude한테 던지면 Apsity가 답해준다. 이건 만들어야겠다고 EP.15에서 npm-subscriber-mcp 만들 때부터 생각했었다.

월간 매거진은 회고다. 매일 알림은 EP.03에서 붙였다. 근데 매일 보면 노이즈다. 한 달이 지나서 “이번 달 뭐가 있었지?”를 보고 싶을 때 흩어진 데이터를 다 뒤지게 된다. 매달 1일에 자동으로 정리해서 메일로 보내주면 끝이다.

셋을 묶어서 보면 이렇다. 발굴 → 활용 → 회고. 이게 빠진 워크플로의 양 끝이었다. 동시에 출시한 이유다.

동시에 가능했던 이유 — Phase 쪼개기

일주일짜리 기능을 사흘에 셋 끝냈다는 게 가능했던 이유는 단순하다. 기능 하나를 통째로 보지 않고, 작은 Phase로 쪼갰다. 각 Phase가 끝날 때마다 동작 확인하고 커밋한다.

예를 들어 월간 매거진은 이렇게 잘랐다.

// Magazine Phase 분할

Phase 1 — 언어 설정 (Settings에 ko/en 추가)
Phase 2 — 한 달 데이터 집계 함수
Phase 3 — Claude로 매거진 본문 생성
Phase 4 — 카드 컴포넌트 4종 (지표/차트/리뷰/제안)
Phase 5 — 매거진 페이지 렌더
Phase 6 — 이메일 발송 (인라인 4장)
Phase 7 — CLI 테스트 도구

Phase 1과 2는 독립적이다. Phase 3은 2의 출력을 받는다. Phase 4와 5는 3의 결과로 만든다. 의존성 그래프를 그리면 직렬로 보이지만, 실제로는 Phase 2와 4를 병행할 수 있다. 데이터 모양은 미리 정해놓고, 집계 쿼리와 카드 UI를 따로 짠다.

이렇게 쪼개면 좋은 점이 두 개다. 첫째, Claude한테 던지는 단위가 작아진다. “매거진 만들어줘”는 너무 크다. “Phase 4: 지표 카드 컴포넌트만 만들어줘. 입력은 이런 객체고 출력은 React 컴포넌트야”는 명확하다. 둘째, 중간에 결과가 이상하면 그 Phase에서 멈출 수 있다. 하루를 통째로 날리는 일이 줄어든다.

키워드 검색은 14 tasks로 잘랐다. MCP는 서버 코드 → 인증 → 게이팅 → UI → 문서 다섯 단계로 잘랐다. 큰 그림은 머리에 있되, 실제 진행은 작은 단위로 하나씩 끝낸다. 이게 핵심이다.

Apsity Keyword Search 검색 화면 — 키워드 입력 + 최근 검색어 칩 + AI Summary 카드
Day 1 — 검색 입력 + AI Summary / GoCodeLab
Apsity Keyword Search 결과 리스트 — Top 50 앱(Lightroom·Picsart·Photo Editor·Airbrush·Facetune 등)
Day 1 — Top 50 결과 리스트 / GoCodeLab

4월 28일은 키워드 검색에 쏟았다. 14 tasks를 한 번에 출시했다. 18개국 iTunes Top 50을 검색해서 보여주는 도구다. 등록된 앱이 아니라 전 세계 어떤 앱이든 키워드로 찾을 수 있다.

요구사항부터 정리했다. 검색 폼, 결과 리스트, 결과 행 클릭하면 앱 상세 사이드 패널, AI 요약, 검색 히스토리, 워치리스트. 무료/유료 필터, 일일 한도, 입력 검증. 한·영 i18n. 그리고 마케팅. Pricing 페이지에 새 기능 표시, 랜딩에 데모, 블로그에 한·영 안내.

14 tasks를 정리하면 이렇다.

// Keyword Search 14 tasks

Backend: iTunes Top 50 헬퍼, 일일 한도, 검색 기록, 입력 검증
API: POST /api/keywords/search, history GET/DELETE, summary
DB: KeywordSearchHistory + plan-limits 확장
Components: SearchForm, SearchResults, SearchHistory, AISummary, SearchTab
UI: /dashboard/keywords 탭, 사이드 패널, 무료/유료 필터, 워치리스트
i18n: ko/en 완전 분리
Marketing: Pricing/Landing/Blog 한·영

핵심은 게이팅이었다. 일일 한도는 plan-limits에 넣었다. FREE는 하루 N회, STARTER 이상은 더 많이. 사이드 패널 상세 정보는 STARTER+, 워치리스트와 매일 스냅샷은 PRO 게이팅. UI에서 막고 API에서 또 막는다. 이건 EP.07에서 LemonSqueezy 결제 붙일 때 익힌 패턴이다. UI 레이어만 막으면 누군가는 우회한다.

출시하고 나서 후속 fix가 셋 있었다. AI 요약이 마크다운으로 들어와서 별표가 그대로 보이는 문제 — 렌더링 로직 추가. SWR 캐시가 검색할 때마다 깜빡이는 문제 — keepPreviousData 옵션. 사이드 패널 열었을 때 배경 스크롤이 같이 움직이는 문제 — body overflow lock. 이 셋은 직접 써보지 않으면 안 보인다. “돌아가는 코드”와 “쓸 수 있는 제품” 사이의 차이다. 80% 먼저 빠르게 내고, 나머지 20%는 실제 문제 생기면 고친다.

Day 2 (4/29) — MCP 서버

Apsity Settings — MCP API 키 발급 + Claude Desktop 설정 코드 스니펫
Day 2 — MCP API 키 발급 화면 / GoCodeLab

4월 29일은 MCP에 쏟았다. EP.15에서 npm-subscriber-mcp 만들면서 패턴을 익혔다. 거기는 npm 다운로드를 Claude에 노출하는 거였고, 이번엔 Apsity 데이터를 노출한다.

MCP 서버 자체는 이틀이면 끝난다. @modelcontextprotocol/sdk로 stdio 서버 만들고, tool 정의하고, 핸들러에서 Apsity API 호출하면 된다. 진짜 시간을 쓴 건 게이팅이었다.

고민은 이거였다. MCP는 Claude 같은 외부 클라이언트에서 호출한다. 즉 사용자가 직접 컨트롤하지 않는 환경에서 키가 돌아간다. FREE 플랜이 무제한으로 호출하면 비용이 나간다. 어떻게 막을 것인가.

// MCP 게이팅 — defense in depth

Layer 1 — Settings UI: FREE는 발급 버튼 비활성화
Layer 2 — POST /api/mcp/keys: 서버에서 플랜 확인 후 차단
Layer 3 — MCP 호출 시: 키 검증 + 플랜 재확인
Layer 4 — Tool 별로 플랜 게이팅 (PRO 전용 tool 분리)

UI를 우회하려고 직접 API 때려도 막힌다. 키를 미리 받아두고 플랜을 다운그레이드해도 호출 시점에 막힌다. 보안은 한 곳에서 막으면 안 된다는 게 EP.06과 EP.07에서 배운 거다.

UI는 Settings에 새 탭으로 붙였다. API 키 발급, 발급된 키 목록, 폐기. 키는 한 번만 보여준다. 나머지는 마지막 4자리만. 이건 GitHub Personal Access Token 패턴 그대로다.

마케팅은 랜딩에 MCP 데모 섹션을 추가하고, /docs/mcp에 가이드를 한·영으로 썼다. Claude Desktop 설정 JSON 예시, 사용 예시 대화, 발급 방법까지. 마케팅 없이 기능만 있으면 아무도 모른다.

Day 3 (4/30) — 월간 매거진

Apsity 월간 매거진 1페이지 — 4월 매출/다운로드/별점/시장 요약 카드
Day 3 — 월간 매거진 1페이지(Wrap, 견본 데이터) / GoCodeLab

4월 30일은 월간 매거진. 위에서 본 Phase 1~7을 하루에 끝냈다. 가능했던 건 매거진이 새로운 데이터를 만드는 게 아니라 기존 데이터를 정리하는 거였기 때문이다. 매출, 다운로드, 리뷰, 키워드 — 다 이미 있다. 합치고, AI한테 요약 시키고, 카드로 보여주고, 메일로 보내면 된다.

Phase 1~3은 데이터 파이프라인이었다. Settings에 매거진 언어 설정 추가. 한 달치 데이터 집계 함수. Claude로 매거진 본문 생성. 여기서 통화 환산을 처음 붙였다. KRW 매출과 USD 매출이 섞여 있는데, 매거진 표시 통화가 KRW면 환산해서 합쳐야 한다. 환율은 매월 1일 기준값으로 캐시. 그리고 Subscription 중복 제거 — 같은 결제가 두 번 들어온 케이스 정리.

Phase 4~7은 출력 단이었다. 카드 컴포넌트 4종 — 핵심 지표, 추이 차트, 리뷰 하이라이트, 다음 달 제안. 매거진 페이지 렌더. 이메일 발송 (Resend, 카드 4장 인라인). CLI 테스트 도구로 임의 월 매거진 미리 보기.

5월 1일 한정 테스트 cron
정식 cron은 매달 1일 새벽 5시(KST)에 돌게 짰다. 근데 30일 출시했는데 1일까지 못 기다린다. 5월 1일에만 도는 한정 cron을 추가해서 나에게 한국어/영어 매거진을 동시에 발송하게 했다. 검증되면 다음 달부터는 정식 cron만 동작한다.

매거진은 데이터 정리 + 자동 발송이지만, 사용자 입장에서는 “매달 1일에 메일이 온다”가 전부다. 안에 들어간 카드 4장과 환율 처리, 중복 제거 같은 건 보이지 않는다. 보이지 않는 디테일에서 시간이 든다. 이건 EP.04에서 FeedMission 만들 때부터 알고 있었다. 제품 수준으로 올리는 시간은 MVP 시간보다 길다.

중간에 끼어든 작업들

3일 동안 메이저 기능만 한 게 아니다. 중간에 끼어든 작업이 셋 있었다.

첫째, 결제→가입 역순 케이스 자동 복구. 결제는 됐는데 가입이 안 된 케이스가 가끔 발생한다. 이전에는 수동으로 처리했다. PendingSubscription 모델을 만들어서 결제 시 임시 저장하고, 가입 완료 시 매칭해서 자동 활성화. 이건 EP.07에서 LemonSqueezy 붙이면서 알았던 엣지 케이스인데 미뤘던 거다.

둘째, 가격 표시 위치 VAT/세금 별도 문구. Pricing 페이지에 “VAT 별도”를 작게 추가. 작은 변경인데 안 하면 결제 후에 “왜 더 붙어요?” 문의가 온다.

셋째, 영문 9편의 한국어 버전 추가. 마케팅 블로그가 영어 우선이었는데, 한국 사용자한테는 한국어가 필요하다. 9편 번역. 그리고 navigator.languages 배열 전체로 한국어 감지 — 일부 브라우저에서 첫 항목만 보면 놓치는 케이스가 있었다.

이런 작업들은 메이저 기능 사이사이에 들어간다. 한 시간씩 끊어서 처리한다. “한 번에 다 한다”는 환상이고, 실제로는 작은 작업들이 같이 굴러간다.

회고 — 가능했던 진짜 이유

3일에 셋 가능했던 이유를 다시 정리하면 이렇다.

첫째, 새 스택이 없었다. Next.js + Supabase + Vercel + Prisma + Resend. 전부 EP.02부터 굴러온 조합이다. 새로운 거 익히는 시간이 없으면 구현 시간만 든다. MCP는 EP.15에서 한 번 만들어봐서 패턴을 알고 있었다.

둘째, Phase를 작게 잘랐다. 한 Phase가 30분~2시간 안에 끝나게. 이러면 Claude한테 던지는 입력도 작아지고, 결과 검증도 빨라진다. “매거진 시스템 만들어줘” 대신 “매거진 본문을 Claude로 생성하는 함수만. 입력은 이런 객체, 출력은 이런 마크다운”.

셋째, 동작 확인을 매 Phase마다 했다. 끝까지 안 가고 중간에 본다. EP.04에서 FeedMission 만들 때 52분에 MVP 나왔는데 그 다음 6일이 더 들었던 거랑 같은 교훈이다. 빨리 만드는 게 빨리 끝나는 게 아니다. 빨리 검증하는 게 빨리 끝나는 거다.

넷째, 마케팅을 같이 굴렸다. 보통 기능 만들고 나서 마케팅 페이지 따로 만든다. 그러면 또 며칠이 든다. 이번엔 Pricing/Landing/Blog 한·영을 기능 task에 같이 넣었다. 출시 = 기능 + 마케팅 + 문서. 한 번에 끝나야 진짜 출시다.

다음 편에서

키워드 검색이 어떻게 동작하는지, MCP 서버 구조, 매거진 데이터 파이프라인 — 각각 따로 다룰 만한 주제가 많다. EP.22부터는 셋 중 하나씩 깊이 파는 글로 이어진다. 이번 편은 사흘에 셋을 어떻게 굴렸는지의 기록이다.

4월의 마지막 주는 이렇게 끝났다. 5월 1일 새벽 5시에 매거진이 처음으로 자동 발송된다. 그게 잘 가면 진짜 끝이다.

자주 묻는 질문

Q. 기능 3개를 동시에 만든다는 게 정말 가능한가?
기능을 통째로 동시에 만든 건 아니다. 하루에 하나씩 메이저 기능을 끝냈고, 각 기능 안에서는 Phase로 쪼개서 순서대로 굴렸다. 동시 처리는 같은 시간에 두 손으로 두 개 만든다는 뜻이 아니라, 일주일짜리 일을 매일 다른 결로 끝낸다는 뜻이다.
Q. MCP 서버는 어떻게 만들었나?
@modelcontextprotocol/sdk 기반 stdio 서버로 시작했다. 핵심은 인증과 게이팅이다. Settings UI에서 API 키를 발급받고, 키 발급 자체를 FREE 플랜에서 차단하는 defense in depth 구조로 짰다. UI에서 막아도 API에서 다시 막는다.
Q. Phase 분할은 어떻게 하나?
독립 실행 가능한 단위로 잘랐다. 매거진의 경우 1~3은 데이터 파이프라인, 4~7은 렌더링·이메일·CLI다. 각 Phase가 끝날 때마다 커밋하고 동작을 확인했다. 끝까지 한 번에 가지 않고 중간 결과를 본다.
Q. Claude한테 다 맡기면 이렇게 빠른가?
맡긴 게 아니다. 내가 요구사항을 명확히 정리하고, 각 Phase의 입출력을 정의하고, Claude한테 구현을 던지고, 결과를 확인하고, 다음 Phase로 넘긴다. 빠른 건 코드 생성이 아니라 판단 루프 전체다. 요구사항이 흐릿하면 결과도 흐릿하게 나온다.
📊 Apsity
App Store 데이터, AI가 자동으로 분석해준다
매출·다운로드·리뷰·키워드를 한 화면에서 본다. Claude MCP로 자연어 질문도 가능. 12개 앱 운영하다 직접 만든 인디 SaaS다.
무료로 체험하기 →

관련 글

Share