Let's Talk

Feel free to reach out. I'll get back to you as soon as possible.

Study18 min

AWS Project5 - Intelligent Document Analysis Engine

AWS+Terraform로 구축한 RAG 기반 지능형 문서 분석 엔진에 대해 설명합니다.

AWS Project5 - Intelligent Document Analysis Engine

Project 5: 지능형 문서 분석 엔진 (RAG 기반)

Terraform으로 구축한 RAG(Retrieval-Augmented Generation) 기반 지능형 문서 분석 엔진입니다. PDF 파일을 S3에 업로드하면 Lambda(Ingest)가 자동으로 트리거되어 pypdf로 텍스트를 추출하고, 500 단어 단위의 청크로 분할한 뒤 Amazon Bedrock Titan Embeddings를 통해 1024차원 벡터로 변환하여 OpenSearch kNN 인덱스에 저장합니다. 문서 메타데이터(상태, 청크 수, 원본 키)는 DynamoDB에 별도 기록됩니다.

사용자가 자연어로 질문을 보내면 API Gateway → Lambda(Query) 경로로 요청이 전달됩니다. Lambda는 질문을 동일하게 Titan으로 벡터화하고, OpenSearch에서 코사인 유사도 기반 kNN 상위 5개 청크를 검색한 뒤 해당 내용을 컨텍스트로 Claude에 전달합니다. Claude는 훈련 지식이 아닌 실제 문서 내용을 근거로 답변을 생성하고, 출처(source)와 사용된 청크 수를 함께 반환합니다. 이로써 환각(hallucination) 없이 문서에 기반한 정확한 질의응답이 가능합니다.


아키텍처 (RAG 패턴)

━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
  [문서 수집 흐름 (Document Ingest)]
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

  PDF 파일


  S3 (documents 버킷)
     │  S3 Event Notification

  Lambda (Ingest)
  ┌──────────────────────────────────────────────┐
  │ 1. pypdf 텍스트 추출                          │
  │ 2. 500 단어 단위 청크 분할                    │
  │ 3. Bedrock Titan Embeddings 호출             │
  │    (각 청크 → 1024-dim 벡터)                 │
  │ 4. OpenSearch PUT (kNN 인덱스)               │
  │ 5. DynamoDB 메타데이터 기록                   │
  └──────────────────────────────────────────────┘
     │                      │
  OpenSearch             DynamoDB
  (벡터 저장)            (상태·청크 수·출처)


━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
  [질의응답 흐름 (Query Flow)]
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

  사용자 질문


  API Gateway (HTTP)


  Lambda (Query)
  ┌──────────────────────────────────────────────┐
  │ 1. Titan Embeddings로 질문 벡터화            │
  │ 2. OpenSearch kNN 검색 (상위 5개 청크)        │
  │ 3. Bedrock Claude에 컨텍스트+질문 전달        │
  │ 4. 답변 + 출처 + 사용 청크 수 반환           │
  └──────────────────────────────────────────────┘


  { answer, sources, chunks_used, question }

주요 구성 요소

