모델 및 서비스 스팸 방지 및 CAPTCHA 지원

REST API, GraphQL API 또는 웹 UI에 스팸 또는 CAPTCHA 지원을 추가하기 전에, 다음에 필요한 지원을 먼저 추가해야 합니다:

  1. 백엔드 ActiveRecord 모델.
  2. 서비스 레이어.

스팸 또는 CAPTCHA 요청 구현의 종류에 관계없이 다음의 모든 변경 사항 또는 대부분의 변경 사항이 필요합니다. GraphQL API를 기반으로 한 일부 최신 기능은 컨트롤러가 없을 수 있으며, 이 경우 컨트롤러에 mark_as_spam 액션을 추가할 필요가 없습니다.

이 작업을 수행하려면:

  1. ActiveRecord 모델에 Spammable 지원 추가.
  2. 컨트롤러에 mark_as_spam 액션에 대한 지원 추가.
  3. 서비스의 execute 메서드에 check_for_spam 호출 추가.

ActiveRecord 모델에 Spammable 지원 추가

  1. 모델 클래스에 Spammable 모듈을 포함시킵니다:

    include Spammable
    
  2. 스팸을 확인할 수 있는 필드를 나타내기 위해 attr_spammable을 추가합니다. 모델당 최대 두 개의 필드를 지원합니다: titledescription. 어떤 필드를 title 또는 description으로 고려할지 지정할 수 있습니다. 예를 들어, 이 줄은 content 필드를 description으로 지정합니다:

    attr_spammable :content, spam_description: true
    
  3. #check_for_spam? 메서드 구현을 추가합니다:

    def check_for_spam?(user:)
      # 변경된 속성, 사용자 유형, 데이터가 공개적으로 표시되는지 여부 및 기타 기준을 포함하여
      # 적용 가능한 다양한 검토 기준에 따라 boolean 결과를 반환합니다. 
      # 이는 모델 유형에 따라 다를 수 있으며 
      # 스팸 확인 요구사항이 진화함에 따라 변경될 수 있습니다.
    end
    

    이 메서드의 논리 검사의 예제를 보려면 다른 기존 Spammable 모델의 구현을 참조하세요.

컨트롤러에 mark_as_spam 액션에 대한 지원 추가

SpammableActions::AkismetMarkAsSpamAction 모듈은 컨트롤러에 #mark_as_spam 액션에 대한 지원을 추가합니다. 이 컨트롤러는 관리자들이 관리자 영역의 스팸 로그 섹션에서 관련된 Spammable 모델의 스팸을 관리할 수 있게 합니다.

  1. 컨트롤러에 SpammableActions::AkismetMarkAsSpamAction 모듈을 포함시킵니다.

    include SpammableActions::AkismetMarkAsSpamAction
    
  2. #spammable_path 메서드 구현을 추가합니다. 스팸 관리 페이지는 편집 후 이 페이지로 리디렉션됩니다. 이 메서드의 구현 예제를 보려면 다른 기존 컨트롤러의 구현을 참조하세요. 일반적으로, 이는 Spammable 모델의 컨트롤러에서 #show 액션이어야 합니다.

    def spammable_path
      widget_path(widget)
    end
    

참고:

기능이 어떻게 구현되는지에 따라 컨트롤러에서 다른 변경이 필요할 수 있습니다. 자세한 내용은 Web UI를 참조하세요.

서비스의 execute 메서드에 check_for_spam 호출 추가

이 접근 방식은 스팸 가능 속성을 지속할 수 있는 모든 서비스에 적용됩니다:

  1. app/services 아래의 관련 생성 또는 업데이트 서비스에서, 모델에 대해 check_for_spam 메서드를 호출합니다.

  2. 스팸 체크가 실패할 경우:

    • 스팸 모델에 오류가 추가되어 유효하지 않게 되고, 저장을 방지합니다.
    • needs_recaptcha 속성이 true로 설정됩니다.

이러한 변경은 후속 백엔드 및 프론트엔드 CAPTCHA 논리에 대해 모델을 처리할 수 있게 합니다.

각 관련 서비스에 이러한 변경을 적용하세요:

  1. execute 메서드에서, 모델에 대해 check_for_spam 메서드를 호출합니다. (서비스가 해당 패턴을 사용하는 경우 before_create 또는 before_update를 사용할 수도 있습니다.) 이 메서드는 명명된 인수를 사용하므로 기존 예제를 참조하면 사용이 명확합니다. 그러나 두 가지 중요한 고려사항이 있습니다:
    1. check_for_spam은 생성되지 않은 (그리고 더러운) Spammable 모델 인스턴스에 필요한 모든 변경 사항이 이루어진 _후_에 실행되어야 합니다. 이 순서는 스팸 확인을 위해 스팸 가능 속성이 존재하도록 보장합니다.
    2. check_for_spam은 오류를 확인하고 save를 시도하기 전에 전에 실행되어야 합니다. 모델의 변경된 속성에서 잠재적인 스팸이 감지되면 저장을 방지해야 합니다.
module Widget
  class CreateService < ::Widget::BaseService
    # 참고: 스팸 검사는 필요할 가능성이 높기 때문에 `perform_spam_check`의 기본값을 `true`로 설정합니다.
    def initialize(project:, current_user: nil, params: {}, perform_spam_check: true)
      super(project: project, current_user: current_user, params: params)

      @perform_spam_check = perform_spam_check
    end

    def execute
      widget = Widget::BuildService.new(project, current_user, params).execute

      # 스팸 체크 전에 더러운 모델을 조작하는 추가 코드.

      # 참고: 스팸 가능 모델이 인스턴스화된 후, 그러나 
      # 유효성 검사되거나 저장되기 전에 이를 수행하세요.
      widget.check_for_spam(user: current_user, action: :create) if perform_spam_check

      # 모델 저장과 관련된 추가 코드이지만, 어떤 속성도 변경해서는 안 됩니다.

      widget.save
    end

    private

    attr_reader :perform_spam_check