captcha를 구현하는 방법에는 크게 2가지 방법이 있습니다..
이글에서는 외부 API를 활용하는 방법에 중점을 두고 설명하고자 합니다.
시간과 리소스를 절약할 수 있음.지속적인 업데이트 제공.Google에서 제공하는 스팸 및 악의적인 봇으로부터 웹사이트를 보호하는 보안 서비스
reCAPTCHA v1 (초기 버전)
reCAPTCHA v2
reCAPTCHA v3


클라이언트 부분에 reCAPTCHA 추가
<script src="https://www.google.com/recaptcha/api.js" async defer></script>
...
<form action="/submit" method="POST">
<div class="g-recaptcha" data-sitekey="사이트 키 입력"></div>
<button type="submit">Submit</button>
</form>
import ReCAPTCHA from "react-google-recaptcha";
...
const [recaptchaToken, setRecaptchaToken] = useState<string | null>(null);
const handleRecaptchaChange = (token: string | null) => {
setRecaptchaToken(token); // 사용자가 인증되면 토큰 설정
console.log("Recaptcha Token:", token); // 테스트를 위한 로그
};
const handleSubmit = async (e: React.FormEvent) => {
e.preventDefault();
console.log("로그인 완료", { email, password });
// reCAPTCHA 토큰이 없으면 경고 메시지 출력
if (!recaptchaToken) {
alert("캡챠 완료 후에 시도해주세요.");
return;
}
// 서버로 토큰 전송
const response = await fetch("검증 path", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ token: recaptchaToken }),
});
const result = await response.json();
alert(result.message);
};
...
<form onSubmit={handleSubmit}>
<ReCAPTCHA
sitekey="사이트 키"
onChange={handleRecaptchaChange}
/>
<button type="submit">Submit</button>
</form>
react 사용 중
-> npm install react-google-recaptcha로 패키지 설치
react-TS 사용 중
-> npm install --save-dev @types/react-google-recaptcha로 패키지 설치
`react-google-recaptcha`는 JS로 작성된 라이브러리이므로, TS는 기본적으로 해당 라이브러리의 구조와 타입을 알지 못해 에러가 발생
백엔드 부분에서 reCAPTCHA 검증 진행
@Value("${recaptcha.secret}") // application.properties에 비밀 키 저장
private String recaptchaSecret;
@Value("${recaptcha.url}") // reCAPTCHA API URL 관리 (https://www.google.com/recaptcha/api/siteverify)
private String recaptchaUrl;
...
public ResponseEntity<APIResponse> recaptcha(@RequestBody Map<String, String> body) {
String token = body.get("token");
if (token == null || token.isEmpty()) {
return ResponseEntity.badRequest().body(new APIResponse(HttpStatus.BAD_REQUEST.value(), "reCAPTCHA 토큰이 없습니다."));
}
RestTemplate restTemplate = new RestTemplate();
// MultiValueMap 자료 구조로 데이터를 저장
MultiValueMap<String, String> params = new LinkedMultiValueMap<>();
params.add("secret", recaptchaSecret);
params.add("response", token);
// HTTP Post 요청 진행
ResponseEntity<Map> response = restTemplate.postForEntity(
url // reCAPTCHA 검증 API의 URL
, params // request 될 파라미터들
, Map.class // 응답 데이터 수신 타입
);
Map responseBody = response.getBody(); // response 데이터
boolean success = (Boolean) responseBody.get("success"); // response의 "success" 필드 값
// 조건문으로 결과 처리
if (success) {
return ResponseEntity.ok(new APIResponse(HttpStatus.OK.value(), "검증 성공!"));
} else {
return ResponseEntity.badRequest().body(new APIResponse(HttpStatus.BAD_REQUEST.value(), "검증 실패!"));
}
}
❓ 일반적인 Map이 아닌 MultiValueMap을 사용한 이유는?
Spring Boot의 RestTemplate을 사용하여 Google reCAPTCHA API와 통신할 때, HashMap이 아닌 MultiValueMap을 사용해야만 정상적으로 작동하는 이유는 HTTP 요청 데이터의 포맷(format)이 Google reCAPTCHA API가 요구하는 방식과 정확히 일치해야 하기 때문입니다.
HashMap
1. 데이터를 단순히 Key-Value 구조로 저장
2. 요청 데이터가 기본적으로 JSON 포맷으로 변환
{
"secret": "YOUR_SECRET_KEY",
"response": "USER_TOKEN"
}
MultiValueMap
1. 데이터를 URL 인코딩된 Key-Value 쌍으로 변환
2. 요청의 Content-Type을 application/x-www-form-urlencoded로 설정
secret=YOUR_SECRET_KEY&response=USER_TOKEN
Google reCAPTCHA API는 요청 데이터를
application/x-www-form-urlencoded형식으로 받기를 요구하기 때문에 MultiValueMap을 사용
만약 HashMap을 데이터를 구성하였다면 application/x-www-form-urlencoded 형식에 맞게 변환하여 반환 요청 전송이 필요




@참고문헌
https://developers.google.com/recaptcha/intro?hl=ko
https://velog.io/@coaudtn0276/ProjectReact%EB%A5%BC-%EC%9D%B4%EC%9A%A9%ED%95%9C-google-reCAPTCHA