리소스 설명 비용
S3 (문서 버킷) PDF 원본 파일을 저장하는 비공개 버킷. 업로드 이벤트가 Lambda(Ingest)를 자동 트리거 프리 티어: 5GB / 월
Lambda (Ingest) S3 이벤트 수신 → pypdf 텍스트 추출 → 청크 분할 → Titan 임베딩 → OpenSearch·DynamoDB 저장 프리 티어: 월 1,000,000 요청
Lambda (Query) API Gateway 요청 수신 → 질문 벡터화 → OpenSearch kNN 검색 → Claude 답변 생성 → 응답 반환 프리 티어: 월 1,000,000 요청
OpenSearch (t3.small.search) 1024-dim knn_vector 필드를 포함한 documents 인덱스로 벡터 유사도 검색 수행. ⚠️ 프리 티어 없음 — ~$0.036/hr ≈ ~$0.86/day 즉시 삭제 필수
DynamoDB 문서별 doc_id, status, chunk_count, source_key 메타데이터 저장 프리 티어: 25GB / 월
API Gateway (HTTP) /query 엔드포인트로 외부 질문 요청을 Lambda(Query)에 프록시 전달 프리 티어: 월 1,000,000 호출
Bedrock Titan Embeddings amazon.titan-embed-text-v2:0 — 텍스트를 1024-dim 벡터로 변환. 서버리스, 첫 호출 시 자동 활성화 ~$0.0001 / 1K 토큰
Bedrock Claude Haiku 4.5 anthropic.claude-haiku-4-5-20251001-v1:0 — 검색된 청크를 근거로 답변 생성. AWS Marketplace 구독 필요 ~$1/$5 per 1M 입력/출력 토큰
SNS 처리 완료·오류 알림 이메일 발송 프리 티어: 월 1,000건 이메일
CloudWatch Lambda 실행 로그, 수집 파이프라인 모니터링 프리 티어: 5GB 로그 수집

사전 준비

IAM 사용자 생성

AWS CLI와 Terraform이 AWS 리소스를 생성할 수 있도록 프로그래밍 방식 액세스 권한을 가진 IAM 사용자가 필요합니다.

1단계: IAM 사용자 생성

AWS 콘솔에서 IAM → 사용자 → 사용자 생성으로 이동합니다. 사용자 이름(예: terraform-admin)을 입력하고, AWS Management Console 액세스는 체크하지 않습니다 (CLI 전용 사용자이므로 콘솔 로그인 권한 불필요).

2단계: AdministratorAccess 권한 부여

권한 설정 단계에서 직접 정책 연결을 선택하고 AdministratorAccess 정책을 검색하여 체크합니다.

⚠️ AdministratorAccess는 실습/테스트용 편의 설정입니다. 실제 운영 환경에서는 최소 권한 원칙에 따라 필요한 서비스만 허용하는 커스텀 정책을 사용하세요.

3단계: 액세스 키(Access Key) 발급 및 저장

사용자 생성 후 해당 사용자 → 보안 자격 증명 → 액세스 키 만들기로 이동합니다. 사용 사례로 **Command Line Interface(CLI)**를 선택합니다. 발급된 액세스 키 ID시크릿 액세스 키는 이 화면에서만 확인 가능하므로, CSV를 다운로드하거나 별도로 메모해 두세요. 화면을 닫으면 시크릿 키는 다시 볼 수 없습니다.


AWS CLI v2 설치

# Linux / macOS의 경우
brew install awscli
aws --version
# Windows의 경우
winget install Amazon.AWSCLI
aws --version

Terraform 설치

# Linux / macOS의 경우

# 1. HashiCorp 저장소 추가
brew tap hashicorp/tap

# 2. Terraform 설치
brew install hashicorp/tap/terraform
terraform --version
# Windows의 경우
winget install Hashicorp.Terraform
terraform --version

설치 후 주의사항

  • Windows winget 약관 동의: winget 명령어를 처음 사용하면 중간에 ‘소스 약관에 동의하십니까?’ 라는 질문(Y/N)이 뜰 수 있습니다. Y를 입력하고 엔터를 누르면 계속 진행됩니다.

  • 버전 확인 에러 발생 시: 설치가 완료된 직후 aws --version이나 terraform --version을 입력했을 때 *‘명령어를 찾을 수 없다’*고 나온다면, 터미널(또는 PowerShell) 창을 완전히 닫고 새로 열어 다시 입력해 보세요. 환경 변수가 새로고침되어 정상 동작합니다.

  • VSCode 사용 시: 터미널 탭만 닫는 것이 아니라 VSCode 창 전체를 완전히 껐다가 다시 켜야 합니다.


Bedrock 모델 액세스 확인

이 프로젝트는 두 가지 Bedrock 모델을 사용합니다. 배포 전에 반드시 아래 내용을 확인하세요.

