MyApplio
Project Case Study
MyApplio
이력서와 지원 현황, 면접 준비를 하나의 흐름으로 이어주는 AI 기반 구직 워크스페이스입니다. Next.js 16과 Supabase 위에 올렸고, Gemini로 이력서 PDF 가져오기와 채용 공고 탐색, 커버레터 초안 작성을 처리합니다. 공고를 클릭 한 번으로 저장하는 크롬 확장 프로그램도 함께 제공합니다.
라이브 프로젝트 보기 →Overview
MyApplio는 AI가 거드는 구직 워크스페이스입니다. 랜딩 페이지에는 "Resume to offer · AI assisted" 라는 한 줄을 걸었고, 그 아래에 "Resumes, applications, interviews. Nothing falls through." 라는 약속을 두었습니다.
대부분의 구직자는 스프레드시트와 이력서 파일, 이메일, 그리고 수십 개의 브라우저 탭에 흩어진 채로 구직을 이어갑니다. 도구 하나가 한 단계씩만 맡고 있고, 서로 무슨 일이 있었는지 알지 못합니다. MyApplio는 단계 사이를 잇는 일 자체가 제품이라는 생각에서 출발했습니다. 저장해 둔 공고가 지원 건이 되고, 지원 건은 이력서를 끌어다 쓰고, 이력서는 공고에 맞춰 다듬어지며, 면접 준비는 그 둘의 맥락을 그대로 물려받습니다.
Problem
공고를 미처 기록하기 전에 사라지거나, 상세 내용 없이 스프레드시트에 붙여넣기만 하게 됩니다
이력서가 거의 똑같은 파일 여러 개로 갈라지면서 어느 것이 최신인지 알 수 없게 됩니다
커버레터를 지원할 때마다 빈 화면에서 다시 시작합니다
마감일과 후속 연락을 기억에 의존합니다
진행이 멈춘 지원 건을 그것을 만들어 낸 자료와 이어 볼 방법이 없습니다
Information Architecture
대시보드는 하나의 목록이 아니라 실제 구직 과정에 등장하는 대상을 중심으로 짰습니다.
수집 — ai-job-search로 공고를 찾고, 크롬 확장으로 클릭 한 번에 담아 둡니다
파이프라인 — applications, companies, roles, people, events, notes, links
자료 — resume, cv, cover-letter, 그리고 공고와 이력서를 견줘 보는 match
준비 — interview-prep, review, ready, goals
공개 영역 — resume-directory, resume-search, 공유할 수 있는 공개 이력서 페이지
Technical Implementation
Stack
| 영역 | 구현 | 비고 |
|---|---|---|
| 프레임워크 | Next.js 16.2.6 + React 19.2.3 | App Router, React Compiler 사용 |
| 데이터·인증 | Supabase (Postgres, Auth, SSR) | 마이그레이션 193개, 데모 계정 읽기 전용까지 RLS로 관리 |
| 서버 상태 | TanStack React Query | 대시보드 캐시와 무효화 처리 |
| AI | Google Gemini 2.5 Flash / Flash-Lite | SDK 없이 REST를 직접 호출 |
| 요청 제한 | Upstash Redis + Ratelimit | AI 엔드포인트 남용을 막습니다 |
| 결제 | Polar | HTTP 직접 호출, 웹훅은 standardwebhooks로 검증 |
| 문서 처리 | pdfjs-dist, jsPDF, html2canvas, ExcelJS | PDF 가져오기와 PDF·스프레드시트 내보내기 |
| 알림 | web-push (VAPID) + Supabase pg_cron | 사용자 타임존에 맞춘 발송, 기기 실패 시 백오프 적용 |
작업에 따라 모델 등급을 나눴습니다
AI 호출은 모두 Gemini REST 엔드포인트를 직접 fetch 하는 방식입니다. 무조건 상위 모델을 쓰지 않고, 호출하는 자리마다 필요한 등급을 골라 씁니다.
`gemini-2.5-flash` — 답이 열려 있는 생성 작업에 씁니다. 공고 탐색, 커버레터 작성, 이력서 생성, 면접 피드백이 여기에 해당합니다.
`gemini-2.5-flash-lite` — 형식이 정해진 대량 추출 작업에 씁니다. PDF 가져오기, 노트 가져오기, 공고 매칭, 이력서 코칭, 스킬 동기화가 여기에 해당합니다.
두 모델 모두 환경변수로 바꿀 수 있고 기본값을 코드에 두었기 때문에, 모델을 교체할 때 코드를 고치지 않고 설정만 바꾸면 됩니다.
이력서 PDF 가져오기는 2단계로 처리합니다
이력서를 가져오는 순간이 제품에서 이탈이 가장 많은 지점이라, 이 부분에 가장 공을 들였습니다. pdfjs-dist로 텍스트를 뽑아낸 다음 Gemini가 두 번 일합니다. 먼저 초안을 뽑고, 이어서 그 초안을 원본과 맞춰 보며 고치는 단계를 거칩니다. 비용과 실행 시간이 늘어나지 않도록 입력 텍스트에는 상한을 뒀고, 파일이 큰 경우 20~40초 정도 걸립니다.
AI 크레딧을 제품의 일부로 다뤘습니다
사용량은 믿고 넘어갈 것이 아니라 재야 할 대상입니다. 호출할 때마다 어떤 모델을 썼는지와 토큰 사용량을 기록하고, 가입과 구독 시점에 크레딧을 지급합니다. 만료는 매일 밤 pg_cron이 훑어서 처리하고, 설정 페이지의 지연 만료를 보조 장치로 함께 뒀습니다.
크론 서비스 없이 스케줄링합니다
vercel.json도 없고 크론 라이브러리도 쓰지 않았습니다. 예약 작업은 Supabase의 pg_cron과 pg_net으로 돌리고, SQL 스크립트로 저장소에 커밋해 두었으며, x-cron-secret 헤더로 인증합니다. 일일 이벤트 푸시는 DB의 IO 부담 때문에 의도적으로 매시간으로 늦췄고, 30분 밑으로는 내리지 말라는 경고를 주석에 남겨 두었습니다. 다음에 이 코드를 볼 사람이 읽을 자리에 운영상의 제약을 적어 둔 셈입니다.
보안
프로덕션에서는 HSTS를 켜고, X-Frame-Options: DENY와 nosniff, COOP, 그리고 꼭 필요한 권한만 남긴 Permissions-Policy를 next.config.ts에서 설정합니다. CSP는 미들웨어에서 요청마다 새 nonce와 함께 내려주고, 위반 사항은 별도 엔드포인트로 모읍니다.
크롬 확장
MyApplio Job Clipper는 서비스 워커와 사이드 패널을 갖춘 Manifest V3 확장입니다. LinkedIn, Indeed, Glassdoor, Workday 같은 사이트에서 공고를 초안 상태의 지원 건으로 담아 옵니다. 추출 로직만 82KB 남짓 되는데, 채용 사이트마다 공고 구조가 전부 다르기 때문입니다. 완성된 지원 건이 아니라 초안으로 담는 데에는 이유가 있습니다. 저장은 클릭 한 번이어야 하고, 그 순간 사용자에게 아무것도 묻지 않아야 하기 때문입니다.
What This Case Study Shows
단계 사이의 연결이 곧 가치가 되는 제품을 설계한 경험
작업별로 모델을 나누고 호출마다 사용량을 재고 입력에 상한을 두어, 프로덕션 AI를 감당할 만한 비용으로 운영하는 방법
촘촘한 RLS와 193개의 마이그레이션, DB에 내장된 스케줄링까지 Supabase를 깊이 활용한 사례
브라우저 확장과 웹 앱을 하나의 제품으로 묶어 내놓은 경험
보안과 비용의 경계를 문서가 아니라 코드에 남기는 방식
Live: https://myapplio.com/
Role: 1인 개발 (제품 기획 → 스키마·RLS → AI 파이프라인 → 결제 → 확장 → SEO)