웹 UI 스팸 보호 및 CAPTCHA 지원
GitLab 애플리케이션의 새로운 UI 영역에 스팸 보호 및 CAPTCHA 지원을 추가하는 접근 방식은 기존 코드의 구현 방식에 따라 달라집니다.
요청 제출의 지원 시나리오
세 가지 서로 다른 시나리오가 지원됩니다. 두 가지는 Apollo 또는 Axios에 대한 JavaScript XHR/Fetch 요청에서 사용되며, 하나는 표준 HTML 양식 요청에서만 사용됩니다:
- JavaScript 기반 제출 (Vue를 통해 가능)
- Apollo 사용 (Fetch/XHR 요청을 통한 GraphQL API)
- Axios 사용 (Fetch/XHR 요청을 통한 REST API)
- 표준 HTML 양식 제출 (HTML 요청)
구현의 일부는 당신이 지원해야 하는 시나리오에 따라 달라집니다.
JavaScript XHR/Fetch 요청에 대한 특정 구현 작업
두 가지 접근 방식이 완전히 지원됩니다:
- Apollo, GraphQL API 사용.
- Axios, GraphQL API 사용.
프론트엔드와 백엔드 간의 스팸 및 CAPTCHA 관련 데이터 통신은 모델에 추가 필드를 필요로 하지 않습니다. 대신 통신은 다음과 같이 처리됩니다:
- 요청의 사용자 정의 헤더 값을 통해.
- 응답의 최상위 JSON 필드를 통해.
스팸 및 CAPTCHA 관련 로직은 재사용 가능한 모듈 및 헬퍼 메서드로 깔끔하게 추상화되어 있으며, 기존 로직을 래핑하고 잠재적인 스팸이 감지되거나 CAPTCHA 표시가 필요할 경우에만 기존 흐름을 변경합니다. 이러한 접근 방식은 애플리케이션의 새로운 영역에 스팸 및 CAPTCHA 지원을 기존 로직에 대한 최소한의 변경으로 추가할 수 있게 합니다. 프론트엔드의 경우, 제로 변경이 필요할 수 있습니다!
프론트엔드에서는 Apollo의 ApolloLink
와 Axios의 인터셉터를 사용하여 이를 추상적으로 및 투명하게 처리합니다. CAPTCHA 표시 는 표준 GitLab UI / Pajamas 모달 컴포넌트로 처리됩니다. 모든 관련 프론트엔드 코드는 app/assets/javascripts/captcha
에서 찾을 수 있습니다.
하지만 요청 인터셉션 및 모달의 실제 처리는 기존 JavaScript 또는 Vue 구성 요소에 필수 변경 없이 투명하게 처리되지만, 요청 또는 오류 처리에 대한 변경이 필요할 수 있습니다. 변경이 필요한 이유는 기존 동작이 제대로 작동하지 않을 수 있기 때문입니다. 예를 들어, 실패하거나 취소된 CAPTCHA 표시가 표준 요청 흐름이나 UI 업데이트를 방해할 수 있습니다. 모든 시나리오에 대한 신중한 탐색 테스트가 잠재적인 문제를 발견하는 데 중요합니다.
이 순서도는 프론트엔드에서 JavaScript XHR/Fetch 요청에 대한 표준 CAPTCHA 흐름을 설명합니다:
백엔드는 또한 믹스인 모듈 및 헬퍼 메서드를 통해 깔끔하게 추상화되어 있습니다. 관련 백엔드 컨트롤러 작업에 필요한 세 가지 주요 변경 사항은 다음과 같습니다 (일반적으로 create
/update
만 해당):
- Update Service 클래스 생성자에
perform_spam_check: true
를 전달합니다. 기본적으로 Create Service에서는true
로 설정됩니다. - 스팸 검사에서 모델 변경이 잠재적으로 스팸으로 분류되면:
- 오류가 모델에 추가됩니다.
- 모델의
needs_recaptcha
속성이 true로 설정됩니다.
- 기존 컨트롤러 액션 반환 값(렌더링 또는 리다이렉트)을
#with_captcha_check_json_format
헬퍼 메서드에 전달된 블록으로 래핑하여 투명하게 처리합니다:- CAPTCHA가 활성화되어 있는지 확인하고, 그렇다면 다음 단계로 진행합니다.
- 모델에 오류가 포함되어 있고
needs_recaptcha
플래그가 true인지 확인합니다.- 예 경우: 적절한 스팸 또는 CAPTCHA 필드를 JSON 응답에 추가하고
409 - Conflict
HTTP 상태 코드를 반환합니다. - 아니오 경우 (CAPTCHA가 비활성화되었거나 스팸이 감지되지 않은 경우): 블록에 전달된 표준 요청 반환 로직을 실행합니다.
- 예 경우: 적절한 스팸 또는 CAPTCHA 필드를 JSON 응답에 추가하고
추상화 덕분에 구현이 설명하는 것보다 더 간단합니다.
숨겨진 세부 사항에 대해 많이 걱정할 필요가 없습니다!
컨트롤러 액션에 대한 지원 추가
기능의 프론트엔드가 GraphQL API를 사용하지 않고 컨트롤러 액션에 직접 제출하는 경우, 적절한 컨트롤러에 대한 지원을 추가해야 합니다.
액션 메서드는 컨트롤러 클래스에 직접 있을 수 있으며, 또는 컨트롤러 클래스에 포함된 모듈로 추상화될 수 있습니다. 우리의 예시는 모듈을 사용합니다. 컨트롤러를 직접 수정할 때 유일한 차이점은 extend ActiveSupport::Concern
이 필요하지 않다는 것입니다.
module WidgetsActions
# 주의: 이 `extend`는 이미 존재할 수 있지만, 모든
# `include` 문보다 먼저 발생하도록 이동해야 합니다. 그렇지 않으면 포함된 모듈의
# 메서드를 찾을 수 없는 혼란스러운 버그가 발생할 수 있습니다.
extend ActiveSupport::Concern
include SpammableActions::CaptchaCheck::JsonFormatActionsSupport
def create
widget = ::Widgets::CreateService.new(
project: project,
current_user: current_user,
params: params
).execute
respond_to do |format|
format.json do
with_captcha_check_json_format do
# 액션의 기존 `render json: ...` (또는 래퍼 메서드) 및 관련 논리. 모델이 유효한지 여부에 따라
# 다양한 렌더링 사례를 포함할 수 있습니다. 이 모든 것은 `with_captcha_check_json_format` 블록
# 내에 래핑됩니다. 예를 들어:
if widget.valid?
render json: serializer.represent(widget)
else
render json: { errors: widget.errors.full_messages }, status: :unprocessable_entity
end
end
end
end
end
end
HTML 양식 요청에 특정한 구현 작업
애플리케이션의 일부 영역은 JavaScript 클라이언트를 통해 GraphQL API를 사용하도록 변환되지 않았으며, 대신 표준 Rails HAML 양식 제출을 HTML
MIME 유형 요청을 통해 의존하고 있습니다. 이러한 영역에서는 액션이 사전 렌더링된 HTML (HAML) 페이지를 응답 본문으로 반환합니다. 불행하게도 이 경우 자바스크립트 기반 프론트엔드 지원을 위와 같이 사용할 수 없습니다. 대신 CAPTCHA 양식의 렌더링을 HAML 템플릿을 통해 처리하는 대체 접근 방식을 사용해야 합니다.
모든 것은 여전히 깔끔하게 추상화되어 있으며, 백엔드 컨트롤러의 구현은 JavaScript/JSON 기반 접근 방식과 사실상 동일합니다. 모듈 이름과 헬퍼 메서드에서 JSON
을 HTML
(적절한 대소문자 사용)으로 교체합니다.
액션 메서드는 컨트롤러에 직접 있을 수도 있고, 모듈에 있을 수도 있습니다. 이 예제에서는 컨트롤러에 직접 있으며, create
대신 update
메서드도 포함됩니다:
class WidgetsController < ApplicationController
include SpammableActions::CaptchaCheck::HtmlFormatActionsSupport
def update
# `widget` 모델 인스턴스를 찾기 위한 기존 논리...
::Widgets::UpdateService.new(
project: project,
current_user: current_user,
params: params,
perform_spam_check: true
).execute(widget)
respond_to do |format|
format.html do
if widget.valid?
# 주의: `spammable_path`는 `SpammableActions::AkismetMarkAsSpamAction`
# 모듈에서 요구되며, 위의 지침에 따라 이 컨트롤러에서 이미 구현되었어야 합니다.
# 라우트 헬퍼 호출을 중복하지 않도록 여기에서 재사용됩니다.
redirect_to spammable_path
else
# 여기까지 도달했다면, 모델 인스턴스에서 오류가 발생했습니다 - 스팸 체크 실패로 인한 오류일 수도 있고
# 모델의 다른 유효성 검사 오류일 수도 있습니다. 어떤 경우든, 우리는 양식을 다시 렌더링할 것이며,
# CAPTCHA 렌더링이 필요할 경우, 이를 자동으로 처리하는
# `with_captcha_check_html_format`이 실행됩니다.
with_captcha_check_html_format { render :edit }
end
end
end
end
end