임베딩 모델 — amazon.titan-embed-text-v2:0

서버리스 방식으로 제공되며, 첫 번째 InvokeModel 호출 시 자동으로 활성화됩니다. 별도의 콘솔 설정이 필요 없습니다.

생성 모델 — anthropic.claude-haiku-4-5-20251001-v1:0

Claude 4.5 이후 모델은 AWS Marketplace 구독이 필요합니다. 구독이 없으면 AccessDeniedException: subscription required 오류가 발생합니다.

구독 방법:

  1. AWS 콘솔 → Amazon BedrockModel catalog로 이동합니다.
  2. Claude Haiku 4.5를 검색하여 Subscribe 또는 Request access 버튼을 클릭합니다.
  3. 구독에는 admin 권한 (aws-marketplace:Subscribe)이 필요합니다. Lambda IAM 역할은 자체 구독을 할 수 없습니다.

p5-bedrock-model-access

구독이 완료되기 전에는 아래 명령으로 모델이 실제로 호출 가능한지 미리 검증하세요.

모델 호출 검증 (PowerShell):

# 임시 요청 파일 생성
@'
{"anthropic_version":"bedrock-2023-05-31","max_tokens":20,"messages":[{"role":"user","content":"Say OK"}]}
'@ | Out-File -Encoding utf8 t.json

