sungyup's.

dev_toolkits / Tools / 1.4 reCAPTCHA

1.4reCAPTCHA

인간과 봇을 구분하기 위한 구글의 테스트 시스템

TL;DR

추억의 쪽지 시험

회사에서 운영중인 웹앱의 데이터가 최근 크롤링을 당했다. 관련해서 백엔드에선 서버쪽에 크롤링 방지 대책들을 도입했는데, 프론트엔드 쪽에서도 브라우저를 자동으로 조작해 크롤링하는 방식에 대한 대응을 하기로 했다. 이 과정에서 reCAPTCHA를 도입하게 되어, 관련 내용들을 정리해본다.

reCAPTCHA란?

reCAPTCHA란, 구글이 제공하는 온라인 유저가 인간인지 자동화된 봇인지 구분하기 위한 테스트 시스템이다.

처음부터 re-가 붙은 이름은 아니었고, 원래는 CAPTCHA(Completely Automated Public Turing test to tell Computers and Humans Apart, 컴퓨터와 인간을 구분하기 위한 완전 자동화된 공개 튜링 테스트)라는 이름의 테스트로, 2000년대 초 Carnegie Mellon University의 Luis von Ahn 교수가 만들었으며, 티켓 예매 페이지나 회원가입 폼, 댓글 창의 스팸 방지가 목적이었다. 이 테스트는 난독화된 문자 이미지를 사람이 직접 입력해야하는 형태였다.

captcha
이미지 출처 : #
컴퓨터는 왜곡된 글자를 잘 인식하지 못하나, 사람은 손글씨 등으로 글자에 약간의 변형이 간 정도는 충분히 인식할 수 있다는 것을 활용한 초기의 CAPTCHA.

그러다가 2007년쯤 되자 전세계의 수많은 사람들이 매일 엄청난 수의 CAPTCHA를 풀고 있었고, Luis von Ahn 교수는 이 어마어마한 노동력을 좀 더 유용한 일에 쓰고자 기발한 생각을 한다. 당시 OCR(광학 문자 인식)은 한계가 있어 스캔한 옛날 책이나 문서를 디지털화하는데 어려움을 겪고 있었는데, 이렇게 스캔한 데이터에서 자동 처리에 실패한 단어들의 이미지를 사람에게 풀게 한 것이다. 문제는 두 개의 단어를 짝지어 출제했고, 둘 중 하나는 이미 정답을 데이터화한 단어였기에 이 문제를 맞춘 사람이 제출한 다른 문제의 답도 정답일 확률이 높다고 처리하는 식이었다. reCAPTCHA의 re-는 "reuse"를 줄인 것으로, 인간 판별 테스트를 사회적 기여 활동으로 재활용했다는 의미다.

2009년, 구글은 reCAPTCHA를 인수하고 reCAPTCHA로 개선한 데이터 처리 방식은 실제로 Google Books와 뉴욕타임즈 아카이브의 디지털화에 기여했다. 구글은 나아가 사람의 입력 데이터를 AI 학습, 지도 데이터, 이미지 인식 등에 활용하여 AI 학습을 돕는 데이터 수집 플랫폼으로도 활용했다.

또, 2014년에는 v2를 발표했는데 이는 기존의 단어 입력 대신 "I'm not a robot" 박스를 체크하거나 쪼개둔 이미지에서 특정 사물이 있는 블록만 체크하게 하는 방식이었다. 이 방식은 효과적이긴 했으나 사용자에게 불편함을 초래했다. 2018년에 나온 v3사용자 마우스 이동, 클릭, 스크롤 등의 행동을 분석사이트 내 행동으로 점수를 부여하는 식으로 변경되었다. 페이지 로드, 클릭, 마우스 이동, 입력 속도 등 수백 개의 신호를 수집해 구글의 Machine Learning 모델이 분석해 0 ~ 1의 점수를 반환하고 0.5 이상이면 사람으로 간주하는 방식으로, v3은 사용자의 행동 로그 자체가 CAPTCHA가 되어 사이트의 흐름을 방해하지 않고 보안을 유지할 수 있다고 한다.

그런데 사실, 우리 사이트에선 도입해봤으나 puppeteer로 작성한 간단한 프로그램도 0.9점을 받는걸 확인할 수 있었다. 뭐가 문제인지 찾아보고 고친 후 후기를 작성하려고 한다.

작동 방식

오늘날 CAPTCHA는 reCAPTCHA 외에 다양한 종류가 있고, reCAPTCHA도 글 작성일 현재(25.10.11) v2와 v3가 있어 버전마다 약간 차이가 있지만, 핵심은 같다. 아래는 reCAPTCHA v3 기준이다.

1. 사이트 등록

우선 Google reCAPTCHA Admin Console에 사이트를 등록한다. 등록 시, Site Key라고 불리는 클라이언트 키Secret Key라고 불리는 서버 키가 주어진다.

2. 클라이언트에서 토큰 요청

사용자가 페이지에 접근하면, grecaptcha.execute(site_key, {action})통해 구글의 reCAPTCHA 서버에서 토큰을 발급받는다(g-가 붙는건 구글이라 그렇다). 이 토큰은 "해당 사용자가 reCAPTCHA 검사를 수행했다"는 증거 역할을 하는 것으로, 약 2분 정도 유효시간을 가지는 1회용 토큰이다. 여기서 action은 어떤 목적의 reCAPTCHA였는지 확인하는 단순 문자열 태그다.

tsx
// reCAPTCHA 스크립트 로드 <script src={`https://www.google.com/recaptcha/api.js?render=${SITE_KEY}`}></script> // 토큰 발급 요청 const token = await grecaptcha.execute(`${SITE_KEY}`, { action: 'login' });

3. 클라이언트에서 서버 요청 시 토큰 전달

사용자가 API를 호출할 때, 이 토큰을 함께 헤더나 바디에 넣어 서버로 전송한다.

typescript
await axios.post('/api/login', { username: 'sungyup', password: '비번', }, { headers: { 'X-Recaptcha-Token': token, // 바디의 parameter에 넣어도 된다. }, });

4. 서버에서 Google Verification API로 검증 요청

서버는 이 토큰을 검증하기 위해 siteverify라는 구글의 검증용 API에 앞서 발급 받은 서버의 Secret Key와 함께 POST 요청을 보낸다.

typescript
const response = await fetch('https://www.google.com/recaptcha/api/siteverify', { method: 'POST', headers: { 'Content-Type': 'application/x-www-form-urlencoded' }, body: new URLSearchParams({ secret: process.env.RECAPTCHA_SECRET_KEY, response: recaptchaToken, }), }); const data = await response.json();

5. 구글에서 서버 응답

구글은 아래와 같은 형식의 JSON을 반환한다.

javascript
{ "success": true, // 검증 성공 여부 "score": 0.9, // v3 전용의, 0 ~ 1 사이의 점수로 0에 가까울 수록 봇이다 "action": "login", // 클라이언트에서 전달한 action "challenge_ts": "2025-10-10T08:34:56Z", "hostname": "sungyup.com" // 요청이 발생한 도메인 }

6. 서버의 처리

서버는 이 응답을 받아 data.success일 때, 또는 이와 함께 data.score가 일정 점수를 넘을 때 인간으로 판단하고 정상 처리하는 로직을, 그렇지 않을 때는 차단하거나 추가 검증하는 로직을 실행할 수 있다.