본문으로 건너뛰기

© 2026 Molayo

Dev.to헤드라인2026. 06. 15. 05:27

AWS Textract와 Claude를 활용한 AI 이력서 점수 측정기 구축

요약

AWS Textract와 Claude를 활용하여 이력서와 직무 기술서(JD) 간의 일치도를 분석하는 풀스택 AI 애플리케이션 구축 사례를 소개합니다. PDF 텍스트 추출부터 AI 점수 산정, 데이터 저장까지의 전체 백엔드 파이프라인과 아키텍처를 다룹니다.

핵심 포인트

  • AWS Textract를 사용하여 복잡한 PDF 레이아웃에서 정확하게 텍스트 추출
  • Claude를 활용한 구조화된 프롬프트 기반의 지능적 점수 산정 및 피드백 생성
  • S3 Presigned URL을 통한 효율적인 파일 업로드 방식 구현
  • FastAPI, ECS Fargate, DynamoDB를 활용한 서버리스 아키텍처 구성

내가 만든 것

이력서가 직무 기술서(Job Description)와 얼마나 잘 일치하는지 점수를 매기는 풀스택(Full-stack) AI 애플리케이션입니다. 지원자에게 5개 카테고리에 걸쳐 0~100점 사이의 점수와 구체적인 개선 제안을 제공합니다.

사용자 흐름:

  1. 이력서 PDF 업로드
  2. 직무 기술서 붙여넣기
  3. 상세한 피드백과 함께 AI 기반 일치 점수 받기
  4. 과거 분석 내역 보기

전체 백엔드 파이프라인은 AWS에서 실행됩니다. Textract가 이력서 텍스트를 추출하고, Claude AI가 지능적인 점수 산정을 수행하며, 결과는 DynamoDB에 저장됩니다.

아키텍처

Browser
  │
  ▼
...