# 모델 호출 테스트
aws bedrock-runtime invoke-model `
  --model-id anthropic.claude-haiku-4-5-20251001-v1:0 `
  --body fileb://t.json `
  --content-type application/json `
  --region us-east-1 out.json

# 응답 확인
Get-Content out.json

out.json"content":[{"type":"text","text":"OK"}] 형태의 응답이 오면 정상입니다. 오류가 발생하면 구독이 완료될 때까지 기다리거나, us.anthropic.claude-opus-4-5-20251101-v1:0으로 폴백하세요.

계정 최초 활성화 확인: OpenSearch와 Bedrock을 포함한 모든 프리미엄 서비스는 AWS 계정이 완전히 활성화된 상태여야 사용할 수 있습니다. 결제 수단 등록, 본인 확인, 지원 플랜 선택이 모두 완료되어야 합니다. AWS 콘솔 → Account에서 확인하세요. 활성화는 최대 24시간이 소요될 수 있습니다.


배포 가이드

Step 0. AWS CLI 자격증명 등록

aws configure
# AWS Access Key ID:     <발급받은 Access Key ID>
# AWS Secret Access Key: <발급받은 Secret Access Key>
# Default region name:   us-east-1
# Default output format: json

연결 확인:

aws sts get-caller-identity

정상이면 Account, UserId, Arn 정보가 출력됩니다.


Step 1. variables.tf 수정

project5-document-engine/variables.tf 파일을 열어 아래 두 변수를 수정합니다.

변수 설명 예시
suffix 리소스 이름 뒤에 붙는 고유 식별자. S3 버킷 이름은 전 세계에서 유일해야 하므로 이름+날짜 조합 권장 yourname-20250527
alert_email SNS 알림을 받을 이메일 주소 your@email.com

Step 2. 인프라 배포

프로젝트 폴더로 이동 후 순서대로 실행합니다.

cd project5-document-engine
terraform init
terraform plan
terraform apply

OpenSearch 프로비저닝 시간: terraform apply는 OpenSearch 도메인 생성으로 인해 10~15분 소요됩니다. 완료될 때까지 기다린 후 다음 단계로 진행하세요. terraform apply는 Ingest Lambda에 필요한 pypdf 패키지도 자동으로 번들링합니다.

p5-terraform-apply-12m50s

배포가 완료되면 터미널에 Outputs이 출력됩니다. 아래 값들을 메모해 두세요.

documents_bucket      = "p5-doc-engine-documents-<suffix>"
opensearch_endpoint   = "https://<opensearch-endpoint>"
query_api_endpoint    = "https://<api-id>.execute-api.us-east-1.amazonaws.com/v1/query"

Step 3. OpenSearch 인덱스 생성 (PDF 업로드 전 필수)

⚠️ 반드시 PDF 업로드(Step 4) 이전에 이 단계를 완료하세요.

만약 인덱스를 생성하지 않은 상태에서 PDF를 먼저 업로드하면, Lambda(Ingest)가 OpenSearch에 첫 번째 청크를 저장할 때 인덱스가 동적 매핑(dynamic mapping)으로 자동 생성됩니다. 이 경우 embedding 필드가 knn_vector 타입이 아닌 일반 float 배열로 매핑되어, 이후 모든 kNN 질의가 HTTP 400 오류로 실패합니다. 이 상황이 발생했다면 인덱스를 삭제하고 재생성한 뒤 PDF를 다시 업로드해야 합니다.

Windows (PowerShell):

Windows 주의: curl 은 PowerShell에서 Invoke-WebRequest의 별칭입니다. --aws-sigv4를 지원하지 않으므로 반드시 curl.exe를 사용하세요.

cd project5-document-engine
$ENDPOINT = terraform output -raw opensearch_endpoint
$KEY = aws configure get aws_access_key_id
$SECRET = aws configure get aws_secret_access_key

curl.exe -X PUT "$ENDPOINT/documents" `
  --aws-sigv4 "aws:amz:us-east-1:es" --user "${KEY}:${SECRET}" `
  -H "Content-Type: application/json" -d "@index-mapping.json"

Linux / macOS:

eval "$(terraform output -raw step1_create_index)"

예상 응답:

{"acknowledged": true, "shards_acknowledged": true, "index": "documents"}

p5-create-the-opensearch-index

p5-create-the-opensearch-vector-index


Step 4. 샘플 PDF 생성 및 업로드

테스트용 샘플 PDF를 생성하고 S3에 업로드합니다. 업로드 직후 CloudWatch 로그를 실시간으로 확인하여 Ingest Lambda가 정상 동작하는지 검증합니다.

Windows (PowerShell):

cd sample_docs
pip install reportlab
python create_sample_pdf.py
cd ..

$BUCKET = terraform output -raw documents_bucket
aws s3 cp sample_docs\sample.pdf s3://$BUCKET/sample.pdf
aws logs tail /aws/lambda/p5-doc-engine-ingest --follow --region us-east-1

Linux / macOS:

cd sample_docs
pip install reportlab
python3 create_sample_pdf.py
cd ..

BUCKET=$(terraform output -raw documents_bucket)
aws s3 cp sample_docs/sample.pdf s3://$BUCKET/sample.pdf
aws logs tail /aws/lambda/p5-doc-engine-ingest --follow --region us-east-1

예상 로그 출력:

[Ingest] 처리 시작: s3://p5-doc-engine-documents-<suffix>/sample.pdf → doc_id=xxxxxxxx
[Ingest] 텍스트 추출 완료: NNN자
[Ingest] 청크 분할 완료: N개
[Ingest] ✅ 완료: N개 청크 색인

p5-upload-pdf-and-watch-cloudwatch-logs


Step 5. DynamoDB + OpenSearch 검증

처리가 완료된 문서의 메타데이터를 DynamoDB에서 조회하고, OpenSearch 인덱스에 청크가 실제로 저장되었는지 확인합니다.

DynamoDB 상태 확인 (PowerShell):

aws dynamodb scan `
  --table-name p5-doc-engine-documents `
  --region us-east-1 `
  --query "Items[*].{status:status.S, chunks:chunk_count.N, source:source_key.S}" `
  --output table

status=completed이고 chunks > 0인 행이 출력되면 정상입니다.

OpenSearch 문서 수 확인 (PowerShell):

$ENDPOINT = terraform output -raw opensearch_endpoint
$KEY = aws configure get aws_access_key_id
$SECRET = aws configure get aws_secret_access_key

curl.exe "$ENDPOINT/documents/_count" `
  --aws-sigv4 "aws:amz:us-east-1:es" --user "${KEY}:${SECRET}"

