LiteTranslate — 브라우저 확장에서 Bedrock 직접 호출

만든 것5분조회

유튜브 이중자막을 번역하고 단어까지 학습하는 크롬 확장을 개인적으로 만들었습니다. 이런 번역 확장이야 이미 많지만, 쓰다 보면 결국 유료로 넘어가고 원하는 대로 손보지도 못하는 게 아쉬워서 직접 만들었습니다. 무겁지 않게 번역만 가볍게 해주자는 생각이었고, 이름도 그러다 보니 자연스럽게 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 을 씁니다. 흐름은 이렇습니다.

Google 로그인 id_token 발급 Cognito Identity Pool GetCredentialsForIdentity 임시 AWS 자격증명 1시간 유효
확장은 영구 키를 안 들고, 매번 1시간짜리 임시 키를 받습니다

chrome.identity.launchWebAuthFlow 로 Google OIDC 로그인을 해서 id_token 을 받고, 그걸 Cognito 의 GetId / GetCredentialsForIdentity 에 넘기면 1시간짜리 임시 AWS 자격증명이 나옵니다. 만료되면 다시 받습니다. 브라우저에 영구 키가 없으니 뜯겨도 잃을 게 없습니다.

서명 — AWS SDK 없이 SigV4를 WebCrypto로 직접

AWS API 는 요청마다 SigV4 서명을 요구합니다. 보통 AWS SDK 가 알아서 해주는데, SDK 를 확장에 넣으면 번들이 커집니다.

그래서 SigV4 서명을 브라우저의 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 개가 관리 부담과 비용 양쪽에서 확실히 낫습니다.

  1. 불러오는 중