Rails의 프래그먼트 캐싱은 페이지의 더 큰 부분이 캐싱될 때 더 큰 속도 향상을 생성합니다. 동적 콘텐츠나 사용자별 콘텐츠가 많은 페이지에서는 더 어렵습니다. 해결책은 hagelslag와 같은 "자바스크립트 스프링클"을 사용하는 것입니다. , 그러나 초콜릿 없이 페이지의 나머지 부분이 캐시에서 직접 제공된 후 사용자별 콘텐츠를 로드하기 위한 추가 요청이 있습니다.
프래그먼트 캐싱
프래그먼트 캐싱과 같은 기술은 렌더링된 페이지의 프래그먼트를 캐싱하여 Rails 애플리케이션의 응답 속도를 높이는 데 사용됩니다. 스마트 캐시 키를 사용하면 보기에 표시된 데이터가 업데이트되기 때문에 콘텐츠가 변경될 때 조각이 자동으로 무효화됩니다.
캐싱 뷰 조각은 캐싱된 조각이 더 자주 사용될 때 더 작은 캐시에서 더 큰 속도 향상을 제공합니다. 예를 들어 페이지의 콘텐츠가 현재 로그인한 사용자에 따라 달라지면 페이지의 조각을 캐싱하는 것이 더 어려워집니다.
읽지 않은 응답
예를 들어 Rails 기반 블로그를 살펴보겠습니다. 이 블로그에서 각 기사에는 여러 응답이 있을 수 있습니다. 사용자가 자신의 계정으로 로그인하고 응답을 게시할 수 있도록 인증 시스템이 추가되었습니다. 각 기사 페이지에는 응답이 나열됩니다. 사용자가 더 쉽게 사용할 수 있도록 아이콘과 다른 배경색으로 새 응답을 표시합니다.
각 기사를 cache
에 래핑하면 차단하면 잘못된 응답을 읽지 않은 것으로 표시할 위험이 있습니다. 사용자가 인덱스 페이지를 요청하면 응답이 캐시됩니다. 다른 사용자가 나중에 같은 페이지를 요청하면 첫 번째 사용자의 읽지 않은 응답과 함께 캐시된 조각을 받게 됩니다.
# app/views/articles/show.html.erb
<%= cache(@article) do %>
<h1><%= @article.title %></h1>
<%= simple_format(@article.content) %>
<section id="responses">
<h2>Responses</h2>
<% @article.responses.each do |response| %>
<div class="<%= response.read_by?(@current_user) ? 'read' : 'unread' %>">
<%= time_tag(response.created_at) %>
<strong><%= response.name %></strong>: <%= response.content %>
</div>
<% end %>
</section>
<% end %>
이를 해결하는 방법은 [@article, @current_user]
를 사용하여 현재 로그인한 사용자를 캐시 키에 추가하는 것입니다. @article
대신 cache
에 전달된 인수로 도우미 메서드입니다.
# app/views/articles/show.html.erb
<%= cache([@article, @current_user]) do %>
<h1><%= @article.title %></h1>
# ...
<% end %>
이렇게 하면 각 사용자가 자신의 읽지 않은 응답을 볼 수 있지만 기사 조각이 이제 각 사용자에 대해 별도로 캐시되기 때문에 조각 캐싱으로 인한 속도 향상의 대부분을 무효화합니다.
자바스크립트 스프링클
대부분의 페이지는 모든 사용자에게 동일하므로 각 방문자에 대해 캐시된 기사 조각을 재사용하고 싶습니다. 이를 위해 캐시에서 기사를 로드하고 JavaScript 요청을 통해 페이지가 로드된 후 사용자별 콘텐츠를 가져오는 추가 요청을 추가할 수 있습니다. 이를 위해 JavaScript 기능을 사용하면 몇 가지 이점이 있습니다.
- 페이지를 한 번 캐시하면 모든 사용자에 대해 조각을 별도로 캐시할 필요 없이 인증되지 않은 게스트 및 다른 사용자에 대해 재사용할 수 있습니다.
- 가장 빠른 응답 시간을 위해 가장 중요한 콘텐츠가 먼저 로드되고 읽지 않은 수와 같은 보조 기능은 나중에 로드됩니다.
- 추가 요청이 JavaScript를 통해 수행되기 때문에 전체 페이지를 CDN에서 에지 캐싱하여 성능을 더욱 향상시킬 수 있습니다.
정리
캐시하기 쉽도록 페이지에서 동적 콘텐츠를 먼저 제거합니다. @current_user
를 제거하겠습니다. cache
의 캐시 키에서 다시 차단하여 더 이상 각 사용자에 대해 캐시되지 않습니다. 그런 다음 컨트롤러에서 읽지 않은 수를 찾는 쿼리를 제거하고 뷰에서 CSS 클래스 이름을 제거합니다.
# app/views/articles/show.html.erb
<%= cache(@article) do %>
<h1><%= @article.title %></h1>
<%= simple_format(@article.content) %>
<section id="responses">
<h2>Responses</h2>
<% @article.responses.each do |response| %>
<div data-response-id="<%= response.id %>">
<%= time_tag(response.updated_at) %>
<strong><%= response.name %></strong>: <%= response.content %>
</div>
<% end %>
</section>
<% end %>
캐시하기는 쉽지만 읽지 않은 응답 기능이 없는 일반 페이지가 남았습니다. 다시 추가해 보겠습니다.
엔드포인트
먼저 사용자의 읽지 않은 응답을 찾기 위해 끝점을 만듭니다. 페이지가 렌더링된 후 현재 상태를 변경하고 싶으므로 페이지에서 JSON을 요청할 것입니다.
# app/controllers/unread_responses_controller.rb
class UnreadResponsesController < ApplicationController
def index
@article = Article.find(params[:article_id])
@responses = @article.unread_responses_for(@current_user)
end
end
# app/views/unread_responses/index.json.jbuilder
json.array! @responses do |response|
json.extract! response, :id
end
# config/routes.rb
Rails.application.routes.draw do
resources :articles do
resources :responses
resources :unread_responses
end
end
엔드포인트는 읽지 않은 응답 ID 목록을 생성합니다.
# GET /articles/1/unread_responses.json
[{"id":1},{"id":2},{"id":3}]
팁 :서버에서 미리 렌더링할 수 있는 동적 구성 요소를 로드할 때 일반적으로 서버 측에서 HTML 렌더링을 수행한 다음 JavaScript를 통해 HTML을 페이지에 직접 삽입하는 것이 더 빠릅니다.
읽지 않은 응답 표시
JavaScript 코드에서 읽지 않은 응답 끝점에 대한 URL을 하드코딩하는 대신 뷰의 데이터 속성에 추가하여 나중에 참조할 수 있습니다.
# app/views/articles/show.html.erb
<section id="responses" data-url="<%= article_unread_responses_path(@article, json: true) %>">
# ...
</section>
페이지 로드가 완료되면 새 엔드포인트에서 읽지 않은 응답 ID를 요청합니다. 그런 다음 해당 목록의 데이터를 사용하여 CSS 클래스를 추가하여 각 기사를 읽지 않은 것으로 표시합니다.
// app/assets/javascripts/application.js
document.addEventListener("turbolinks:load", function () {
responses = document.getElementById("responses");
if (!responses.dataset.loaded) {
Rails.ajax({
url: responses.dataset.url,
type: "GET",
success: function (data) {
responses.dataset.loaded = true;
data.forEach(function (response) {
element = document.querySelector(
"[data-response-id='" + response.id + "']"
);
element.classList.add("unread");
});
},
});
}
});
Rails 애플리케이션은 Turbolinks를 사용하기 때문에 turbolinks:load
를 수신하여 페이지가 로드될 때까지 기다립니다. 이벤트. 해당 이벤트가 발생하면 해당 ID를 사용하여 응답 상자를 찾습니다.
그런 다음 응답 요소에 loaded
가 있는지 확인합니다. 데이터 속성. 나중에 읽지 않은 응답을 업데이트한 후에 이 속성을 설정하므로 브라우저의 뒤로 버튼을 사용하여 페이지를 다시 로드하는 경우 추가 요청을 하지 않습니다.
첫 번째 로드에서 loaded
속성이 아직 설정되지 않았으므로 계속해서 엔드포인트에 요청합니다. 성공하면 반환된 결과의 각 기사를 반복하고 ID로 응답의 요소를 찾은 다음 "읽지 않은" CSS 클래스를 여기에 추가합니다.
뿌린다!
재사용 가능한 콘텐츠를 프래그먼트 캐싱하고 나중에 사용자별 동적 비트를 추가하기 위해 페이지에 JavaScript를 뿌리면 캐시에서 직접 대부분의 중요한 콘텐츠를 제공함으로써 앱의 초기 응답 시간을 더 빠르게 할 수 있습니다. 기본 콘텐츠가 로드된 후 추가 요청을 수행하고 페이지를 업데이트하는 데 시간이 걸리지만 동적 콘텐츠를 연기하면 전체 요청이 사용자 특정 부분을 포함한 모든 것을 제공하는 것보다 더 오래 걸리더라도 앱이 더 빠르게 느껴집니다. 캐시.
더 정교한 설정을 위해 HTML 보기를 JavaScript에 연결하는 프레임워크에 스프링클 패턴을 래핑하는 JavaScript 라이브러리인 Stimulus를 확인하십시오.
Rails 애플리케이션의 JavaScript 스프링클에 대한 이 소개가 마음에 드셨기를 바랍니다. 이 기사, 블로그 또는 다루고 싶은 다른 주제에 대해 의견이 있으시면 @AppSignal로 연락해 주십시오.