핵심적인 아키텍처 결정 사항은 /api/v1/* 경로를 CloudFront를 통해 ALB로 라우팅하는 것이었습니다. 이는 프론트엔드가 정적 파일과 API 호출 모두에 대해 단일 HTTPS 엔드포인트를 사용함을 의미하며, 혼합 콘텐츠(Mixed content) 문제나 CORS 문제를 방지합니다.

기술 스택

계층기술
프론트엔드 (Frontend)React 18 + TypeScript + Vite + TailwindCSS
...

사용된 AWS 서비스

서비스용도
ECS FargateFastAPI를 위한 서버리스 컨테이너 호스팅
...

분석 파이프라인

사용자가 이력서와 직무 기술서를 제출하면 다음과 같은 과정이 진행됩니다:

1단계 — Presigned URL을 통한 PDF 업로드

프론트엔드는 백엔드에 Presigned S3 URL을 요청한 다음, 브라우저에서 S3로 PDF를 직접 업로드합니다. 이를 통해 바이너리 파일을 FastAPI 서버를 거쳐 라우팅하는 것을 방지할 수 있습니다.

# POST /api/v1/upload
def get_presigned_url(file_name: str, content_type: str) -> dict:
    s3_key = f"uploads/{uuid4()}/{file_name}"
...

2단계 — AWS Textract를 이용한 텍스트 추출

PDF가 S3에 저장되면, Textract가 모든 텍스트를 추출합니다. Textract는 다단 이력서, 표, 헤더와 같은 복잡한 PDF 레이아웃을 단순한 PDF 파서(Parser)보다 훨씬 더 잘 처리합니다.

# textract_service.py
def extract_text_from_s3(s3_key: str) -> str:
    response = textract_client.detect_document_text(
...

참고: detect_document_text는 동기(synchronous) 방식으로 작동하며 표준적인 이력서에 적합합니다. 만약 10페이지 이상의 문서를 지원해야 한다면, start_document_analysis (비동기 (asynchronous) 방식)로 전환하세요. 이 방식은 타임아웃 없이 더 큰 파일을 처리할 수 있습니다.

Step 3 — Claude를 활용한 AI 점수 측정 (AI Scoring)

추출된 이력서 텍스트와 직무 기술서(job description)는 5개 카테고리에 대한 점수와 개선 제안을 요청하는 구조화된 프롬프트(structured prompt)와 함께 Claude로 전송됩니다.

5가지 점수 측정 카테고리:

  1. 기술 일치도 (Skills Match) — 기술적 역량이 얼마나 잘 부합하는지
  2. 경력 수준 (Experience Level) — 연차 및 숙련도 일치 여부
  3. 학력 (Education) — 학위 및 전공 관련성
  4. 키워드 (Keywords) — ATS(채용 관리 시스템)에 중요한 키워드 포함 여부
  5. ATS 포맷팅 (ATS Formatting) — 이력서 구조 및 서식

Claude는 구조화된 JSON 응답을 반환합니다. LLM의 비결정론적(non-deterministic) 출력이 프론트엔드를 망가뜨리지 않도록, 저는 DynamoDB에 저장하기 전 FastAPI 레이어에서 Pydantic을 사용하여 JSON 구조를 검증했습니다. 만약 Claude가 예상치 못한 형식을 반환하면, 데이터를 조용히 손상시키는 대신 명확한 에러와 함께 요청이 즉시 실패(fail fast)하도록 처리했습니다.

{
  "overall_score": 78,
  "categories": [
...

Step 4 — DynamoDB에 저장

결과는 UUID를 파티션 키(partition key)로 하여 저장됩니다. DynamoDB의 PAY_PER_REQUEST 과금 방식은 용량 계획(capacity planning)이 필요 없으며, 사용하지 않을 때는 비용이 발생하지 않습니다.

# dynamodb_service.py
def save_result(result: AnalysisResult) -> None:
    table.put_item(Item={
...

API 엔드포인트 (API Endpoints)

MethodEndpointDescription
GET/health상태 확인 (Health check)
.........

AWS CDK를 이용한 인프라 구축

모든 AWS 리소스는 CDK Python을 사용하여 코드로 정의되었으며, 4개의 스택(stack)으로 분리되어 있습니다.

StorageStack

서버 측 암호화(server-side encryption)가 적용된 S3 버킷과 업로드된 PDF에 대한 30일 수명 주기 만료(lifecycle expiry) 설정이 포함됩니다.

DatabaseStack

PAY_PER_REQUEST 과금 방식의 DynamoDB 테이블로, 별도의 용량 계획이 필요하지 않습니다.

BackendStack

가장 복잡한 스택입니다. 2개의 가용 영역 (AZ)을 가진 VPC, 1개의 NAT Gateway (ECS 태스크가 Anthropic API에 도달하기 위해 외부 인터넷 연결이 필요함), ECS Fargate 서비스, 그리고 Application Load Balancer (ALB)로 구성됩니다.

핵심 결정 사항: 백엔드에 Lambda 대신 ECS Fargate를 사용했습니다. Claude AI의 응답은 상세한 분석을 위해 10~30초가 소요될 수 있습니다. Lambda는 15분의 제한 시간이 있지만, 콜드 스타트 (Cold Start) 문제와 스트리밍 응답 (Streaming responses)의 복잡성 때문에 이 워크로드에는 Fargate가 더 깔끔한 선택이었습니다. Lambda의 잠재적인 타임아웃 및 콜드 스타트 문제에 비해, 장시간 실행되는 LLM 추론 (Inference) 과정에서 Fargate가 보여주는 안정성은 신뢰성을 위한 의도적인 선택이었습니다.

# backend_stack.py (단순화 버전)
fargate_service = ecs_patterns.ApplicationLoadBalancedFargateService(
    self, "BackendService",
...

FrontendStack

S3를 위한 OAC (Origin Access Control)가 적용된 CloudFront, SPA 라우팅 (404 → index.html), 그리고 ALB로 프록시되는 /api/v1/* 동작 (Behaviour)으로 구성됩니다. 캐시 무효화 (Cache invalidation)는 배포 시마다 자동으로 실행됩니다. CloudFront + ALB + S3 패턴은 프로덕션 환경의 SPA를 구축할 때 제가 즐겨 사용하는 방식입니다. 이 방식은 CORS 문제를 완전히 제거하고 에지 (Edge)에서 SSL 종료 (Termination)를 중앙 집중화합니다.

Deploy Pipeline

단일 make deploy 명령어로 모든 과정을 실행합니다:

make deploy
# 1. 백엔드 테스트 실행 (pytest)
# 2. 프론트엔드 테스트 실행 (vitest)
...

테스트가 하나라도 실패하면 배포가 중단되어, 결함이 있는 코드가 AWS에 도달하지 않도록 합니다.

Testing Strategy

Backend — 모든 AWS 서비스를 모킹 (Mock)하기 위해 moto를 사용한 pytest를 활용합니다. 테스트를 위해 실제 AWS 자격 증명이 필요하지 않습니다.

cd backend
pytest                    # 커버리지를 포함한 모든 테스트
pytest tests/services/    # 서비스 레이어만 테스트
...

Frontend — Vitest + Testing Library를 사용합니다.

cd frontend
npm test                  # 모든 테스트
npm run test:coverage     # 커버리지 리포트 포함

Lessons Learned

1. 파일 업로드 시 Presigned URL을 사용하세요
PDF를 FastAPI를 통해 라우팅하면 불필요한 부하와 지연 시간 (Latency)이 발생합니다. Presigned URL을 통해 브라우저에서 S3로 직접 업로드하는 것이 더 빠르고 저렴합니다.

2. 긴 AI 호출을 위해 Lambda 대신 ECS Fargate 사용
Lambda는 빠른 작업에는 적합하지만, AI 점수 측정에는 10~30초가 소요됩니다. Fargate 컨테이너는 항상 Warm 상태(Warm)를 유지하므로 이러한 작업을 자연스럽게 처리할 수 있습니다.

3. 프라이빗 ECS 태스크를 위해 NAT Gateway 필수
프라이빗 서브넷(Private Subnet)에 있는 ECS 태스크가 Anthropic API에 도달하려면 NAT Gateway가 필요합니다. 처음에 이것 없이 시도했을 때는 AI 호출이 아무런 오류 메시지 없이 타임아웃(Timeout)되었습니다.

4. AI 워크로드를 위해 ALB 유휴 제한 시간(Idle Timeout) 연장
ALB의 기본 유휴 제한 시간은 60초입니다. Claude는 상세한 분석을 위해 더 오랜 시간이 걸릴 수 있습니다. 이를 120초로 설정하면 응답 중간에 연결이 끊기는 현상을 방지할 수 있습니다.

5. CloudFront 라우팅으로 CORS 문제 완전 해결
/api/v1/* 경로를 CloudFront를 통해 ALB로 프록시(Proxying)하면 프론트엔드와 API가 동일한 도메인을 공유하게 됩니다. 따라서 CORS 설정이 전혀 필요하지 않습니다.

6. 비밀값 관리를 위한 SSM Parameter Store
API 키를 환경 변수(Environment Variables)나 Docker 이미지에 절대 넣지 마세요. SSM SecureString을 사용하면 컨테이너 시작 시점에 주입되므로, 키가 로그나 CDK 출력에 노출되지 않습니다.

7. 인증(Auth)은 의도적으로 v2로 연기
현재 버전에는 사용자 인증 기능이 없습니다. AWS Cognito를 이용한 인증은 v2에서 계획 중입니다: 사용자 로그인, 사용자별 개인 기록, 그리고 API Gateway의 Cognito Authorizer 도입 등이 포함됩니다. 인증을 미룸으로써 v1의 범위를 작게 유지하고 즉시 배포 가능한 상태로 만들 수 있었습니다.

GitHub

전체 소스 코드는 GitHub에서 확인할 수 있습니다:

👉 github.com/sanjaypatoliya/ai-resume-analyzer

저자 소개

저는 Sanjay Patoliya입니다. 7개의 AWS 자격증을 보유한 AWS 인증 엔지니어로, AWS 상에서 프로덕션 환경에 즉시 적용 가능한 AI 시스템을 구축하고 있습니다.

원문 게시지: sanjaypatoliya.com

AI 자동 생성 콘텐츠

본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.

원문 바로가기
0

댓글

0