1인 개발자로서 AI 회계 SaaS를 구축한 방법 (그리고 다르게 했을 점)
요약
해고 후 4개월 만에 프리랜서를 위한 AI 회계 SaaS인 Tally Assistant를 구축한 1인 개발자의 경험담입니다. Next.js, Prisma, OpenAI, Clerk 등 현대적인 기술 스택을 활용한 아키텍처 설계와 실무적인 시행착오를 공유합니다.
핵심 포인트
- Next.js 16의 App Router와 정적 생성을 활용한 SEO 최적화
- Prisma의 타입 안정성을 통한 런타임 에러 방지 및 개발 효율 증대
- Clerk을 이용한 인증 기능 구현 및 Webhook 기반 데이터 동기화
- 무질서한 금융 데이터를 처리하기 위한 AI 기반 자동화 솔루션 구축
요약 (TL;DR): 개발직에서 해고되었습니다. 4개월 만에 프리랜서를 위한 AI 회계 도구를 구축했습니다. 기술 스택: Next.js 16, Prisma, OpenAI, Clerk, Inngest. Product Hunt에 출시했습니다. 여기 아키텍처, 실수, 그리고 몇 달의 시간을 아껴준 결정적인 선택이 있습니다.
배경 이야기
저는 중국 청두 출신의 풀스택 개발자 Dominic입니다 🇨🇳. 정규직 프로그래밍 직업을 갖기 전까지 수년간 국제 고객들을 대상으로 프리랜서 활동을 했습니다. 그러다 해고를 당했습니다.
즉시 구직 활동을 하는 대신, 프리랜서 시절 저를 가장 괴롭혔던 문제인 회계 (bookkeeping) 문제를 해결하기로 결심했습니다.
매달 저는 Stripe, PayPal, Wise, 그리고 중국 은행에서 CSV 파일을 다운로드했는데, 각각의 형식이 완전히 달랐습니다. 날짜 형식을 재조정하고, 유럽식 쉼표를 변환하고, 통화를 맞추고, 수동으로 인보이스를 작성하는 데 몇 시간을 소비했습니다. 그러고 나서 결제가 늦어지는 고객들을 어색하게 독촉하곤 했습니다.
기존 도구들을 살펴보았습니다. QuickBooks와 Xero는 프리랜서가 아닌 회계사를 위해 만들어진 것처럼 느껴졌습니다. 그들은 깔끔하고 표준화된 데이터를 기대했습니다. 실제 프리랜서의 데이터는 제각각인 컬럼 헤더, 다른 날짜 형식, 다른 언어 등 매우 무질서합니다.
그래서 저는 Tally Assistant를 만들었습니다. 어떤 데이터든 던져 넣으면 나머지는 알아서 처리하는 AI 회계 도구입니다.
기술 스택 (그리고 각 요소를 선택한 이유)
Next.js 16 (App Router)
SEO를 위한 SSR (Server-Side Rendering)과 깔끔한 파일 기반 라우팅을 위한 App Router가 필요했기에 Next.js를 선택했습니다. 랜딩 페이지, 블로그, 기능 페이지, 비교 페이지 등은 모두 빌드 타임에 정적으로 생성됩니다. 마케팅 페이지에 클라이언트 사이드 JS가 없다는 것은 빠른 Core Web Vitals와 만족스러운 Google 검색 순위를 의미합니다.
잘한 점: 60개 이상의 모든 SEO 페이지에 대한 정적 생성 (Static generation). 콘텐츠 파일로부터 사이트맵 자동 생성.
바꿀 점: 블로그 페이지에 ISR (Incremental Static Regeneration, 증분 정적 재생성)을 더 일찍 설정하여, 새 포스트를 올릴 때마다 전체 재배포를 할 필요가 없도록 했을 것입니다.
Prisma + PostgreSQL
Prisma의 타입 안정성 쿼리 (type-safe queries)는 1인 개발자에게 초능력과 같습니다. 스키마 (schema)를 변경하고 prisma migrate dev를 실행하면, TypeScript가 어디가 망가졌는지 모든 곳을 알려줍니다. 컬럼 (column) 누락으로 인한 조용한 런타임 에러 (runtime error)가 발생하지 않습니다.
저를 살린 한 가지: postinstall 스크립트에서 prisma generate를 사용한 것입니다. 덕분에 Vercel은 배포 시점에 항상 최신 클라이언트 (client)를 생성할 수 있었습니다.
인증을 위한 Clerk
인증 (auth) 기능을 처음부터 직접 만들고 싶지 않았습니다. 이는 지루하고, 보안상 매우 중요하며, 제품의 본질적인 가치를 더해주지 않기 때문입니다. Clerk은 저에게 다음을 제공했습니다:
- Google, GitHub, 이메일을 통한 회원가입/로그인
- 단 10줄의 코드로 구현하는 소셜 로그인
- Webhook 기반의 Prisma 데이터베이스 사용자 동기화
- 유지보수 제로
Webhook 패턴은 잘못 구현하기 쉽기 때문에 설명할 가치가 있습니다:
// Clerk webhook → Prisma에서 사용자 생성 또는 업데이트
// 사용자 생성, 업데이트, 삭제 시 실행됨
export async function POST(req: Request) {
...
OpenAI — 핵심 엔진
AI는 네 가지 작업을 수행하며, 각 작업마다 서로 다른 프롬프팅 (prompting) 전략을 사용합니다:
1. CSV 파싱 (parsing): CSV의 처음 20개 행을 원문 텍스트로 전송하면, GPT-4o가 날짜 형식 감지 및 통화 식별 기능이 포함된 컬럼 타입의 JSON 맵 (map)을 반환합니다. 핵심 통찰은 AI에게 모든 행을 파싱하라고 요청하지 않는 것입니다. 대신 AI에게 _스키마 (schema)를 설명_하도록 요청한 뒤, 나머지 부분은 결정론적 코드 (deterministic code)를 사용하여 파싱합니다. 이 방식이 더 빠르고, 저렴하며, 더 신뢰할 수 있습니다.
2. 영수증 OCR: 결제 스크릿샷을 읽기 위해 GPT-4V (vision)를 사용합니다. 프롬프트는 가맹점, 금액, 통화, 날짜 필드를 포함한 구조화된 JSON 출력을 명시적으로 요청합니다. 한 이미지에 여러 트랜잭션 (transaction)이 있는 경우, 모델에게 배열 (array)을 반환하도록 요청하여 감지합니다.
3. 인보이스 (invoice) 생성: 자연어 → 구조화된 인보이스. 프롬프트는 자유 형식의 설명을 세금 계산이 포함된 품목 (line items)으로 변환합니다. 모델이 수학적 계산을 정확히 수행할 수 있도록 시스템 프롬프트 (system prompt)에 사용자가 설정한 세율과 통화를 포함합니다.
4. 재무 채팅 어시스턴트 (Finance chat assistant): RAG (Retrieval-Augmented Generation) 방식 — 사용자가 질문을 하면, Prisma에서 사용자의 재무 데이터를 쿼리(query)한 다음, 그 결과와 질문을 모델에 보내 요약합니다.
가장 큰 AI 교훈: 모델의 버전보다 프롬프트 (prompt)의 품질이 더 중요합니다. 저는 AI 파이프라인 (pipeline)의 다른 어떤 부분보다 프롬프트 디자인을 반복하는 데 더 많은 시간을 소비했습니다. 명시적인 출력 형식 지침이 포함된 잘 구조화된 시스템 프롬프트 (system prompt)는 모호한 프롬프트를 사용하는 더 큰 모델을 언제나 이깁니다.
백그라운드 작업(Background Jobs)을 위한 Inngest
두 가지 크론 잡 (cron jobs)이 필요했습니다: 매일 미납 송장(overdue invoice)을 확인하는 작업과 리마인더 이메일을 보내는 작업입니다. Inngest는 스텝 함수 (step functions)를 통해 이 두 가지를 모두 처리합니다. 각 스텝은 개별적으로 재시도 (retry)가 가능하며 대시보드에서 확인할 수 있습니다.
export const checkOverdueInvoices = inngest.createFunction(
{ id: "check-overdue-invoices", triggers: [{ cron: "TZ=America/New_York 0 8 * * *" }] },
async ({ step }) => {
...
이메일 발송을 위한 Resend
송장 및 결제 리마인더를 위한 트랜잭션 이메일 (Transactional email) 용도입니다. 단순한 REST API를 제공하며, SMTP 설정이 필요 없고 전달률 (deliverability)이 매우 뛰어납니다. 주의할 점 한 가지는, @yourdomain.com 주소로 이메일을 보내기 전에 Resend에서 도메인을 인증해야 한다는 것입니다. 저는 첫 번째 리마인더 이메일 배치가 소리 없이 실패했을 때 이 사실을 뼈아프게 배웠습니다.
성과를 거둔 SEO 전략
"Tally Assistant"라는 키워드로 순위를 높이는 것만으로는 충분하지 않다는 것을 알고 있었습니다. 존재조차 모르는 제품을 검색하는 사람은 없기 때문입니다. 그래서 저는 특정 검색 쿼리 (search queries)를 타겟팅하는 **60개 이상의 정적 랜딩 페이지 (static landing pages)**를 구축했습니다:
- 11개의 기능 페이지 (feature pages): "AI 송장 생성기 (AI invoice generator)", "영수증 스캐너 (receipt scanner)", "CSV 가져오기 (CSV import)" 타겟팅
- 9개의 산업별 페이지 (industry pages): "Shopify 판매자를 위한 장부 정리 (bookkeeping for Shopify sellers)", "사진작가를 위한 장부 정리 (bookkeeping for photographers)" 타겟팅
- 6개의 비교 페이지 (comparison pages): "QuickBooks 대안 (QuickBooks alternative)", "Wave 대안 (Wave alternative)" 타겟팅
- 15개의 용어 사전 (glossary terms): "매출채권이란 무엇인가 (what is accounts receivable)", "송장 정의 (invoice definition)" 타겟팅
- 4개의 심층 가이드 (in-depth guides) 및 6개의 무료 템플릿 페이지 (free template pages)
모든 페이지는 다음을 사용합니다:
- JSON-LD 구조화된 데이터 (Structured data) (FAQPage, Article, DefinedTerm 스키마)
- 적절한 헤딩 계층 구조 (heading hierarchy)를 갖춘 시맨틱 HTML (Semantic HTML)
- 관련 콘텐츠 간의 내부 링크 (Internal linking)
- 사후 고려 사항이 아닌, 검색 스니펫 (search snippets) 형태로 작성된 메타 설명 (Meta descriptions)
이 복합적인 SEO 전략을 구축하는 데 약 2주의 작성 시간이 소요되었으며, 이미 유기적 트래픽 (organic traffic)을 생성하고 있습니다.
내가 저지른 세 가지 실수 (당신은 그러지 않도록)
1. 아무도 요청하지 않은 기능을 만들었습니다
드래그 앤 드롭 로고 배치와 사용자 지정 색상 선택기가 포함된 PDF 영수증 디자이너를 만드는 데 3주를 썼습니다. 아름다웠지만, 쓸모없었습니다. 아무도 요청하지 않았습니다. 마침내 실제 프리랜서들과 대화를 나누었을 때, 그들은 이렇게 말했습니다: "그냥 내 은행 계좌와 CSV 가져오기 (CSV import) 기능이 잘 작동하게만 해주세요." 저는 디자이너 기능을 삭제하고 그 시간을 범용 CSV 파싱 (CSV parsing)에 투자했습니다. 그리고 이것이 사용자들이 가장 많이 언급하는 1순위 기능이 되었습니다.
교훈: 구축하기 전에 사용자들과 대화하세요. 이제 저는 코드를 작성하기 전에 Reddit의 r/freelance에 매 기능에 대한 피드백을 요청하는 글을 올립니다.
2. 너무 조용하게 출시했습니다
저는 구축하는 데 4개월을 썼지만, 배포 (distribution)에 대해서는 0분을 생각했습니다. 저의 첫 출시는 12명의 팔로워를 가진 제 계정에 "무언가를 만들었습니다"라고 트윗하는 것이었습니다. 반응은 냉담했습니다.
실제로 효과가 있었던 것들: Product Hunt (태평양 표준시 기준 화요일 자정으로 예약), 피드백을 요청하는 Reddit 게시물 (홍보가 아닌 방식), 장부 정리 (bookkeeping)에 대해 불평하는 사람들에게 보내는 X(구 트위터) 답글 (링크가 아닌 도움을 제안하는 방식), 그리고 제가 게시물을 올리는 날인지 여부와 상관없이 계속해서 트래픽을 가져다주는 이 SEO 페이지들입니다.
교훈: 배포는 사후 고려 사항이 아닙니다. 시간의 50%를 배포를 위해 할당하세요.
3. 기본 통화를 USD로 하드코딩(hardcoded)했습니다
전체 다중 통화 (multi-currency) 시스템을 구축했지만, 보고서의 기본 통화를 USD로 설정해 두었습니다. 독일의 한 사용자가 물었습니다: "왜 모든 것이 달러로 되어 있나요? 저는 베를린에 있는데요." 당황스러웠지만 수정은 쉬웠습니다. 이제 기본 통화는 사용자가 설정에서 구성하는 대로 적용됩니다.
교훈: 사용자에 대한 가정을 하드코딩하지 마세요. 저는 중국에 있지만, 제 사용자는 어디에나 있습니다.
다음 단계는
- Stripe, PayPal, Wise API와의 직접 연동 (현재는 CSV 기반)
- 영수증 스캔을 위한 iOS/Android 컴패니언 앱 (companion app)
- CSV 파서 (parser) 오픈 소스화 (제가 가장 자랑스럽게 생각하는 부분입니다)
직접 체험해 보거나 무엇이든 물어보세요
출시 기간 동안 제품은 무료입니다: tallyassistant.com
만약 여러분이 1인 개발자로서 SaaS를 구축하고 있거나 — 혹은 구축할 계획이라면 — 댓글로 무엇이든 물어보세요. 모든 질문에 답변해 드리겠습니다. 특히 다음과 같은 주제에 대해 이야기 나누는 것을 환영합니다:
- 구조화된 데이터 추출을 위한 프롬프트 엔지니어링 (Prompt engineering)
- 멀티 테넌트 (multi-tenant) SaaS를 위한 Prisma 스키마 (schema) 설계
- 신규 제품을 위한 SEO 전략
- 1인 SaaS의 실제 경제성 (비용, 매출, 번 레이트 (burn rate))
중국 청두에서 제작됨 🐼 · Next.js + Prisma + OpenAI + Clerk + Inngest + Resend
AI 자동 생성 콘텐츠
본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기