Spring Boot에서 CAPTCHA API 호출하기

작성: solvethree읽는 데 3분

서버 측 CaptchaClient 빈을 하나 등록하고 작업을 한 번 생성한 다음, 저장한 ID로 폴링하세요. 아래 헬퍼는 동기식 Spring RestClient 호출을 사용하며 Gson JsonObject 응답을 반환합니다.

이 페이지의 내용

의존성 추가 및 헬퍼 등록

제공된 헬퍼는 Java 21+, Spring Web 7.0.10, Gson 2.15.0을 사용합니다. 기존 Spring Boot 프로젝트에서는 예제에 맞추려고 spring-web만 재정의하지 말고 Boot가 관리하는 호환 Spring 버전을 유지하세요. 애플리케이션에 Gson이 아직 포함되어 있지 않다면 의존성으로 추가하세요.

Spring · 서버 측 RestClient 서비스
// Java 21+, Spring Web 7.0.10 and Gson 2.15.0; use your Boot dependency management.
// Save as CaptchaClient.java in your application's package.
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import java.net.http.HttpClient;
import java.time.Duration;
import org.springframework.http.MediaType;
import org.springframework.http.client.JdkClientHttpRequestFactory;
import org.springframework.web.client.RestClient;

public final class CaptchaClient {
    private final RestClient api;
    public CaptchaClient(String key) {
        var transport = HttpClient.newBuilder().connectTimeout(Duration.ofSeconds(10))
            .followRedirects(HttpClient.Redirect.NEVER).build();
        var factory = new JdkClientHttpRequestFactory(transport);
        factory.setReadTimeout(Duration.ofSeconds(25));
        api = RestClient.builder().baseUrl("https://api.solvethree.com/v1")
            .requestFactory(factory).defaultHeader("Authorization", "Bearer " + key).build();
    }
    public JsonObject create(String site, String proxy, String requestId) {
        var input = new JsonObject();
        input.addProperty("site", site);
        input.addProperty("action", "login");
        input.addProperty("proxy", proxy);
        input.addProperty("requestId", requestId); // Save before calling create.
        String body = api.post().uri("/tasks?waitMs=0").contentType(MediaType.APPLICATION_JSON)
            .body(input.toString()).retrieve().body(String.class);
        return JsonParser.parseString(body).getAsJsonObject();
    }
    public JsonObject get(String id) {
        if (!id.matches("[A-Za-z0-9_-]+")) throw new IllegalArgumentException("Invalid task ID");
        String body = api.get().uri("/tasks/{id}?waitMs=15000", id)
            .retrieve().body(String.class);
        return JsonParser.parseString(body).getAsJsonObject();
    }
}
// Construct once with System.getenv("SOLVETHREE_API_KEY") in a server-side @Bean.
// Call create once, save its id, then get(id) until ready/failed or your deadline.
// RestClient throws on HTTP errors; keep the saved requestId and any known task id.

클래스를 애플리케이션 패키지에 CaptchaClient.java로 저장하세요. 서버 측 @Bean 팩터리 메서드에서 SOLVETHREE_API_KEY 환경 변수를 사용해 인스턴스를 한 번 생성하세요. 시작 시 키가 설정되어 있는지 확인하세요. 헬퍼는 이 키를 Bearer 인증 헤더에 추가합니다. 브라우저 코드에 키를 넣지 마세요.

생성자 주입으로 해당 빈을 재사용하세요. RestClient는 여러 스레드에서 재사용할 수 있지만, 각 요청은 호출 스레드를 블로킹합니다. WebFlux 이벤트 루프에서 이 메서드들을 호출하지 마세요. 동기식 클라이언트에 관한 설명은 Spring의 REST 클라이언트 문서를 참고하세요.

전송 계층은 연결 타임아웃 10초, 읽기 타임아웃 25초를 사용하며 리디렉션을 따르지 않습니다. 응답은 타입이 지정된 응답 객체로 매핑하지 않고 문자열로 읽은 뒤 Gson으로 파싱합니다.

사이트, 액션, 프록시 설정

