Turbo Stream으로 익명 댓글을 빠르고 안전하게 갱신하기
갱신 범위를 먼저 정한다
댓글 작성 뒤 페이지 전체를 새로 고치지 않으려면 Turbo Stream이 잘 맞는다. 다만 개인 블로그의 익명 댓글은 모든 방문자에게 실시간 방송할 필요가 없는 경우가 많다. 작성 요청을 보낸 브라우저의 댓글 목록과 폼만 갱신하면 구현과 운영이 단순해진다. Solid Cable broadcast는 협업 기능이 실제로 필요할 때 별도로 결정해도 늦지 않다.
화면에는 안정적인 target을 둔다.
<section id="comments">
<div id="comments-list">
<%= render @comments %>
</div>
<div id="comment-form">
<%= render "comments/form", post: @post, comment: @comment %>
</div>
</section>
ID는 디자인을 위한 이름이 아니라 서버 응답과 DOM이 맺는 계약이다. partial을 정리할 때도 이름을 임의로 바꾸지 말고 controller test로 target을 고정한다.
Turbo와 HTML 응답을 함께 제공한다
JavaScript가 꺼졌거나 Turbo 요청이 아닌 경우에도 댓글을 작성할 수 있어야 한다. 저장 성공 시 Turbo Stream은 댓글을 append하고 폼을 초기화한다. 일반 HTML 요청은 상세 화면으로 303 See Other redirect한다.
if @comment.save
respond_to do |format|
format.turbo_stream
format.html do
redirect_to post_path(@post, anchor: "comments"),
status: :see_other
end
end
end
실패 시에는 저장하지 못한 객체의 오류를 포함한 폼을 다시 렌더링하고 422 Unprocessable Content를 반환한다. 성공과 실패가 모두 200이면 브라우저 테스트는 통과하는 것처럼 보여도 캐시, 모니터링, 클라이언트 동작이 실제 결과를 구분하기 어려워진다.
공개된 발행 글에만 댓글을 허용한다
댓글 create action에서 Post를 찾을 때는 반드시 published scope를 사용한다. 초안 ID나 slug를 추측해 댓글을 달 수 있으면 관리자 화면을 통해 초안 존재 여부가 간접적으로 노출된다.
def set_post
@post = Post.published.find_by!(slug: params[:post_slug])
end
처리 순서도 중요하다. 먼저 발행 글인지 확인하고, 그다음 IP 기반 요청 제한을 적용한 뒤 honeypot을 검사한다. 존재하지 않는 초안 요청이 제한 횟수를 소모하거나, 반복 bot이 honeypot 응답만 받아 rate limit을 우회하지 않도록 순서를 테스트로 고정한다.
스팸 방지는 여러 얇은 장치를 겹친다
익명 댓글에 복잡한 CAPTCHA를 바로 도입하기보다 서버 검증 honeypot과 짧은 시간 창의 rate limit을 함께 적용할 수 있다. honeypot 필드는 사람에게 보이지 않지만 자동 입력 도구가 채우기 쉬운 이름으로 만들고, 서버는 값이 있으면 실제 저장 없이 일반 성공처럼 응답한다. bot에게 탐지 규칙을 알려 주는 오류 메시지는 반환하지 않는다.
댓글 본문과 작성자 이름에는 길이 제한을 둔다. 출력은 ERB의 기본 escaping을 유지하고 사용자 문자열에 html_safe를 호출하지 않는다. Markdown을 허용하지 않는 댓글이라면 단순 텍스트로 유지하는 편이 공격 면과 렌더링 규칙을 줄여 준다.
비동기 흐름은 system test로 확인한다
controller test에서는 Turbo Stream 응답의 action과 target을 검사한다. system test에서는 사용자가 보는 결과를 확인한다.
- 유효한 댓글을 제출하면 새 항목이 한 번만 나타난다.
- 제출 후 입력 폼이 비워지고 성공 안내가 노출된다.
- 빈 본문은 저장되지 않으며 같은 페이지에 오류가 보인다.
- honeypot이 채워진 요청은 성공처럼 응답하지만 DB 행은 늘지 않는다.
- 제한을 초과하면
429상태와 재시도 안내가 나타난다. - 일반 HTML 요청도 redirect 후 작성한 댓글을 보여 준다.
Turbo 폼 제출 직후 다른 페이지로 이동하는 테스트는 경쟁 상태를 만들 수 있다. 먼저 댓글 DOM 또는 redirect 목적지를 assert해 비동기 응답 완료를 기다린 다음 다음 탐색을 수행한다. 이 한 단계가 간헐적으로 실패하는 system test를 크게 줄여 준다.
댓글
아직 댓글이 없습니다.
댓글 남기기