사용자 Gmail에서 채용 지원서를 보내는 AI를 구축하다 — LaTeX + MCP 아키텍처 소개
요약
사용자의 Gmail을 통해 채용 지원 프로세스를 자동화하는 Resume-MCP 시스템의 엔지니어링 아키텍처를 소개합니다. LaTeX를 활용한 ATS 친화적 PDF 생성과 MCP(Model Context Protocol)를 통한 에이전트 도구 통합 방식을 다룹니다.
핵심 포인트
- LaTeX를 사용하여 ATS(채용 관리 시스템)에 최적화된 결정론적 PDF 레이아웃 생성
- 이력서 섹션을 병렬 처리(Fan-out)하여 지연 시간 단축 및 시스템 안정성 확보
- MCP 서버 구현을 통해 AI 클라이언트가 직접 지원 프로세스를 실행하도록 설계
- FastAPI 기반의 단일 코드 경로로 웹, 봇, MCP 클라이언트 통합 관리
참고: 이 글은 Resume-MCP 블로그에 처음 게시되었습니다. 개발자 커뮤니티를 위해 다시 포스팅합니다.
채용 지원 과정은 대부분 복사-붙여넣기 노동입니다: 이력서를 수정하고, 자기소개서(cover note)를 새로 작성하고, 채용 담당자의 이메일을 찾고, 첨부하고, 보내고, 반복하는 것이죠. 저는 이 전체 루프를 하나의 붙여넣기로 압축하고 싶었습니다. 그래서 Resume-MCP를 구축했습니다. 채용 공고만 붙여넣으면 약 60초 후에 JD(Job Description)에 맞춰진 ATS 친화적인 PDF 이력서와 개인화된 커버 이메일이 사용자 본인의 Gmail에서 발송됩니다.
본 게시물은 기능 소개가 아니라 엔지니어링 스토리입니다. 아키텍처, 중요했던 선택들, 그리고 저를 괴롭혔던 부분들에 대해 다룹니다.
하나의 다이어그램으로 보는 파이프라인
Job description (붙여넣기 / PDF / DOCX / 이미지)
│
▼
...
전체 시스템은 FastAPI 앱입니다. 웹 앱, Telegram 봇, 그리고 — 개발자들이 관심을 가질 부분인 — 모든 MCP 클라이언트에서 구동할 수 있도록 하는 MCP 서버가 있습니다.
HTML-to-PDF 렌더러 대신 LaTeX를 사용한 이유
대부분의 이력서 빌더는 HTML을 렌더링하고 PDF로 인쇄합니다. 이는 화면에서는 괜찮아 보이지만, 채용 지원 시스템(ATS)에 의해 쉽게 파괴됩니다. 그 이유는 텍스트 추출 순서가 DOM이 우연히 갖게 되는 순서를 따르기 때문입니다.
LaTeX는 다음을 제공합니다:
- 결정론적 레이아웃 (Deterministic layout) — 동일한 입력은 바이트 단위로 동일한 PDF를 생성합니다.
단순한 방식은 "내 이력서와 직무 기술서(JD)를 줄 테니, 다시 써줘"라고 하는 것입니다. 하지만 이는 속도가 느리고, 입력값이 길어지면 모델이 맥락을 놓치게 됩니다. 대신 저는 이력서를 세 개의 독립적인 청크(chunk)로 나누어 병렬로 처리(fan out)합니다:
- 헤더 + 기술 (header + skills) — 기술 스택의 순서를 재조정하여 직무 기술서(JD)와 일치하는 키워드가 먼저 드러나도록 합니다.
- 경력 (experience) — 내용을 허구로 지어내지 않으면서, 직무 기술서(JD)의 언어를 반영하도록 불렛 포인트(bullet points)를 다시 작성합니다.
- 프로젝트 (projects) — 동일한 처리를 거치며, 이 청크는 *비치명적(non-fatal)*입니다. 만약 여기서 Gemini가 실패하더라도, 전체 요청을 실패시키는 대신 경고를 로그에 남기고 프로젝트 섹션을 비운 채로 계속 진행합니다.
병렬 처리(Fanning out)를 통해 지연 시간(latency)을 각 청크의 합계가 아닌, 가장 느린 단일 청크의 시간 정도로 단축했습니다. 비치명적인 프로젝트 청크는 생각보다 중요합니다. 이는 "이력서가 준비되었습니다"와 "무언가 오류가 발생했습니다, 다시 시도하세요"의 차이를 만듭니다.
MCP 관점
Model Context Protocol (MCP)를 사용하면 AI 클라이언트가 사용자의 도구를 직접 호출할 수 있습니다. 저는 동일한 HTTP 엔드포인트를 래핑(wrap)하는 MCP 서버를 /mcp에 마운트했습니다. 따라서 MCP를 인식하는 클라이언트에서 _"내 이력서를 이 직무 기술서(JD)에 맞춰 수정하고 지원해줘"_라고 말하면, 별도의 UI 없이 전체 파이프라인이 실행됩니다.
여기서 얻은 교훈은 다음과 같습니다. MCP 도구가 기존 FastAPI 라우트에 대한 얇은 래퍼(thin wrapper)이기 때문에, 유지 관리해야 할 코드 경로가 하나뿐이라는 점입니다. 웹 앱, 텔레그램 봇, 그리고 MCP 서버가 모두 동일한 엔드포인트를 호출합니다. 새로운 기능이 출시되면 모든 곳에 동시에 적용됩니다.
사용자의 Gmail로 발송하기 (모두가 궁금해하는 부분)
이메일은 noreply@myapp 같은 주소에서 발송되는 것이 아닙니다. OAuth를 통해 Gmail API를 사용하여 사용자의 실제 계정에서 발송됩니다. 이 방식 덕분에 마케팅 스팸이 아니라 실제 사람 지원자가 보낸 것처럼 채용 담당자의 편지함에 도달할 수 있습니다.
그 대가로 실제 OAuth 구현 작업이 필요합니다: gmail.send 스코프(scope), 리프레시 토큰(refresh-token) 처리, 그리고 까다로운 예외 케이스들(두 번째 동의 시 refresh_token 누락, 권한 부여 후 사용자가 스코프를 취소하는 경우 등)이 존재합니다. 사용자를 대신하여 동작하는 무언가를 구축하고 있다면, 기능 자체보다 인증 상태 머신(auth state machine)에 더 많은 시간을 할당하십시오.
과거의 나에게 해주고 싶은 말
- 초기에 결정론적인 출력 형식(deterministic output format)을 선택하십시오. 첫날에는 LaTeX가 무겁게 느껴졌지만, 나중에 "왜 PDF 모양이 달라졌지?"라며 고민할 수 있는 몇 주를 아껴주었습니다.
- AI 호출을 독립적으로 만들고 실패 시 기능이 점진적으로 저하(degrade gracefully)되도록 하십시오. 전부 아니면 전무(all-or-nothing)인 프롬프트보다는 최선을 다하는 청크(best-effort chunk) 방식이 더 낫습니다.
- 핵심 로직을 먼저 일반적인 HTTP 엔드포인트로 구축하십시오. 로직이 이미 깔끔한 API 뒤에 존재했기 때문에 봇(bot)과 MCP 서버를 만드는 과정은 매우 간단했습니다.
직접 시도해보고 싶다면: **resume-mcp.site**를 방문하세요. 직무 기술서(JD)를 붙여넣고, 맞춤형 PDF를 확인한 뒤, (Gmail을 연결했다면) 페이지를 떠나지 않고 바로 지원서를 보낼 수 있습니다.
LaTeX 템플릿, Gemini 청킹(chunking), 또는 MCP 배선(wiring) 등 어떤 계층에 대해서든 댓글로 더 자세히 논의할 준비가 되어 있습니다.
AI 자동 생성 콘텐츠
본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기