티스토리 뷰

해당 글은 https://blog.naver.com/kbs4674/221182443309 로 부터 게시글이 이전되었습니다.

 

댓글에 또 다른 답 댓글, 즉 중첩댓글에 대한 글을 다룹니다.

 

  • 중첩댓글 : act as commentable with threading

 참고 

  • Post Scaffold에서 댓글을 구축하는 예시로 설명함.

  • Devise Gem을 기본적으로 사용한다는 가정 하에 사용하셔야 합니다. [참고]


1.  Gemfile  에 다음 내용을 입력해줍니다.

gem 'acts_as_commentable_with_threading'

새로운 Gem을 설치해 주세요.

bundle install

 

2. 다음 명령어를 입력해서 Model과 DB(Migration 파일)를 생성해 줍니다.

rails generate acts_as_commentable_with_threading_migration

새로운 모델 파일과 테이블 파일이 생성되는걸 확인할 수 있습니다.

 

3. 새로 생성된 파일 중, 테이블 파일을 보면 Rails 버전이 없습니다.

class ActsAsCommentableWithThreadingMigration < ActiveRecord::Migration

제목 마지막에 자신의 레일즈에 맞는 버전으로 내용을 수정합니다.

저는 Rails 5.2 버전이라서 아래와 같이 수정했습니다.

class ActsAsCommentableWithThreadingMigration < ActiveRecord::Migration[5.2]

그리고 테이블 파일에서 아래와 같이 컬럼을 추가해주세요. (댓글 작성자 이메일 주소를 담을 컬럼)

class ActsAsCommentableWithThreadingMigration < ActiveRecord::Migration[5.2]
  def self.up
    create_table :comments, :force => true do |t|
      t.string :nickname
      ...
  end
 
  def self.down
    drop_table :comments
  end
end

 

4. 게시판 역할을 맡을 Post scaffold를 생성합니다.

rails g scaffold Post title content:text user:references

 

5. DB를 Migrate 합니다.

rake db:migrate

 

6 댓글을 적용하려는 게시판 모델에 가셔서 다음 내용을 입력해주세요.

 참고  Post Scaffold에 댓글을 적용하는 예시로 설명하겠습니다.

class Post < ApplicationRecord
  ...
  belongs_to :user
  acts_as_commentable
end

 

7.  app/controllers  에서  comments_controller.rb  파일을 생성 후, 다음 내용을 입력해주세요!

class CommentsController < ApplicationController
  before_action :authenticate_user!
 
  def create
    commentable = commentable_type.constantize.find(commentable_id)
    @comment = Comment.build_from(commentable, current_user.id, body)
    @comment.nickname = params[:comment][:nickname]
    
    respond_to do |format|
      if @comment.save
        make_child_comment
        format.html  { redirect_to(request.referrer, :notice => '댓글이 작성되었습니다.') }
      else
        format.html  { redirect_to(request.referrer, :alert => '댓글 내용을 작성해주세요.') }
      end
    end
  end
 
  def destroy
      @comment = Comment.find_by(id: params[:id])
      @comment.delete
      respond_to do |format|
        format.html { redirect_to(request.referrer, :notice => '댓글이 삭제되었습니다.')}
        format.js
      end
  end
 
  private
 
  def comment_params
    params[:comment][:nickname] = current_user.email
    params.require(:comment).permit(:body, :commentable_id, :commentable_type, :comment_id, :user_id, :nickname)
  end
 
  def commentable_type
    comment_params[:commentable_type]
  end
 
  def commentable_id
    comment_params[:commentable_id]
  end
 
  def comment_id
    comment_params[:comment_id]
  end
 
  def body
    comment_params[:body]
  end
 
  def make_child_comment
    return "" if comment_id.blank?
 
    parent_comment = Comment.find comment_id
    @comment.move_to_child_of(parent_comment)
  end
end

 Attribute 명칭 참고 

1) commentable_type 

대댓글이 작성될 때 당시 게시글의 Model 이름에 대한 정보

 

