액션 텍스트 개요
이 가이드는 풍부한 텍스트 콘텐츠 처리를 시작하는 데 필요한 모든 것을 제공합니다.
이 가이드를 읽고 나면 다음을 알게 될 것입니다:
- 액션 텍스트가 무엇이며 어떻게 설치하고 구성하는지.
- 풍부한 텍스트 콘텐츠를 어떻게 생성, 렌더링, 스타일링 및 사용자 정의하는지.
- 첨부 파일을 어떻게 처리하는지.
액션 텍스트란 무엇인가?
액션 텍스트는 풍부한 텍스트 콘텐츠의 처리와 표시를 용이하게 합니다. 풍부한 텍스트 콘텐츠는 굵게, 기울임, 색상, 하이퍼링크 등의 서식 요소를 포함하는 텍스트로, 일반 텍스트보다 시각적으로 향상되고 구조화된 표현을 제공합니다. 이를 통해 풍부한 텍스트 콘텐츠를 생성하고 테이블에 저장한 다음 모델에 연결할 수 있습니다.
액션 텍스트에는 Trix라는 WYSIWYG 편집기가 포함되어 있으며, 이는 사용자에게 풍부한 텍스트 콘텐츠를 생성하고 편집할 수 있는 사용자 친화적인 인터페이스를 제공하는 데 사용됩니다. 텍스트 서식, 링크 또는 인용 추가, 이미지 삽입 등 다양한 기능을 처리합니다. Trix 편집기의 예시는 여기에서 확인할 수 있습니다.
Trix 편집기에 의해 생성된 풍부한 텍스트 콘텐츠는 RichText 모델에 저장되며, 이 모델은 애플리케이션의 기존 Active Record 모델과 연결될 수 있습니다. 또한 포함된 이미지(또는 다른 첨부 파일)는 Active Storage(종속성으로 추가됨)를 사용하여 자동으로 저장되고 해당 RichText 모델과 연결됩니다. 콘텐츠를 렌더링할 때 액션 텍스트는 먼저 콘텐츠를 살균하여 페이지의 HTML에 안전하게 포함할 수 있도록 합니다.
정보: 대부분의 WYSIWYG 편집기는 HTML의 contenteditable
및 execCommand
API를 래핑합니다. 이러한 API는 Internet Explorer 5.5에서 웹 페이지의 실시간 편집을 지원하기 위해 Microsoft에 의해 설계되었습니다. 이후 다른 브라우저에 의해 역공학되어 복사되었습니다. 결과적으로 이러한 API는 완전히 명시되거나 문서화되지 않았으며, WYSIWYG HTML 편집기의 범위가 매우 넓기 때문에 각 브라우저의 구현에는 자체적인 버그와 문제점이 있습니다. 따라서 JavaScript 개발자들은 이러한 불일치를 해결해야 합니다.
Trix는 이러한 불일치를 피하기 위해 contenteditable
을 I/O 장치로 취급합니다: 입력이 편집기에 도달하면 Trix는 해당 입력을 내부 문서 모델에 대한 편집 작업으로 변환한 다음 해당 문서를 다시 편집기에 렌더링합니다. 이를 통해 Trix는 매 키 입력 후에 발생하는 일을 완전히 제어할 수 있으며 execCommand
와 관련된 불일치를 피할 수 있습니다.
설치
액션 텍스트를 설치하고 풍부한 텍스트 콘텐츠 작업을 시작하려면 다음을 실행하세요:
$ bin/rails action_text:install
이 명령은 다음을 수행합니다:
trix
와@rails/actiontext
의 JavaScript 패키지를 설치하고application.js
에 추가합니다.- 포함된 이미지와 다른 첨부 파일의 분석 및 변환을 위해
image_processing
gem을 추가합니다. Active Storage 개요 가이드를 참조하세요. - 풍부한 텍스트 콘텐츠와 첨부 파일을 저장하는 다음 테이블을 만드는 마이그레이션을 추가합니다:
action_text_rich_texts
,active_storage_blobs
,active_storage_attachments
,active_storage_variant_records
. actiontext.css
를 생성하고application.css
에 포함합니다. Trix 스타일시트도application.css
파일에 포함됩니다.- 액션 텍스트 콘텐츠와 Active Storage 첨부 파일(blob)을 렌더링하는 기본 뷰 partial
_content.html
와_blob.html
을 추가합니다.
그 후 마이그레이션을 실행하면 새로운 action_text_*
와 active_storage_*
테이블이 앱에 추가됩니다:
$ bin/rails db:migrate
액션 텍스트 설치 시 action_text_rich_texts
테이블을 생성할 때는 다형성 관계를 사용합니다. 따라서 여러 모델이 풍부한 텍스트 속성을 추가할 수 있습니다. 이는 record_type
과 record_id
열을 통해 수행되며, 각각 모델의 ClassName과 레코드 ID를 저장합니다.
정보: 다형성 연관에서 모델은 단일 연관에서 여러 다른 모델에 속할 수 있습니다. Active Record 연관 가이드에서 자세히 알아보세요.
따라서 액션 텍스트 콘텐츠를 포함하는 모델이 식별자로 UUID 값을 사용하는 경우, 액션 텍스트 속성을 사용하는 모든 모델이 고유 식별자로 UUID 값을 사용해야 합니다. 액션 텍스트에 대해 생성된 마이그레이션도 레코드 참조 줄에 type: :uuid
를 지정하도록 업데이트해야 합니다.
t.references :record, null: false, polymorphic: true, index: false, type: :uuid
풍부한 텍스트 콘텐츠 생성하기
이 섹션에서는 풍부한 텍스트를 생성하는 데 필요한 구성을 살펴봅니다.
RichText 레코드는 Trix 편집기에서 생성된 콘텐츠를 직렬화된 body
속성에 저장합니다. 또한 Active Storage를 사용하여 저장된 포함된 파일에 대한 모든 참조를 보유합니다. 이 레코드는 풍부한 텍스트 콘텐츠를 가지려는 Active Record 모델과 연결됩니다. 연결은 풍부한 텍스트를 추가하려는 모델에 has_rich_text
클래스 메서드를 배치하여 수행됩니다.
# app/models/article.rb class Article < ApplicationRecord has_rich_text :content end
참고: Article 테이블에 content
열을 추가할 필요가 없습니다. has_rich_text
는 action_text_rich_texts
테이블과 연결하고 모델에 다시 연결합니다. 속성 이름을 content
와 다르게 지정할 수도 있습니다.
모델에 has_rich_text
클래스 메서드를 추가하면 뷰에서 해당 필드에 대한 풍부한 텍스트 편집기(Trix)를 사용할 수 있습니다. 이를 위해 rich_text_area
폼 필드를 사용합니다.
<%# app/views/articles/_form.html.erb %> <%= form_with model: article do |form| %> <div class="field"> <%= form.label :content %> <%= form.rich_text_area :content %> </div> <% end %>
이렇게 하면 풍부한 텍스트 콘텐츠를 생성하고 업데이트할 수 있는 Trix 편집기가 표시됩니다. 나중에 편집기 스타일 제거 또는 추가에 대해 자세히 살펴보겠습니다.
마지막으로 편집기의 업데이트를 수락하려면 관련 컨트롤러에서 참조된 속성을 매개변수로 허용해야 합니다:
class ArticlesController < ApplicationController def create article = Article.create! params.require(:article).permit(:title, :content) redirect_to article end end
has_rich_text
를 사용하는 클래스 이름을 변경해야 하는 경우, action_text_rich_texts
테이블의 다형성 유형 열 record_type
에 대해서도 해당 행을 업데이트해야 합니다.
액션 텍스트는 다형성 연관에 의존하므로, 이는 데이터베이스에 클래스 이름을 저장합니다. 따라서 Ruby 코드의 클래스 이름과 저장된 데이터 간의 동기화를 유지하는 것이 중요합니다. 이 동기화는 코드베이스의 클래스 참조와 저장된 데이터 간의 일관성을 유지하는 데 필수적입니다.
풍부한 텍스트 콘텐츠 렌더링하기
ActionText::RichText
인스턴스는 이미 콘텐츠를 안전하게 살균했기 때문에 페이지에 직접 포함될 수 있습니다. 다음과 같이 콘텐츠를 표시할 수 있습니다:
<%= @article.content %>
ActionText::RichText#to_s
는 RichText를 안전한 HTML 문자열로 변환합니다. 반면 ActionText::RichText#to_plain_text
는 HTML 안전이 아닌 문자열을 반환하므로 브라우저에 렌더링해서는 안 됩니다. ActionText::RichText
클래스의 문서에서 액션 텍스트의 살균 프로세스에 대해 자세히 알아볼 수 있습니다.
참고: content
필드에 첨부된 리소스가 있는 경우 Active Storage에 필요한 종속성이 설치되어 있지 않으면 제대로 표시되지 않을 수 있습니다.
풍부한 텍스트 콘텐츠 편집기(Trix) 사용자 정의하기
편집기의 표현을 스타일 요구 사항에 맞게 업데이트해야 할 때가 있습니다. 이 섹션에서는 그 방법을 안내합니다.
Trix 스타일 제거 또는 추가하기
기본적으로 액션 텍스트는 .trix-content
클래스가 있는 요소 내에 풍부한 텍스트 콘텐츠를 렌더링합니다. 이는 app/views/layouts/action_text/contents/_content.html.erb
에 설정됩니다. 이 클래스가 있는 요소는 trix 스타일시트에 의해 스타일링됩니다.
trix 스타일을 업데이트하려면 app/assets/stylesheets/actiontext.css
에 사용자 정의 스타일을 추가할 수 있습니다.
그러나 기본 trix 스타일시트 대신 자체 스타일을 제공하거나 타사 라이브러리를 사용하려면 app/assets/stylesheets/actiontext.css
파일의 전처리기 지시문에서 trix를 제거할 수 있습니다:
= require trix
편집기 컨테이너 사용자 정의하기
풍부한 텍스트 콘텐츠 주변에 렌더링되는 HTML 컨테이너 요소를 사용자 정의하려면 설치 네, 계속해서 풍부한 텍스트 콘텐츠 편집기(Trix) 사용자 정의 방법을 설명하겠습니다.
임베디드 이미지 및 첨부 파일에 대한 HTML 사용자 정의하기
임베디드 이미지와 다른 첨부 파일(blob이라고 함)에 대해 렌더링되는 HTML을 사용자 정의하려면 설치 프로그램이 생성한 app/views/active_storage/blobs/_blob.html.erb
템플릿을 편집하세요:
<%# app/views/active_storage/blobs/_blob.html.erb %> <figure class="attachment attachment--<%= blob.representable? ? "preview" : "file" %> attachment--<%= blob.filename.extension %>"> <% if blob.representable? %> <%= image_tag blob.representation(resize_to_limit: local_assigns[:in_gallery] ? [ 800, 600 ] : [ 1024, 768 ]) %> <% end %> <figcaption class="attachment__caption"> <% if caption = blob.try(:caption) %> <%= caption %> <% else %> <span class="attachment__name"><%= blob.filename %></span> <span class="attachment__size"><%= number_to_human_size blob.byte_size %></span> <% end %> </figcaption> </figure>
첨부 파일
현재 액션 텍스트는 Active Storage를 통해 업로드된 첨부 파일과 Signed GlobalID에 연결된 첨부 파일을 지원합니다.
Active Storage
풍부한 텍스트 편집기 내에서 이미지를 업로드하면 액션 텍스트가 이를 사용하고 Active Storage를 사용합니다. 그러나 Active Storage에는 Rails에서 제공되지 않는 일부 종속성이 있습니다. 내장 미리보기 기능을 사용하려면 이러한 라이브러리를 설치해야 합니다.
이러한 라이브러리 중 일부는 필수이며, 편집기 내에서 예상되는 업로드 유형에 따라 달라집니다. 액션 텍스트와 Active Storage를 사용할 때 사용자가 겪는 일반적인 오류 중 하나는 편집기에 이미지가 올바르게 렌더링되지 않는 것입니다. 이는 일반적으로 libvips
종속성이 설치되지 않았기 때문입니다.
Signed GlobalID
Active Storage를 통한 첨부 파일 외에도 액션 텍스트는 Signed GlobalID로 확인할 수 있는 모든 것을 포함할 수 있습니다.
Global ID는 모델 인스턴스를 고유하게 식별하는 앱 전체 URI입니다: gid://YourApp/Some::Model/id
. 이는 다양한 클래스의 객체를 참조하는 단일 식별자가 필요할 때 유용합니다.
이 방법을 사용할 때 액션 텍스트는 첨부 파일에 서명된 global ID(sgid)가 필요합니다. 기본적으로 Rails 앱의 모든 Active Record 모델은 GlobalID::Identification
관심사를 믹스인하므로 서명된 global ID로 확인될 수 있으며 따라서 ActionText::Attachable
호환 가능합니다.
액션 텍스트는 나중에 최신 콘텐츠를 다시 렌더링할 수 있도록 저장 시 삽입한 HTML을 참조합니다. 이를 통해 레코드가 변경될 때 항상 현재 콘텐츠를 표시할 수 있습니다.
액션 텍스트는 global ID를 로드하고 기본 partial 경로로 해당 인스턴스를 렌더링합니다.
액션 텍스트 첨부 파일은 다음과 같이 보일 수 있습니다:
<action-text-attachment sgid="BAh7CEkiCG…"></action-text-attachment>
액션 텍스트는 <action-text-attachment>
요소의 sgid 속성을 해결하여 인스턴스를 렌더링합니다. 해결되면 해당 인스턴스가 렌더링 헬퍼에 전달됩니다. 그 결과 HTML은 <action-text-attachment>
요소의 자손으로 포함됩니다.
<action-text-attachment>
요소 내에 첨부 파일로 렌더링되려면 ActionText::Attachable
모듈을 포함해야 합니다. 이 모듈은 GlobalID::Identification
관심사를 통해 제공되는 #to_sgid(**options)
를 구현합니다.
또한 선택적으로 #to_attachable_partial_path
를 선언하여 사용자 정의 partial 경로를 렌더링하고 #to_missing_attachable_partial_path
를 선언하여 누락된 레코드를 처리할 수 있습니다.
다음은 예시입니다:
class Person < ApplicationRecord include ActionText::Attachable end person = Person.create! name: "Javan" html = %Q(<action-text-attachment sgid="#{person.attachable_sgid}"></action-text-attachment>) content = ActionText::Content.new(html) content.attachables # => [person]
Action Text 첨부 파일 렌더링하기
<action-text-attachment>
가 기본적으로 렌더링되는 방식은 기본 경로 partial을 통해서입니다.
이를 좀 더 자세히 살펴보기 위해 User 모델을 고려해 보겠습니다:
# app/models/user.rb class User < ApplicationRecord has_one_attached :avatar end user = User.find(1) user.to_global_id.to_s #=> gid://MyRailsApp/User/1 user.to_signed_global_id.to_s #=> BAh7CEkiCG…
참고: .find(id)
클래스 메서드가 있는 모델에 GlobalID::Identification
을 믹스인할 수 있습니다. 지원은 Active Record에 자동으로 포함됩니다.
위의 코드는 모델 인스턴스를 고유하게 식별하는 식별자를 반환합니다.
다음으로 User 인스턴스의 서명된 GlobalID를 참조하는 <action-text-attachment>
요소가 포함된 풍부한 텍스트 콘텐츠를 고려해 보겠습니다:
<p>Hello, <action-text-attachment sgid="BAh7CEkiCG…"></action-text-attachment>.</p>
액션 텍스트는 “BAh7CEkiCG…"문자열을 사용하여 User 인스턴스를 확인합니다. 그런 다음 콘텐츠를 렌더링할 때 기본 partial 경로로 렌더링합니다.
이 경우 기본 partial 경로는 users/user
partial입니다:
<%# app/views/users/_user.html.erb %> <span><%= image_tag user.avatar %> <%= user.name %></span>
따라서 액션 텍스트가 렌더링한 최종 HTML은 다음과 같습니다:
<p>Hello, <action-text-attachment sgid="BAh7CEkiCG…"><span><img src="..."> Jane Doe</span></action-text-attachment>.</p>
action-text-attachment에 대한 다른 partial 렌더링하기
첨부 가능한 항목에 대해 다른 partial을 렌더링하려면 User#to_attachable_partial_path
를 정의하세요:
class User < ApplicationRecord def to_attachable_partial_path "users/attachable" end end
그런 다음 해당 partial을 선언합니다. User 인스턴스는 user partial-local 변수로 사용할 수 있습니다:
<%# app/views/users/_attachable.html.erb %> <span><%= image_tag user.avatar %> <%= user.name %></span>
확인되지 않은 인스턴스 또는 누락된 action-text-attachment에 대한 partial 렌더링하기
액션 텍스트가 User 인스턴스를 확인할 수 없는 경우(예: 레코드가 삭제된 경우), 기본 대체 partial이 렌더링됩니다.
누락된 첨부 파일 partial을 렌더링하려면 클래스 수준 to_missing_attachable_partial_path
메서드를 정의하세요:
class User < ApplicationRecord def self.to_missing_attachable_partial_path "users/missing_attachable" } end
그런 다음 해당 partial을 선언합니다.
<%# app/views/users/missing_attachable.html.erb %> <span>Deleted user</span>
API를 통한 첨부 가능
아키텍처가 전통적인 Rails 서버 측 렌더링 패턴을 따르지 않는 경우 백엔드 API(예: JSON 사용)를 사용해야 할 수 있으며, 이 경우 파일 업로드를 위한 별도의 엔드포인트가 필요할 것입니다. 이 엔드포인트는 ActiveStorage::Blob
을 생성하고 attachable_sgid
를 반환해야 합니다:
{ "attachable_sgid": "BAh7CEkiCG…" }
그런 다음 attachable_sgid
를 가져와 프런트엔드 코드에서 <action-text-attachment>
태그를 사용하여 풍부한 텍스트 콘텐츠에 삽입할 수 있습니다:
<action-text-attachment sgid="BAh7CEkiCG…"></action-text-attachment>
기타
N+1 쿼리 방지하기
종속 ActionText::RichText
모델을 미리 로드하려면 이름이 지정된 범위를 사용할 수 있습니다. 풍부한 텍스트 필드 이름이 content
라고 가정하면 다음과 같이 사용할 수 있습니다:
Article.all.with_rich_text_content # 첨부 파일 없이 본문 미리 로드 Article.all.with_rich_text_content_and_embeds # 본문과 첨부 파일 모두 미리 로드