REST API 스팸 방지 및 CAPTCHA 지원

모델을 REST API를 통해 수정할 수 있다면, 스팸 가능성이 있는 속성을 수정할 수 있는 모든 관련 API 엔드포인트에도 지원을 추가해야 합니다. 이는 POSTPUT 뮤테이션을 확실히 포함하지만, 모델의 비밀/공개 플래그를 변경하는 것과 관련된 것과 같이 다른 엔드포인트도 포함될 수 있습니다.

REST 엔드포인트에 지원 추가

주요 단계는 다음과 같습니다:

  1. resource에서 helpers SpammableActions::CaptchaCheck::RestApiActionsSupport를 추가합니다.
  2. Update 서비스 클래스 생성자에 perform_spam_check: true를 전달합니다. Create 서비스에서는 기본적으로 true로 설정됩니다.
  3. Spammable 모델 인스턴스를 생성하거나 업데이트한 후, #check_spam_action_response!를 호출하여 생성된 또는 업데이트된 인스턴스를 변수에 저장합니다.
  4. 요청의 ‘failure’ 케이스에 대한 오류 처리 로직을 식별합니다. 생성 또는 업데이트에 성공하지 못했을 때, 가능한 스팸 감지의 인디케이터로 사용됩니다. 이는 Spammable 인스턴스에 오류를 추가합니다. 이 오류는 일반적으로 render_api_error! 또는 render_validation_error!와 유사합니다.
  5. 기존 오류 처리 로직을 with_captcha_check_rest_api(spammable: my_spammable_instance) 호출로 감싸서 기존 에러 처리 로직을 실행합니다. 이 때, Spammable 모델 인스턴스를 변수에 저장한 후 spammable: 이름 있는 인자로 전달합니다. 이 호출은 다음을 수행합니다:
    1. 모델에서 필요한 스팸 확인을 수행합니다.
    2. 스팸이 감지되면:
      • 설명적인 스팸 특정 오류 메시지를 가진 Grape #error! 익셉션을 발생시킵니다.
      • 응답에 에러 필드로 추가된 관련 정보를 포함합니다. 이러한 필드에 대한 자세한 내용은 REST API 문서의 스팸으로 감지된 요청 해결 섹션을 참조하세요.
    note
    앞서 설명한 표준 ApolloLink 또는 Axios 인터셉터 CAPTCHA 지원을 사용하는 경우, 자동으로 처리되기 때문에 필드 세부 정보를 무시할 수 있습니다. GraphQL API를 직접 사용하여 잠재적인 스팸을 확인하고 해결된 CAPTCHA 응답으로 요청을 다시 제출하려고 하는 경우에 이 정보가 관련이 있습니다.

snippets 리소스에서 postput 액션에 대한 예는 다음과 같습니다:

module API
  class Snippets < ::API::Base
    #...
    resource :snippets do
      # 이 헬퍼는 `#with_captcha_check_rest_api`를 제공합니다
      helpers SpammableActions::CaptchaCheck::RestApiActionsSupport
      
      post do
        #...
        service_response = ::Snippets::CreateService.new(project: nil, current_user: current_user, params: attrs).execute
        snippet = service_response.payload[:snippet]
        
        if service_response.success?
          present snippet, with: Entities::PersonalSnippet, current_user: current_user
        else
          # 일반적인 에러 응답을 `with_captcha_check_rest_api(spammable: snippet)` 블록으로 감싸줍니다
          with_captcha_check_rest_api(spammable: snippet) do
            # 가능한 스팸이 감지되었을 경우, `#with_captcha_check_rest_api`에 의해 Grape가 `error!`를 통해 처리하도록 익셉션이 발생합니다
            render_api_error!({ error: service_response.message }, service_response.http_status)
          end
        end
      end
      
      put ':id' do
        #...
        service_response = ::Snippets::UpdateService.new(project: nil, current_user: current_user, params: attrs, perform_spam_check: true).execute(snippet)
        
        snippet = service_response.payload[:snippet]
        
        if service_response.success?
          present snippet, with: Entities::PersonalSnippet, current_user: current_user
        else
          # 일반적인 에러 응답을 `with_captcha_check_rest_api(spammable: snippet)` 블록으로 감싸줍니다
          with_captcha_check_rest_api(spammable: snippet) do
            # 가능한 스팸이 감지되었을 경우, `#with_captcha_check_rest_api`에 의해 Grape가 `error!`를 통해 처리하도록 익셉션이 발생합니다
            render_api_error!({ error: service_response.message }, service_response.http_status)
          end
        end
      end
    end
  end
end