2) commentable_id

게시글이 작성된 Model의 id값 (위 예제는 Post 모델의 3번 게시글에 작성된 댓글들임.)

 

3) parent_id

대댓글에서만 보이는 기록으로, 어떤 id의 댓글에서 대댓글이 달렸는지 알 수 있습니다.


8.  app/controllers/posts_controller.rb  파일에서 show 액션 내부에 다음 내용을 입력해주세요!

def show
    ...
    @new_comment  = Comment.build_from(@post, current_user.id, "")
end

 

9.   app/views/  에  comments  라는 폴더를 생성해낸 뒤,  app/views/comments  폴더 안에 다음 파일들을 생성 후 코드를 입력해주세요.

1) app/views/comments/_form.html.erb

<%= form_for @new_comment do |f| %>  
    <%= f.hidden_field :commentable_id, value: @new_comment.commentable_id %>
    <%= f.hidden_field :commentable_type, value: @new_comment.commentable_type %>
    <div class="field form-group">
        <%= f.text_area :body, class: 'form-control' %>
    </div>
    
    <div class="field form-group">
        <%= submit_tag "댓글 작성", style: "float: right" %><br/>
    </div>
<% end %>

 

2) app/views/comments/_reply.html.erb

<% comments.each do |comment| %>  
    <div class="comments-show" id="comment<%= comment.id %>">
        <div class="comment">
            <%= comment.user != nil ? comment.user.email : comment.email %> / <%= time_ago_in_words(comment.created_at) %>전 [ <%= comment.created_at.strftime('%Y-%m-%d %H:%M') %> ] /
            <%= link_to "지우기", comment_path(comment), method: :delete, remote: false, data: { confirm: "정말로 지우시겠습니까?" }, style: "color: red; font-weight: bold" %>
            <%= content_tag(:div, comment.body, style: "white-space: pre-wrap;") %>
            
            <div class="comment-nav" align="right">
                <a onclick="showHide('comment_reply_<%= comment.id %>')" onfocus="this.blur()">답댓글</a>
            </div>
            
            <div id="comment_reply_<%= comment.id %>" style="display:none;">
                <div class="reply-form">
                    <%= form_for @new_comment do |f| %>
                        <%= f.hidden_field :commentable_id, value: @new_comment.commentable_id %>
                        <%= f.hidden_field :commentable_type, value: @new_comment.commentable_type %>
                        <%= f.hidden_field :comment_id, value: comment.id %>
                        <div class="field form-group">
                        <%= f.text_area :body, class: 'form-control' %>
                        </div>
                        <div class="field form-group" style="margin-bottom: 60px">
                            <%= submit_tag "답댓글 작성", style: "float: right;" %>
                        </div>
                    <% end %>
                </div>
            </div>
        </div>
        <div style="margin-left: 100px;">
            <%= render partial: "comments/reply_end", locals: {comments: comment.children} %>
        </div>
    </div>
    <hr/>
<% end %>

 

3) app/views/comments/_template.html.erb

<div class="comments-header">  
    <h4>댓글 (<%= commentable.comment_threads.count %>)</h4>
</div>
 
<div class="comments-container">  
    <%= render partial: "comments/reply", locals: {comments: commentable.root_comments} %>
</div>
<%= render partial: "comments/form", locals: {new_comment: new_comment} %>

 

4) app/views/comments/_reply_end.html.erb

<% comments.each do |comment| %>  
    <div class="comments-show">
        <div class="comment">
            <%= comment.user != nil ? comment.user.email : comment.email %> / <%= time_ago_in_words(comment.created_at) %>전 [ <%= comment.created_at.strftime('%Y-%m-%d %H:%M') %> ] /
            <%= link_to "지우기", comment_path(comment), method: :delete, remote: false, data: { confirm: "정말로 지우시겠습니까?" }, style: "color: red; font-weight: bold" %>
            <%= content_tag(:div, comment.body, style: "white-space: pre-wrap;") %>
        </div>
    </div>