create를 호출하기 전에 워크스페이스에서 대상 사이트의 활성 프로필을 설정하세요. 호스트명과 액션을 전송해도 해당 프로필은 생성되지 않습니다. 사이트가 아직 설정되지 않았다면 사이트 프로필 설정을 참고하세요.

  • 전체 페이지 URL이 아닌 호스트명을 site로 전달하세요. reCAPTCHA 요청에는 url 또는 type 필드가 없습니다.
  • 헬퍼는 action을 login으로 하드코딩합니다. 의도한 요청이 다른 액션을 사용한다면 이 값을 변경하거나 메서드 매개변수로 만드세요.
  • 프록시 URL 하나를 전달하세요. API에는 proxy 또는 proxyCandidates 중 정확히 하나가 필요하며, 이 헬퍼는 단일 프록시 옵션을 구현합니다.
  • 헬퍼는 recaptchaType을 생략하므로 API는 기본값인 enterprise-v3를 사용합니다.

한 번 생성한 뒤 저장한 작업 ID로 폴링

  1. 1.

    생성 전에 요청 ID 저장

    ASCII 영문자, 숫자, 밑줄 또는 하이픈으로 이루어진 1–64자 길이의 requestId를 정하세요. create를 호출하기 전에 작업 입력값과 함께 저장하세요. 생성 응답이 유실되어도 이 값으로 동일한 작업을 복구할 수 있습니다.

  2. 2.

    create 호출 및 반환된 id 저장

    호스트명, 프록시, 저장한 요청 ID로 빈의 create 메서드를 호출하세요. 이 메서드는 waitMs=0으로 /v1/tasks에 POST 요청을 한 번 전송합니다. 반환된 id를 저장하세요. 이후 조회에는 요청 ID가 아닌 이 작업 ID를 사용하세요.

  3. 3.

    작업이 종료 상태에 도달할 때까지 get 호출

    저장한 작업 ID를 get에 전달하세요. 각 조회는 waitMs=15000을 요청하고 응답을 기다리는 동안 블로킹합니다. 애플리케이션 수준의 제한 시간 내에서 pending 또는 solving이면 계속하세요. ready 또는 failed이면 중단하세요. HTTP 타임아웃은 전체 폴링 루프가 아닌 개별 요청을 제한합니다.

  4. 4.

    준비 완료 상태에서만 솔루션 읽기

    reCAPTCHA의 경우 작업 ID가 아닌 solution.token을 의도한 대상에 제출하세요. 성공한 동일한 프록시와 함께 반환된 userAgent 및 secChUa를 사용하세요. 관련 없는 브라우저 헤더로 대체하거나 대상 URL을 변경하지 마세요. 준비 완료 상태의 작업은 솔루션을 제공하지만, 이후 요청을 대상이 수락했다는 의미는 아닙니다.

HTTP 오류와 작업 실패를 구분하여 처리

작업 ID를 받기 전에 create 호출이 타임아웃됩니다.

클라이언트가 응답을 받지 못했더라도 서버에서 작업이 생성되었을 수 있습니다.

다음을 시도해 보세요: 저장한 requestId로 동일한 작업 요청을 다시 보내 복구하세요. 입력값이 일치하면 기존 작업이 반환됩니다. 새 논리적 작업을 생성하려는 경우가 아니라면 새 요청 ID를 생성하지 마세요.

get 호출이 HTTP 계층에서 실패합니다.

RestClient.retrieve()는 성공이 아닌 HTTP 응답을 받으면 예외를 발생시킵니다. 이는 작업 상태가 failed인 것과 다릅니다.

다음을 시도해 보세요: 성공 페이로드를 읽으려 하기 전에 HTTP 오류를 처리하세요. 다른 작업을 생성하지 말고 복구를 위해 이미 알고 있는 작업 ID를 유지하세요. 잘못된 입력값을 그대로 재시도하기 전에 원인을 진단하세요.

작업 응답의 상태가 failed입니다.

해당 작업의 errorCode와 error를 읽으세요. site-unreachable은 발생 가능한 작업 오류 코드 중 하나입니다.

다음을 시도해 보세요: 해당 작업의 폴링을 중단하고 보고된 원인을 해결한 뒤 새 작업을 시작하세요. 풀이 실패 시 환불됩니다. 실패가 크레딧 부족을 의미한다고 단정하지 마세요.

요청 ID를 재사용하면 충돌이 발생합니다.

새 작업 입력값을 해당 요청 ID에 대해 저장한 입력값과 비교하세요.

다음을 시도해 보세요: 기존 작업을 복구하려면 원래 입력값을 사용하세요. 의도적으로 다른 작업 입력값을 사용하려면 새 요청 ID를 사용하세요.

오류 해석은 CAPTCHA API 오류를 참고하세요.

첫 작업을 실행하세요.

워크스페이스 크레딧 $5로 시작하세요.

워크스페이스 만들기
Spring Boot에서 CAPTCHA API 호출하기 · solvethree