LiteTranslate — 브라우저 확장에서 Bedrock 직접 호출
유튜브 이중자막을 번역하고 단어까지 학습하는 크롬 확장을 개인적으로 만들었습니다. 이런 번역 확장이야 이미 많지만, 쓰다 보면 결국 유료로 넘어가고 원하는 대로 손보지도 못하는 게 아쉬웠습니다. 번역만 가볍게 해주자는 생각이었고 이름도 그러다 보니 자연스럽게 LiteTranslate 가 됐습니다.
번역과 사전, 문법 설명, 요약은 전부 LLM 을 쓰고, LLM 호출은 AWS Bedrock(여러 LLM 을 API 하나로 부르는 AWS 관리형 서비스)으로, 사용자 인증은 AWS Cognito 로 합니다. 그런데 서버를 하나도 두지 않았습니다. 그 사이에 백엔드를 두는 대신 확장이 사용자 브라우저에서 이 AWS 서비스들을 직접 부르는 구조입니다.
서버가 없으니 운영비도 관리 부담도 없어서, 첫 달에 개발·테스트를 몰아쳤는데도 전체 AWS 비용이 615원이었고 그마저 대부분 Bedrock 호출이었습니다.
왜 서버를 안 뒀나
번역기의 흔한 구조는 확장 → 내 백엔드 → LLM 이고, 백엔드가 API 키를 들고 있으면서 인증·과금·rate limit 을 처리합니다.
이걸 두면 서버를 24시간 띄워야 하고, 그만큼 비용이 들고 관리할 것도 생깁니다. 개인·지인용 확장에 그건 과해서 백엔드를 없애고 확장이 직접 AWS 를 부르게 했습니다.
대신 브라우저에서 어떻게 인증할지, AWS API 호출에 필요한 서명을 어떻게 만들지 두 가지를 풀어야 했습니다.
인증 — Cognito Identity Pool로 1시간짜리 임시 키
브라우저에 AWS 영구 키를 넣을 수는 없습니다. 확장 코드는 사용자 손에 있어서 뜯으면 그대로 노출되기 때문입니다. 그래서 Cognito Identity Pool 로 그때그때 짧게 쓰고 버리는 키를 받아 씁니다.
chrome.identity.launchWebAuthFlow 로 Google OIDC 로그인을 해서 id_token 을 받고, 그걸
Cognito 의 GetId / GetCredentialsForIdentity 에 넘기면 1시간짜리 임시 AWS 자격증명이 나오고,
만료되면 같은 절차로 다시 받습니다. 브라우저에 영구 키가 없으니 뜯겨도 나가는 건 1시간짜리 임시
키고, 피해가 그 시간 안으로 끊깁니다.
서명 — AWS SDK 없이 SigV4를 WebCrypto로 직접
AWS API 는 요청마다 SigV4 서명을 요구하고, 보통은 AWS SDK 가 알아서 해 주는 일이라 신경 쓸 일이 없습니다. 다만 SDK 를 확장에 통째로 넣으면 번들이 커집니다.
그래서 서명은 브라우저의 WebCrypto API 로 직접 구현했습니다. SigV4 가 하는 일은 정해진 순서로
요청 요소를 정규화하고 HMAC-SHA256 을 여러 번 이어서 거는 것이라, WebCrypto 의
crypto.subtle.sign('HMAC', ...) 만 있으면 SDK 없이 되고 번들도 그만큼 작아집니다.
이렇게 서명한 요청을 Bedrock Converse API(번역·사전·문법·요약), DynamoDB (단어장·사용량), Polly(발음)로 직접 보냅니다.
사용자 격리 — IAM 조건으로 파티션 키 고정
서버가 없으니 “이 사용자는 자기 데이터만 본다” 를 코드로 지킬 수 없습니다. 확장에서 쿼리 조건으로 걸면 되겠다 싶었지만, 확장 코드는 사용자 손에 있어서 얼마든지 고칠 수 있습니다.
그래서 막는 자리를 확장 밖으로 옮겨서, IAM 정책 조건으로 걸었습니다.
dynamodb:LeadingKeys = ${cognito-identity.amazonaws.com:sub}
DynamoDB 파티션 키가 Cognito 식별자와 일치할 때만 접근을 허용하는 조건이라, 각 사용자의 임시 자격증명은 자기 식별자로 시작하는 행만 읽고 쓸 수 있습니다. 확장 코드를 고쳐서 남의 키를 넣어 봐도 요청 자체를 IAM 이 거부하니, 지키는 쪽이 고칠 수 있는 코드가 아니라 AWS 가 됩니다.
전체 그림
[브라우저 확장]
content.js ──┐ ┌─> Bedrock (번역·사전·문법·요약)
youtube.js ──┼─> background.js ───┼─> DynamoDB (단어장·사용량, 계정별 격리)
popup.js ──┘ (서비스워커) ├─> Polly (발음)
└─> Cognito (로그인 → 임시 자격증명)
background.js(서비스 워커)가 인증·서명·모든 모델 호출·캐시·쿼터를 담당하는 중심이고, 나머지는
페이지에서 텍스트를 모아 넘기고 돌아온 결과를 화면에 꽂는 역할만 합니다.
서버를 없앤다는 건 책임을 AWS로 옮기는 것
서버를 없애는 건 “백엔드 코드를 없애는 것” 이 아니라 책임을 AWS 서비스로 옮기는 것이었습니다. 인증은 Cognito, 권한 격리는 IAM, 과금 통제는 사용량 계량 + 예산 알람으로. 각각을 관리형 서비스에 맡기니 직접 운영할 서버가 남지 않았습니다.
개인이나 지인이 쓰는 규모라 가능한 선택이기도 해서, 사용자가 수만 명으로 늘거나 서버 사이드에서만 할 수 있는 로직이 생기면 백엔드가 다시 필요해집니다. 다만 작게 시작하는 도구라면 서버를 하나도 안 두는 쪽이 관리 부담과 비용 양쪽에서 낫습니다.
댓글 0