<% end %>

 

10. 각 파일별로 제시되는 내용을 원하는 위치에 입력해주세요!

1) app/views/posts/show.html.erb

<%= render partial: "comments/template", locals: {commentable: @post, new_comment: @comment} %>

 참고  위 코드는 게시글에 작성된 댓글들을 보여줍니다.

 

2) app/views/posts/index.html.erb

<% @posts.each do |post| %>
    ...
    <%= post.comment_threads.count %>
    ...
<% end %>

 참고  위 코드는 댓글 갯수를 보여줍니다.

 

11. 라우터 규칙을 정의합니다.

##config/routes.rb

resources :comments, only: [:create, :destroy]

 

12. app/views/layouts/application.html.erb  에 다음 코드를 넣습니다. (답댓글 입력창을 을 접었다 폈다 할 수 있음.)

<script type="text/javascript">
    function showHide(id) {
       var obj = document.getElementById(id);
       if (obj.style.display == 'none')
           obj.style.display = 'block';
       else
           obj.style.display = 'none';
    }
</script>

 

13. 끝입니다!

이후 디자인은 여러분들 몫입니다!

 

 

 

  •  알아두면 좋은 지식  style: "white-space: pre-wrap;"

## 9번 과정의 4) 코드 내용 중

<%= content_tag(:div, comment.body, style: "white-space: pre-wrap;") %>

9번 과정에 입력한 코드 중, 작성된 댓글을 보여주는 위 코드가 있었습니다.

그리고 이 중,  style: "white-space: pre-wrap;"  이 있는데 이 기능에 대해 짚고 넘어가보자 합니다.

우리가 글을 작성을 할 때 엔터를 쳐야하는 순간이 있습니다.

엔터를 치고 글을 작성 후, 결과물을 보면

 

보통은 HTML 태그에서는 엔터 처리를 뜻하는  <br/>태그  를 넣지 않는 한, 엔터처리를 자동으로 해주지 않습니다.

위 사례와 같이 엔터처리를 위해 존재하는 코드가 바로 style 속성에서 제공해주는 white-space: pre-wrap; 입니다.

 

 style: "white-space: pre-wrap;"  을 입력 후, 결과를 보면

 

 

위와같이 결과가 잘 나오는게 확인됩니다.

그런데 어떤분은 이런 질문을 할 수 있습니다.

 

" Rails에서 제공하는 .html_safe 메소드를 쓰면 되지 않나요? "

 

.html_safe 메소드도 좋은 정답이긴 하나, 이미지 태그, audio 태그, meta 태그 등 불 필요한 태그의 사용도 허락을 하는 문제점이 있습니다.

반면에,  style: "white-space: pre-wrap;"  는 위 사진과 같이 <br/> 태그만을 허용합니다.

 

 

  •  참고  시간 오류가 뜰 경우

https://kbs4674.tistory.com/53#timeago 페이지의 Time-ago 과정을 따라하시면 됩니다.

 

 

  • 알림잼 기능 연동하기

https://kbs4674.tistory.com/76 과정을 참고해주세요.

 

 

  •  참고  Ajax 비동기 처리를 통한 댓글 추가 및 제거

비동기 처리를 이용한 댓글 처리

(제가 댓글을 쓴다해서 남들에게까지 실시간적으로 보여지는건 아닙니다.)

 

저희는 위 예시와 같이 비동기 처리를 통해 댓글이 새로고침 없이 작동되게 하고자 합니다!

 

1.  app/views/comments  폴더에  create.js.erb  파일과  destroy.js.erb  파일을 생성합니다.

 참고  create.js.erb 및 destroy.js.erb는  app/controllrs/comments_controller.rb  에서 따온 이름입니다.

다른 이름으로 쓰면 안됩니다.

 

2. 각 파일 별로 코드를 작성해주세요.

1) app/views/comments/create.js.erb

<% if @comment.parent == nil %> <%# 댓글에 대한 view 처리 %>
    $("#comment-ajax").append("<%= escape_javascript render 'comments/comment_ajax', comment: @comment %>");
