개요
현재 우리 회사의 로그인 페이지는 왜곡된 문자열(v1)을 입력해야 로그인할 수 있는 기존 CAPTCHA 방식을 사용하고 있다. 이 방식은 일정 수준의 봇 방지 효과가 있지만, 사용자가 매번 직접 문자를 입력해야 하는 불편함이 있다.
이 불편함을 개선하기 위해 실제 서비스에 적용하기 전에 로컬 환경에서 Google reCAPTCHAT v3를 테스트 해보았다. reCAPTCHAT v3는 사용자의 입력 없이도 백그라운드에서 점수를 계산해 판단하기 때문에, 보안을 유지하면서도 사용자 경험을 해치지지 않는 장점이 있다.
reCAPTCHA란?
reCaptcha는 구글이 제공하는 사람과 bot을 구분하기 위한 보안 서비스이다.
보통 로그인, 회원가입, 댓글 작성 등 bot 공격이 빈번한 곳에서 적용된다.
버전 별로 보면 , 이런식이다.
버전 방식 사용자경험
| v1 | 왜곡된 문자 입력 | 매우 불편 |
| v2 | I’m not a bot 체크박스 / 이미지에서 특정 사물 선택 | 조금 불편 |
| v3 | 자동 판별 (점수 기반) | 방해 없음! + 백엔드에서 판단 |

키 발급
https://www.google.com/recaptcha/admin/create?hl=ko
로그인 - Google 계정
이메일 또는 휴대전화
accounts.google.com
에 접속한 뒤에,

reCAPTCHA 유형을 3으로 선택하고, 도메인으로는 개발 또는 운영의 도메인을 추가한다.
보통 개발은 localhost로 하니까 추가해두면 편하다.
그렇게 발급 받은 사이트 키(Site-Key)와 비밀 키(Private Key)는 따로 저장해도 되지만 설정에서 또 확인할 수 있다.
- 사이트 키: 프론트엔드에서 사용 (외부에 공개됨)
- 비밀 키: 백엔드용에서 사용 (절.대 외부에 노출되면 안 된다.)
백엔드 구현
백엔드에서는 프론트에서 전달 받은 recapcha 토큰을 Google 서버에 검증 요청하여 해당 사용자가 사람인지 봇인지 판별하는 역할을 한다.
즉, 백엔드는
- Google의 검증 API 로 토큰을 전송하고,
- Google이 반환한 응답(JSON)을 확인해,
- 스코어가 기준치 이상인 경우에만 로그인 요청을 허용한다.
1. 비밀키 설정
먼저 비밀 키를 코드에 직접 하드코딩 하지 않고 설정 파일을 통해 관리한다. application.yml에 비밀키를 세팅 한다.
google:
captcha:
secret-key: ...
secret-key는 아까 발급받은 비밀 키를 입력한다. 다시 한번 확인하면 site key는 프론트용, secret key는 백엔드 검증용!
2. response DTO 작성
https://developers.google.com/recaptcha/docs/v3?hl=ko#site_verify_response 를 보면 JSON 형식으로 어떻게 응답하는지 확인 할 수 있다. 이를 매핑하기 위해 Response DTO를 작성한다.
@Getter @Setter
public class RecaptchaResponse {
private boolean success;
private double score;
private String action;
private String challengeTs;
private String hostname;
private List<String> errorCodes;
}
- score: Google이 계산한 사람일 확률 점수 (0.0 ~ 1.0 사이)
- action: 프론트에서 보낸 action 이름
- error-code: 검증 실패 시, 에러 코드 목록
3. request DTO 작성
프론트엔드에서는 로그인 요청 시 사용자 정보와 함께 captchaToken을 백엔드로 전송한다.
@Getter @Setter
public class LoginRequest {
private String username;
private String password;
private String recaptchaToken;
}
4. 서비스 구현
이제 Google의 검증 API로 토큰을 전달하고 응답을 검증하는 로직을 작성한다.
RestTemplate을 이용해 Google 서버로 HTTP POST 요청을 보내고, 응답의 score 값이 기준치 이상인지 판단한다.
@Slf4j
@Service
public class RecaptchaService {
@Value("${google.recaptcha.secret-key}")
private String secretKey;
private static final String GOOGLE_RECAPTCHA_VERIFY_URL = "https://www.google.com/recaptcha/api/siteverify";
private static final double RECAPTCHA_SCORE_THRESHOLD = 0.5;
public boolean verify(String token) {
if (token == null || token.isEmpty()) {
return false;
}
RestTemplate restTemplate = new RestTemplate();
LinkedMultiValueMap<String, String> params = new LinkedMultiValueMap<>();
params.add("secret", secretKey);
params.add("response", token);
RecaptchaResponse response = restTemplate.postForObject(GOOGLE_RECAPTCHA_VERIFY_URL, params, RecaptchaResponse.class);
if (response == null || !response.isSuccess()) {
log.warn("[AUTH] recaptcha verification failed: {}", response != null ? response.getErrorCode() : "Response is null");
return false;
}
if (response.getScore() < RECAPTCHA_SCORE_THRESHOLD) {
log.warn("[AUTH] recaptcha score threshold exceeded: {}", response.getScore());
return false;
}
return true;
}
}
작동 방식을 요약해보면,
- Google 검증 서버에 비밀키(secret-key)와 토큰(captchaToken)을 전달
- 응답에서 success와 score 확인
- 점수가 0.5 미만이면 bot으로 판단.
- 로그를 남기로 false 반환
threshold 는 서비스 특성에 따라 조절이 가능하다.
댓글이나 회원가입 같은 영역은 0.3 ~0.5, 결제 요청은 0.7 이상으로 높게 설정하는 식으로 조정한다.
5. 컨트롤러 작성
이제 검증로직을 실제 로그인 요청에 연결한다.
로그인 요청 시 토큰을 받아 RecaptchaService.verify() 메서드로 검증하고, 결과에 따라 로그인 처리를 진행한다.
@RequiredArgsConstructor
@RestController
public class LoginController {
private final RecaptchaService recaptchaService;
@PostMapping("/login")
public ResponseEntity<String> login(@RequestBody LoginRequest loginRequest) {
boolean isRecaptchaVerified = recaptchaService.verify(loginRequest.getRecaptchaToken());
if (!isRecaptchaVerified) {
return ResponseEntity.status(HttpStatus.BAD_REQUEST).body("recaptcha verification failed");
}
return ResponseEntity.ok("login successful");
}
}
지금은 로그인으로 테스트를 했지만, reCaptcha 검증은 로그인 시점 뿐 아니라 회원가입, 비밀번호 찾기, 댓글 작성 등에도 적용할 수 있다. verify() 이후에 실제 로그인 로직을 연결하면 된다.
이렇게 구현하면 프론트엔드에서 받은 토큰을 백엔드가 Google에 직접 검증하게 된다. 보안을 유지하면서 사용자 경험을 개선시킬 수 있다.

다음 글에서는 프론트(vue)에서 어떻게 적용 시키는지 설명할 예정이다.
'Growth log (성장 기록) > Project (경험)' 카테고리의 다른 글
| reCAPTCHAT v3 적용하기(2) - Vue 프론트엔드 설정 (0) | 2025.10.30 |
|---|