{"count": N, ...} 형태의 응답에서 N > 0이면 벡터 인덱싱이 성공한 것입니다.

p5-verify-dynamodb-and-opensearch


테스트 시나리오 — RAG 질의응답

terraform output -raw query_api_endpoint 로 API 엔드포인트를 확인하고, 아래 명령에서 <api-id> 자리에 실제 값을 넣어 사용합니다.

① 기본 질의

$API = terraform output -raw query_api_endpoint
Invoke-RestMethod -Uri $API -Method POST -ContentType "application/json" `
  -Body '{"question": "What was the Q4 revenue?"}'

예상 응답:

{
  "answer": "According to the document, the Q4 revenue reached $2,000,000, representing a 35% year-over-year growth compared to Q4 2023...\n\nSources: sample.pdf",
  "sources": ["sample.pdf"],
  "chunks_used": 5,
  "question": "What was the Q4 revenue?"
}

sources 배열에 원본 파일명이 포함되고, chunks_used가 0보다 크면 RAG 파이프라인이 정상 동작하는 것입니다.


② 의미 기반 검색 (다른 표현, 동일 내용)

RAG의 핵심 기능인 의미 유사도(semantic similarity) 검색을 검증합니다. 문서에 없는 표현으로 질문해도 관련 청크를 올바르게 검색해야 합니다.

Invoke-RestMethod -Uri $API -Method POST -ContentType "application/json" `
  -Body '{"question": "How much did the cloud migration save?"}'

문서에 “cloud migration”이라는 표현이 없더라도 AWS 관련 청크가 검색되어 Claude가 답변을 생성하면, 벡터 유사도 검색이 정상 동작하는 것입니다.

p5-query-test-success

p5-test-query-api-and-delete-pdf


⚠️ 비용 관리

서비스별 비용 현황

서비스 프리 티어 단가 비고
OpenSearch (t3.small) 없음 ~$0.036/hr ≈ ~$0.86/day ⚠️ 주요 비용 발생원. 테스트 후 즉시 삭제 필수
Lambda 월 1,000,000 요청 $0.20 / 백만 요청 (초과 시) 테스트 규모에서 무료
S3 5GB / 월 $0.023 / GB (초과 시) 테스트 규모에서 무료
DynamoDB 25GB / 월 $0.25 / WCU (초과 시) 테스트 규모에서 무료
API Gateway 월 1,000,000 호출 (12개월) $1.00 / 백만 호출 (초과 시) 테스트 규모에서 무료
Bedrock Titan Embeddings 없음 ~$0.0001 / 1K 토큰 테스트 규모에서 극미량
Bedrock Claude Haiku 4.5 없음 ~$1/$5 per 1M 입력/출력 토큰 테스트 규모에서 극미량
SNS 월 1,000건 이메일 $2.00 / 100,000건 (초과 시) 테스트 규모에서 무료
CloudWatch 5GB 로그 수집 $0.50 / GB (초과 시) 테스트 규모에서 무료

⚠️ OpenSearch가 이 프로젝트의 유일한 실질적 비용입니다. t3.small 인스턴스는 프리 티어가 없으며 가동 중에는 지속적으로 비용이 청구됩니다. 테스트를 마친 즉시 terraform destroy를 실행하세요. 하룻밤만 방치해도 약 $0.86이 청구됩니다.


What is RAG?

RAG(Retrieval-Augmented Generation)는 AI가 질문에 답할 때 학습 데이터에만 의존하지 않고 실제 문서에서 근거를 찾아 답변을 생성하는 패턴입니다.

일반 AI:  학습 지식만으로 답변 → 사실과 다른 내용을 그럴듯하게 생성하는 환각(hallucination) 위험
RAG:      실제 문서 검색 → 검색된 내용을 근거로 답변 생성 → 높은 정확도 + 출처 제공 가능