<% else %> <%# 대댓글에 대한 view 처리 %>
    $("#reply-ajax<%= @comment.parent.id %>").append("<%= escape_javascript render 'comments/reply_ajax', comment: @comment, new_comment: @comment %>");
<% end %>
 
<%# 댓글 카운트가 동적으로 변화 %>
$('#comment_quantity').empty();
$('#comment_quantity').append('<%= @model_name.find("#{@model_id}").comment_threads.count %>');

 

2) app/views/comments/destroy.js.erb

$('#comment<%= @comment.id %>').remove(); <%# 삭제이벤트 발동 / 코멘트의 id값을 찾아서 댓글 제거 %>
 
<%# 댓글 카운트가 동적으로 변화 %>
$('#comment_quantity').empty();
$('#comment_quantity').append('<%= @model_name.find("#{@model_id}").comment_threads.count %>');

 

3.  app/controllers/comments_controller.rb  파일을 열고 코드를 수정해주세요.

 참고  + 코드 추가, - 코드 제거 혹은 주석처리 입니다.

class CommentsController < ApplicationController
  before_action :authenticate_user!
 
  def create
    commentable = commentable_type.constantize.find(commentable_id)
    @comment = Comment.build_from(commentable, current_user.id, body)
    @comment.nickname = params[:comment][:nickname]
    
    # 댓글 비동기 처리 (모델 정보, 게시글의 ID 값을 찾아냄)
+    @model_name = eval(commentable_type)
+    @model_id = eval(commentable_id)
+    @new_comment  = Comment.build_from(eval(commentable_type).find(commentable_id), current_user.id, "")
    
    respond_to do |format|
      if @comment.save
        make_child_comment
+        format.js { flash[:notice] = "댓글이 작성되었습니다." }
-        #format.html  { redirect_to("#{request.referrer}#comment#{@comment.id}", :notice => '댓글이 작성되었습니다.') }
        @comment.save
      else
        format.html  { redirect_to(request.referrer, :alert => '댓글 내용을 작성해주세요.') }
      end
    end
  end
 
  def destroy
    @comment = Comment.find_by(id: params[:id])
    
    # 댓글 비동기 처리 (모델 정보, 게시글의 ID 값을 찾아냄)
+    @model_name = eval(@comment.commentable_type)
+    @model_id = @comment.commentable_id
    
    @comment.destroy
    respond_to do |format|
-      # format.html { redirect_to(request.referrer, :notice => '댓글이 삭제되었습니다.') }
+      format.js { flash[:notice] = "댓글이 삭제되었습니다." }
    end
  end
 
  ...
end

 

4.  app/views/comments/_template.html.erb  파일에서 코드를 수정해주세요.

 참고  + 코드 추가, - 코드 제거 혹은 주석처리 입니다.

<div class="comments-header">  
-    <h4>댓글 (<%= commentable.comment_threads.count %>)</h4>
+    <h4>댓글 (<span id="comment_quantity"><%= commentable.comment_threads.count %></span>)</h4>
</div>
 
- <div class="comments-container">
+ <div class="comments-container" id="comment-ajax">
    <%= render partial: "comments/reply", locals: {comments: commentable.root_comments} %>
</div>
<%= render partial: "comments/form", locals: {new_comment: new_comment} %>

 

5.  app/views/comments/_form.html.erb  파일에서 코드를 수정해주세요.

 참고  + 코드 추가, - 코드 제거 혹은 주석처리 입니다.

- <%= form_for @new_comment do |f| %>  
+ <%= form_for @new_comment, remote: true, method: :post do |f| %>  
    <%= f.hidden_field :commentable_id, value: @new_comment.commentable_id %>
    <%= f.hidden_field :commentable_type, value: @new_comment.commentable_type %>
    <div class="field form-group">
        <%= f.text_area :body, class: 'form-control' %>
    </div>
    
    <div class="field form-group">
        <%= submit_tag "댓글 작성", style: "float: right" %><br/>
    </div>
