--- title: Go Server SDK --- # Go Server SDK 공식 Go 서버 측 SDK입니다 — `github.com/Captcha-La/captchala-go`. SDK가 대신 처리해 주는 세 가지 작업: 1. **Validate** — 브라우저 SDK에서 받은 `pt_` pass token 을 검증합니다. 2. **Issue** — 다가올 챌린지를 특정 액션 / IP / UID 에 바인딩하는 일회용 `sct_` server token 을 발급합니다. 3. **Moderate** — 대시보드가 사용하는 동일한 OpenAI 호환 파이프라인을 통한 멀티모달(텍스트 + 이미지) 콘텐츠 모더레이션을 수행합니다. ## 설치 ```bash go get github.com/Captcha-La/captchala-go ``` 표준 라이브러리만 사용합니다. 외부 의존성이 없습니다. ## Validate (`pt_` 토큰) 최종 사용자의 IP 를 전달하십시오(`CF-Connecting-IP` / `X-Forwarded-For` 에서 가져오고 `RemoteAddr` 로 폴백). 선택 사항이지만 **권장**됩니다 — 추가 위험 검사에 사용됩니다. ```go import captchala "github.com/Captcha-La/captchala-go" client := captchala.NewClient(appKey, appSecret) func handler(w http.ResponseWriter, r *http.Request) { token := r.FormValue("captcha_token") result, err := client.ValidateWithClientIP(token, false, clientIP(r)) if err != nil || !result.Valid { http.Error(w, result.Error, http.StatusBadRequest) return } // Verification passed; proceed with the request. // result.CaptchaArgs has Platform / UserIP / Referer / Pkg / SolvedAt / RiskScore } // Real end-user IP behind a CDN / proxy func clientIP(r *http.Request) string { if ip := r.Header.Get("CF-Connecting-IP"); ip != "" { return ip } if xff := r.Header.Get("X-Forwarded-For"); xff != "" { return strings.TrimSpace(strings.Split(xff, ",")[0]) } host, _, _ := net.SplitHostPort(r.RemoteAddr) return host } ``` IP 가 없으신가요? `client.Validate(token)` 을 사용하십시오 — 검증은 그대로 동작하며, IP 기반 추가 위험 신호만 빠질 뿐입니다. 원본 `server_token` 이 `bind_uid` 와 함께 발급되었다면 비교하십시오: ```go if result.UID != expectedUserID { http.Error(w, "user mismatch", http.StatusBadRequest) return } ``` 챌린지를 푼 시점의 컨텍스트도 참고용으로 `result.CaptchaArgs` 에 함께 반환됩니다: ```go ip := result.CaptchaArgs.UserIP // visitor IP recorded at solve time ``` ## Server token 발급 가치가 높은 플로우(로그인, 회원가입, 결제)에 권장되는 방식: 백엔드가 일회용 `sct_` 토큰을 발급하여 브라우저에 전달하고, 브라우저는 이를 `serverToken` prop 으로 사용합니다. 단일 사용, 액션 한정이며 선택적으로 IP/UID 에 바인딩됩니다. ```go issue, err := client.IssueServerTokenWithOptions("login", captchala.IssueOptions{ BindingIP: userIP, // 다른 IP가 토큰을 사용하면 백엔드가 거부합니다 TTL: 300, // 초 MaxUses: 5, // SDK 재시도 예산 BindUID: userID, // 검증 시 ValidateResult.UID 와 매칭 }) if err != nil || !issue.OK { http.Error(w, issue.Error, http.StatusBadRequest) return } // issue.Token 을 브라우저로 전달합니다 ``` 기본값 전용 단축 메서드: ```go issue, err := client.IssueServerToken("login") ``` ## 콘텐츠 모더레이션 멀티모달 — `ModerationItem` 슬라이스(OpenAI 호환 형식의 텍스트 및/또는 image_url)를 입력으로 받습니다: ```go result, err := client.ModerationCheck([]captchala.ModerationItem{ captchala.TextItem(userComment), captchala.ImageURLItem(uploadedImageURL), }, userID) if err != nil || !result.OK { http.Error(w, result.Error, http.StatusBadRequest) return } if result.Flagged { if result.HasCategory("violence", "csam") { // 강제 차단 } } ``` 순수 텍스트용 단축 메서드: ```go result, err := client.ModerationText("user comment here", userID) ``` 카테고리는 모델이 정의합니다. 고정된 집합을 하드코딩하는 대신 `result.Categories` 를 방어적으로 순회하십시오. ## 타입 | Type | Fields | |---|---| | `ValidateResult` | `Valid`, `Error`, `UID`, `ChallengeID`, `Action`, `Offline`, `ClientOnly`, `Warning`, `CaptchaArgs` | | `CaptchaArgs` (참고용) | `Platform`, `UserIP`, `Referer`, `Pkg`, `SolvedAt`, `RiskScore` | | `IssueResult` | `OK`, `Token`, `ExpiresIn`, `IssuedAt`, `Error`, `Message` | | `IssueOptions` | `BindingIP`, `TTL`, `MaxUses`, `BindUID` | | `ModerationItem` | `Type`, `Text`, `ImageURL` (`TextItem()` / `ImageURLItem()` 헬퍼 사용) | | `ModerationResult` | `OK`, `Flagged`, `Categories`, `ContentType`, `Raw`, `Error`, `Message`; 메서드 `HasCategory(...names)` | ## 링크 - [pkg.go.dev](https://pkg.go.dev/github.com/Captcha-La/captchala-go) · [GitHub](https://github.com/Captcha-La/captchala-go) - [Web SDK 개요](/ko/web-sdk) · [API 레퍼런스](/ko/api-reference)