P5가 RAG를 구현하는 방식:

  1. 문서 임베딩: PDF 텍스트를 청크로 분할하고, 각 청크의 의미를 1024차원 숫자 벡터로 변환하여 OpenSearch에 저장합니다.
  2. 질문 임베딩: 사용자 질문도 동일한 방식으로 벡터로 변환합니다.
  3. 유사도 검색: 질문 벡터와 가장 유사한 청크를 OpenSearch kNN 검색으로 상위 5개 추출합니다.
  4. 근거 기반 답변 생성: 추출된 청크를 컨텍스트로 Claude에 전달하여, 문서 내용을 근거로 한 답변을 생성합니다.

덕분에 “Q4 revenue”와 “fourth quarter earnings”처럼 표현이 달라도 의미가 유사하면 올바른 청크가 검색됩니다. Claude는 훈련 지식이 아닌 실제 문서를 근거로 답변하므로 환각이 억제됩니다.


트러블슈팅 노트

1. kNN 질의 HTTP 400 오류 — embedding 필드 타입 충돌

증상: Query API가 500 Internal Server Error를 반환하고, CloudWatch 로그에 HTTP Error 400: Bad Request (OpenSearch kNN 검색 거부)가 찍힘.

원인: OpenSearch 인덱스를 생성하기 전에 PDF를 먼저 업로드했습니다. Lambda(Ingest)가 첫 번째 청크를 저장하면서 인덱스가 동적 매핑으로 자동 생성되고, 이때 embedding 필드가 knn_vector가 아닌 일반 float 배열로 매핑됩니다. knn_vector 타입이 아니면 kNN 검색 자체가 불가능합니다.

해결: 기존 인덱스를 삭제하고 올바른 매핑으로 재생성한 뒤, PDF를 다시 업로드합니다.

$ENDPOINT = terraform output -raw opensearch_endpoint
$KEY = aws configure get aws_access_key_id
$SECRET = aws configure get aws_secret_access_key