<% end %>

 

6.  app/views/comments  폴더에  _comment_ajax.html.erb  및  _reply_ajax.html.erb  파일을 새로 생성해주세요.

 

7. 방금 과정에서 생성한 파일에 다음과 같이 입력해주세요.

1) app/views/comments/_comment_ajax.html.erb

<div class="comments-show" id="comment<%= comment.id %>">
    <div class="comment">
        <%= comment.user != nil ? comment.user.email : comment.email %> / <%= time_ago_in_words(comment.created_at) %>전 [ <%= comment.created_at.strftime('%Y-%m-%d %H:%M') %> ] /
        <%= link_to "지우기", comment_path(comment), method: :delete, remote: true, data: { confirm: "정말로 지우시겠습니까?" }, style: "color: red; font-weight: bold" %>
        <%= content_tag(:div, comment.body, style: "white-space: pre-wrap;") %>
        
        <div class="comment-nav" align="right">
            <a onclick="showHide('comment_reply_<%= comment.id %>')" onfocus="this.blur()">답댓글</a>
        </div>
        
        <div id="comment_reply_<%= comment.id %>" style="display:none;">
            <div class="reply-form">
                <%= form_for @new_comment, remote: true, method: :post do |f| %>
                    <%= f.hidden_field :commentable_id, value: @new_comment.commentable_id %>
                    <%= f.hidden_field :commentable_type, value: @new_comment.commentable_type %>
                    <%= f.hidden_field :comment_id, value: comment.id %>
                    <div class="field form-group">
                    <%= f.text_area :body, class: 'form-control' %>
                    </div>
                    <div class="field form-group" style="margin-bottom: 60px">
                        <%= submit_tag "답댓글 작성", style: "float: right;" %>
                    </div>
                <% end %>
            </div>
        </div>
    </div>
    <div style="margin-left: 100px;" id="reply-ajax<%= comment.id %>">
    </div>
    <hr/>
</div>

 

2) app/views/comments/_reply_ajax.html.erb

<div class="comments-show" id="comment<%= comment.id %>">
    <div class="comment">
        <%= comment.user != nil ? comment.user.email : comment.email %> / <%= time_ago_in_words(comment.created_at) %>전 [ <%= comment.created_at.strftime('%Y-%m-%d %H:%M') %> ] /
        <%= link_to "지우기", comment_path(comment), method: :delete, remote: true, data: { confirm: "정말로 지우시겠습니까?" }, style: "color: red; font-weight: bold" %>
        <%= content_tag(:div, comment.body, style: "white-space: pre-wrap;") %>
    </div>
</div>

 

8. 기존의 파일에서 일부 코드를 수정해주세요.

1) app/views/comments/_reply.html.erb

<% comments.each do |comment| %>  
    <%= render 'comments/comment_ajax', comment: comment %>
<% end %>

 

2) app/views/comments/_reply_end.html.erb

<% comments.each do |comment| %>  
    <%= render 'comments/reply_ajax', comment: comment %>
<% end %>

 

9. 이제 비동기 상태로 댓글이 실시간으로 올라가는것처럼 보여지는게 확인이 됩니다.

(하지만 실시간적으로 댓글이 달리는건 아니니 참고 바랍니다!!)

 

10.  읽을거리  Ajax 비동기 개념 이해 [클릭]

 

 

  • 참고 자료

1. 이전페이지 이동 [클릭]

2. Gem 사용설명 참고 [클릭]

3. Gem 사용설명 참고(한국어, 간략설명) [클릭]

4.  white-space: pre-wrap;  속성 [클릭]

댓글
댓글쓰기 폼
공지사항
Total
37,626
Today
18
Yesterday
231
링크
«   2020/08   »
            1
2 3 4 5 6 7 8
9 10 11 12 13 14 15
16 17 18 19 20 21 22
23 24 25 26 27 28 29
30 31          
글 보관함