웹 UI 스팸 방지 및 CAPTCHA 지원
GitLab 애플리케이션의 새로운 UI 영역에 스팸 방지 및 CAPTCHA 지원을 추가하는 접근 방식은 기존 코드가 구현된 방식에 따라 다릅니다.
요청 제출의 지원되는 시나리오
세 가지 다른 시나리오가 지원됩니다. 두 가지는 JavaScript XHR/Fetch 요청에 사용되며 Apollo나 Axios 중 하나를 사용하고, 다른 하나는 표준 HTML 폼 요청에만 사용됩니다:
- JavaScript 기반 제출(아마도 Vue를 통해)
- Apollo 사용 (Fetch/XHR 요청을 통한 GraphQL API)
- Axios 사용 (Fetch/XHR 요청을 통한 REST API)
- 표준 HTML 폼 제출 (HTML 요청)
구현의 일부는 지원해야 하는 시나리오에 따라 다릅니다.
JavaScript XHR/Fetch 요청에 특화된 구현 작업
두 가지 접근 방식은 완전히 지원됩니다:
- GraphQL API를 사용한 Apollo.
- GraphQL API를 사용한 Axios.
프론트엔드와 백엔드 간의 스팸 및 CAPTCHA 관련 데이터 통신은 모델에 추가 필드를 필요로 하지 않습니다. 대신에 통신은 다음과 같이 처리됩니다:
- 요청에서 사용자 지정 헤더 값으로.
- 응답에서 상위 수준의 JSON 필드로.
스팸 및 CAPTCHA 관련 로직은 재사용 가능한 모듈 및 도우미 메서드로 깔끔하게 추상화되어 있으며, 잠재적인 스팸이 감지되거나 CAPTCHA 표시가 필요한 경우 기존 흐름을 변경시키지 않고 기존 로직을 변경할 수 있습니다. 이 접근 방식을 통해 새로운 애플리케이션 영역에 스팸 및 CAPTCHA 지원을 추가할 때 기존 로직을 최소한으로 변경할 수 있습니다. 프론트엔드의 경우, 변경 사항이 없어도 될 수도 있습니다!
프론트엔드에서는 Vue를 위한 ApolloLink
및 Axios를 위한 Axios interceptor를 사용하여 추상적이고 투명하게 처리됩니다. CAPTCHA 표시는 표준 GitLab UI / Pajamas 모달 컴포넌트에서 처리됩니다. 관련 프론트엔드 코드는 app/assets/javascripts/captcha
에서 찾을 수 있습니다.
그러나 실제 요청 가로채기 및 모달 처리가 투명하더라도, 폼이나 페이지에 사용된 JavaScript 또는 Vue 컴포넌트를 변경하지 않고는 요청이나 오류 처리에 변경이 필요할 수 있습니다. 기존 동작이 올바르게 작동하지 않을 수 있기 때문에 변경 사항이 필요합니다. 예를 들어, 실패한 또는 취소된 CAPTCHA 표시가 표준 요청 흐름이나 UI 업데이트를 중단하는 경우 등입니다. 잠재적인 문제를 발견하기 위해 신중한 탐사 테스트가 중요합니다.
이 순서도는 프론트엔드에서 JavaScript XHR/Fetch 요청의 표준 CAPTCHA 흐름을 보여줍니다:
백엔드도 섞이지 않게 깔끔하게 추상화되어 있습니다. 관련된 백엔드 컨트롤러 동작에 대해 필요한 세 가지 주요 변경은 다음과 같습니다(일반적으로 create
/update
중 하나):
-
perform_spam_check: true
를 Update Service 클래스 생성자에 전달합니다. Create Service에서는 기본값으로true
로 설정됩니다. - 스팸 확인이 모델의 변경을 가리키는 경우:
- 모델에 오류가 추가됩니다.
- 모델의
needs_recaptcha
속성이 true로 설정됩니다.
-
#with_captcha_check_json_format
도우미 메서드에 전달된 블록으로 랩핑된 기존 컨트롤러 동작 반환값(렌더링 또는 리디렉팅)을 확인하여 처리합니다. 이는 다음을 투명하게 처리합니다:- CAPTCHA가 활성화되어 있는지 확인하고, 그렇다면 다음 단계 진행.
- 모델에 오류가 있는지, 그리고
needs_recaptcha
플래그가 true인지 확인합니다.- 그렇다면: JSON 응답에 적절한 스팸 또는 CAPTCHA 필드를 추가하고
409 - Conflict
HTTP 상태 코드를 반환합니다. - 그렇지 않다면(만약 CAPTCHA가 비활성화되었거나 스팸이 감지되지 않은 경우): 블록 내에서 전달된 표준 요청 반환 로직이 실행됩니다.
- 그렇다면: JSON 응답에 적절한 스팸 또는 CAPTCHA 필드를 추가하고
추상화 덕분에 설명하는 것보다 구현이 더 간단합니다. 숨겨진 세부 사항을 크게 걱정할 필요가 없습니다!
다음 변경 사항을 수행하세요:
컨트롤러 동작에 지원 추가
기능의 프론트엔드가 컨트롤러 동작에 직접 제출하고 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: ...` (또는 래퍼 메서드) 및 관련 로직을 여기에 전부 래핑되어 있습니다.
# 예를 들어:
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 (HAML) 페이지를 반환합니다. 이 경우에는 위에서 설명한 JavaScript 기반 프론트엔드 지원을 사용할 수 없습니다. 대신 HAML 템플릿을 통해 CAPTCHA 양식의 렌더링을 처리해야 합니다.
모든 것은 여전히 깔끔하게 추상화되어 있으며, 백엔드 컨트롤러의 구현은 거의 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