# 1. 잘못 생성된 인덱스 삭제
curl.exe -X DELETE "$ENDPOINT/documents" `
  --aws-sigv4 "aws:amz:us-east-1:es" --user "${KEY}:${SECRET}"

# 2. 올바른 knn_vector 매핑으로 인덱스 재생성
curl.exe -X PUT "$ENDPOINT/documents" `
  --aws-sigv4 "aws:amz:us-east-1:es" --user "${KEY}:${SECRET}" `
  -H "Content-Type: application/json" -d "@index-mapping.json"

# 3. PDF 재업로드
$BUCKET = terraform output -raw documents_bucket
aws s3 cp sample_docs\sample.pdf s3://$BUCKET/sample.pdf

2. Bedrock AccessDeniedException — 두 가지 원인

증상: Lambda(Query)가 Claude 호출 시 AccessDeniedException 오류를 반환하고 답변이 생성되지 않음.

원인 및 해결:

  • 원인 1 — IAM 정책 누락: Lambda IAM 역할이 bedrock:InvokeModel을 허용하는 ARN 패턴에 inference-profile/ 경로가 포함되어 있지 않습니다. Claude 4.5 이후 모델은 베어 foundation-model/ ARN으로 직접 호출할 수 없으며, 반드시 inference-profile/ 형태의 ARN을 사용해야 합니다. iam.tf에서 anthropic.claude* glob이 두 ARN 패턴(inference-profile/foundation-model/)을 모두 포함하는지 확인하세요.

  • 원인 2 — Marketplace 구독 미완료: ...required AWS Marketplace actions (aws-marketplace:Subscribe) 오류는 Claude Haiku 4.5 모델의 구독이 완료되지 않은 것입니다. 관리자 권한으로 AWS Marketplace에서 Claude Haiku 4.5를 구독하거나, 구독이 완료될 때까지 variables.tfbedrock_model_idus.anthropic.claude-opus-4-5-20251101-v1:0으로 임시 변경 후 terraform apply를 재실행하세요.


3. 질의 응답이 영어로 출력됨

증상: 한국어로 질문했는데 Claude 답변이 영어로 반환됨.

원인 및 해결: 정상 동작입니다. Lambda(Query)의 RAG 프롬프트가 영어로 작성되어 있으므로 Claude는 기본적으로 영어로 응답합니다. 한국어 응답이 필요하면 lambda/query/index.py의 프롬프트 템플릿에 “Please respond in Korean.” 또는 “한국어로 답변하세요.“를 추가하세요.


4. OpenSearch 도메인 이미 존재 오류 — terraform apply 실패

증상: terraform apply 실행 시 OpenSearch 도메인이 이미 존재한다는 오류가 발생함.

원인 및 해결: 이전 terraform destroy가 아직 완료되지 않았습니다. OpenSearch 도메인 삭제는 비동기적으로 처리되며 5~10분이 소요됩니다. AWS 콘솔 → OpenSearch → Domains에서 도메인이 완전히 사라진 것을 확인한 뒤 terraform apply를 재실행하세요.

AWS Console → OpenSearch → Domains
https://us-east-1.console.aws.amazon.com/esv3/home?region=us-east-1

5. Textract 지역 오류 (구 버전 참고)

초기 버전에서는 문서 텍스트 추출에 AWS Textract를 사용했으나, Textract는 일부 리전에서 활성화되지 않아 InvalidS3ObjectException 또는 UnsupportedDocumentException이 발생하는 문제가 있었습니다. 현재 버전에서는 Textract를 제거하고 pypdf로 대체하여 별도의 리전 활성화 없이 Lambda 내에서 직접 텍스트를 추출합니다.


6. Titan Embeddings v2 차원 불일치 오류 (구 버전 참고)

초기 버전에서 Titan Text Embeddings v1(768-dim)과 v2(1024-dim)를 혼용하면 OpenSearch 인덱스 매핑과 실제 벡터 차원이 맞지 않아 저장 또는 검색이 실패했습니다. 현재 버전은 amazon.titan-embed-text-v2:0을 사용하고 인덱스 매핑을 1024차원으로 고정하여 이 문제를 해결했습니다.


인프라 삭제

⚠️ 긴급: OpenSearch가 ~$0.86/day 비용을 지속 발생시킵니다. 테스트 완료 즉시 삭제하세요.

S3 버킷에 오브젝트가 남아 있으면 terraform destroy가 실패합니다. 반드시 버킷을 먼저 비운 뒤 삭제를 진행하세요.

Windows (PowerShell):

# 1. S3 버킷 비우기 (오브젝트가 남아있으면 terraform destroy 실패)
$BUCKET = terraform output -raw documents_bucket
aws s3 rm s3://$BUCKET --recursive

# 2. Terraform 리소스 전체 삭제
terraform destroy

Linux / macOS:

BUCKET=$(terraform output -raw documents_bucket)
aws s3 rm s3://$BUCKET --recursive
terraform destroy

terraform destroy 완료 후 OpenSearch 도메인이 실제로 삭제되었는지 콘솔에서 확인하세요. 도메인 삭제는 비동기 처리로 수 분이 소요됩니다.

AWS Console → OpenSearch → Domains → "p5-doc-engine" 항목이 사라졌는지 확인
https://us-east-1.console.aws.amazon.com/esv3/home?region=us-east-1

Lambda 함수(Ingest·Query), OpenSearch 도메인, DynamoDB 테이블, API Gateway, S3 버킷, SNS 토픽, IAM 역할, CloudWatch 로그 그룹 등 Terraform이 생성한 모든 리소스가 삭제됩니다.


기술 스택

Terraform · AWS Lambda · AWS S3 · Amazon OpenSearch Service · AWS DynamoDB · AWS API Gateway · Amazon Bedrock · Titan Text Embeddings v2 · Claude Haiku 4.5 · AWS SNS · AWS CloudWatch · AWS IAM · Python · pypdf · reportlab